#!/usr/bin/perl -w

use strict;
use OpenGL qw/ :all /;

use lib 'd:/Programs/Perl/lib';
use Graphics::OpenGL;

#マウス始点など
my ($cx, $cy, $sx, $sy);
#回転
my ($ax, $ay, $az) = (0, 1, 0);
my $ca = 0.0;
my $angle = 0.0;

#/* セレクションバッファのサイズ */
my $SelectionBufferSize = 100;
#/* オブジェクトの数 */
my $nObjects = 5;

my ($WindowWidth, $WindowHeight) = (300, 300);

#/* 各オブジェクトの色 */
my $gray = [ 0.7, 0.7, 0.7, 1.0 ];
my $blue = [ 0.1, 0.1, 0.9, 1.0 ];
my @material;

my $OGL = new Graphics::OpenGL();
$OGL->CreateSelectionBuffer($SelectionBufferSize);

$OGL->Initialize(GLUT_RGBA | GLUT_DEPTH, 0, 0, $WindowWidth, $WindowHeight, 'Test');
$OGL->SetFunctions(
		-Reshape    => sub { &resize($OGL, @_);  },
		-Display    => sub { &display($OGL, @_); },
		-Mouse      => sub { &mouse($OGL, @_);   },
		-Motion     => sub { &motion($OGL, @_);  },
		-Keyboard   => \&keyboard,
		);
&InitializeOpenGL($OGL);
&scene($OGL);
$OGL->Run();

exit;

sub keyboard
{
	my ($key, $x, $y) = @_;
#  /* '\033' は ESC の ASCII コード */
	if($key == 'q' or $key == 'Q' or $key == 0x33) {
		exit;
	}
	else {
	}
}

sub motion {
	my ($OGL, $x, $y) = @_;
#print "motion: ($x, $y)\n";

	my $SCALE = 360.0;

#マウスポインタの位置のドラッグ開始位置からの変位
	my $dx = ($x - $cx) * $sx;
	my $dy = ($y - $cy) * $sy;

#マウスポインタの位置のドラッグ開始位置からの距離
	my $a = sqrt($dx * $dx + $dy * $dy);

	if($a != 0.0) {
#距離を角度に換算してドラッグ開始時の回転角に加算
		$angle = $ca + $SCALE * $a; #fmod($ca + $SCALE * $a, 360.0);
#マウスポインタの変位から回転軸ベクトルを求める
		$ax = $dy / $a;
		$ay = $dx / $a;
		$az = 0.0;
print "rotation: $angle ($ax, $ay, $az)\n";
		$OGL->Redraw();
	}
}

sub mouse {
	my ($OGL, $button, $state, $x, $y) = @_;

	if($button == GLUT_LEFT_BUTTON) {
		if($state == GLUT_DOWN) {
#print "mouse: ($x, $y)\n";
			$cx = $x;
			$cy = $y;
			$ca = $angle;
		}
	}

	if($button == GLUT_LEFT_BUTTON) {
		if($state == GLUT_DOWN) {
			$OGL->EnterSelectionMode($x, $y, 1, 1);

#もう一度シーンを描画する
			$OGL->PushMatrix();
			$OGL->Rotated($angle, $ax, $ay, $az);
			for (my $i = 0 ; $i < $nObjects ; $i++) {
				$OGL->DrawListObject($i);
			}
			$OGL->PopMatrix();

			my $hits = $OGL->ExitSelectionMode();
print "hits = $hits\n";

			my @SelectedObjects = $OGL->GetSelectedObjects();
			for (my $i = 0; $i < @SelectedObjects ; $i++) {
				my $pObj  = $SelectedObjects[$i];
				my $near  = $pObj->{Zmin};
				my $far   = $pObj->{Zmax};
				my ($ox, $oy, $oz) = $OGL->Get3DPosition($x, $y);
				my $piObj = $pObj->{piObjects};
				my $nObj  = @$piObj;
print "  n[$i]=$nObj: Z=$near, $far\n";
print "      ($ox, $oy, $oz)\n";
				for (my $j = 0; $j < $nObj ; $j++) {
					my $iObj = $piObj->[$j];
print "      iObj(blue)[$j]=$iObj\n";
					$material[$iObj]->{-Diffuse} = $blue;
				}
			}
		}
		else {
#SelectionBufferの内容はマウスの左ボタンをクリックした時にしか変更されないので，まだ残っている
			my @SelectedObjects = $OGL->GetSelectedObjects();
			for(my $i = 0; $i < @SelectedObjects ; $i++) {
				my $pObj = $SelectedObjects[$i];
				my $piObj = $pObj->{piObjects};
				my $nObj = @$piObj;
				for (my $j = 0; $j < $nObj ; $j++) {
					my $iObj = $piObj->[$j];
print "      iObj(gray)[$j]=$iObj\n";
					$material[$iObj]->{-Diffuse} = $gray;
				}
			}
		}
		$OGL->Redraw();
	}
	elsif($button == GLUT_MIDDLE_BUTTON) {
	}
	elsif($button == GLUT_RIGHT_BUTTON) {
	}
	else {
	}
}

sub resize
{
	my ($OGL, $w, $h) = @_;

	$sx = 1.0 / $w;
	$sy = 1.0 / $h;

#  /* ウィンドウ全体をビューポートにする */
	$OGL->Viewport(0, 0, $w, $h);

#  /* 透視変換行列を設定する */
	$OGL->MatrixMode(GL_PROJECTION);
	$OGL->InitializeMatrix();
	$OGL->Perspective(30.0, $w / $h, 1.0, 100.0);

#  /* モデルビュー変換行列を指定しておく */
	$OGL->MatrixMode(GL_MODELVIEW);
}

sub scene
{
	my ($OGL) = @_;

	my $width  = 4.0;
	my $center = $width / 2.0;
	my $step   = $width / ($nObjects - 1);

#セレクションの対象になるオブジェクトを生成して，ディスプレイリストを作っておく
	for (my $i = 0; $i < $nObjects ; $i++) {
#図形をディスプレイリストに登録
		$OGL->NewList($i, GL_COMPILE);
		$OGL->DrawSphere($i * $step - $center, (($i & 1) * 2 - 1) * $step, 0.0, 0.5, undef, 10);
		$OGL->EndList();
	}
}

sub display {
	my ($OGL) = @_;

	$OGL->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	$OGL->InitializeMatrix();
	$OGL->LookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

	$OGL->PushMatrix();
	$OGL->Rotated($angle, $ax, $ay, $az);

#一旦シーンを描画する
	for (my $i = 0; $i < $nObjects ; $i++) {
		$OGL->SetMaterial($material[$i], GL_FRONT);
#		$OGL->CallList($i);
		$OGL->DrawListObject($i);
	}

	$OGL->PopMatrix();

	$OGL->Flush();
}

sub InitializeOpenGL
{
	my ($OGL) = @_;

#  /* 初期設定 */
	$OGL->ClearColor(1.0, 1.0, 1.0, 0.0);
	$OGL->Enable(GL_DEPTH_TEST, GL_CULL_FACE, GL_LIGHT0, GL_LIGHTING);

#オブジェクトのディスプレイリストを作る
	if($OGL->GenLists($nObjects) <= 0) {
		printf("Can't create so many objects: %d.\n", $nObjects);
		exit(1);
	}

#各オブジェクトの色を初期化
	for (my $i = 0; $i < $nObjects ; $i++) {
		$material[$i] = new Graphics::OpenGLMaterial(
				-Diffuse   => $gray,
#				-Ambient   => [0.5, 0.7, 0.5, 0.5],
#				-Specular  => [1.0, 1.0, 1.0, 1.0],
#				-Emission  => [0.0, 0.0, 1.0, 1.0],
#				-Shininess => [50.0],
			);
	}
}