#!/usr/bin/perl -w

use strict;
use OpenGL qw/ :all /;

use lib 'd:/Programs/Perl/lib';
use Graphics::OpenGL;
use Crystal::OpenGL;

#/* セレクションバッファのサイズ */
my $SELECTIONS = 100;

#/* オブジェクトの数 */
my $NOBJECTS = 5;

#/* ディスプレイリストの識別子 */
my $objects = 0;

#/* 各オブジェクトの色 */
my @color;

#/* 色データ */
my $gray = [ 0.7, 0.7, 0.7, 1.0 ];
my $blue = [ 0.1, 0.1, 0.9, 1.0 ];

my $selection = OpenGL::Array->new($SELECTIONS, GL_INT);
#my @selection;  # /* セレクションバッファ　　　　　 */
my $hits = 0;   # /* セレクトされたオブジェクトの数 */


glutInit();
glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH);
glutCreateWindow("title");
glutDisplayFunc(sub { &display(); } );
glutMouseFunc(\&mouse);
glutReshapeFunc(\&resize);
glutKeyboardFunc(\&keyboard);
&init();
&scene();
glutMainLoop();

exit;

sub mouse {
	my ($button, $state, $x, $y) = @_;

	if($button == GLUT_LEFT_BUTTON) {
		if($state == GLUT_DOWN) {
			my $ptr;
			my @vp;
#      /* セレクションに使うバッファの設定．これはセレクショ
#         ンモード以外の時（glRenderMode(GL_SELECT) より前）
#         に実行する必要がある．セレクションバッファには，入
#         るだけのデータが詰め込まれる */
			glSelectBuffer_c($SELECTIONS, $selection->ptr());

#      /* レンダリングモードをセレクションモードに切替える */
			glRenderMode(GL_SELECT);

#      /* セレクションバッファの初期化，これはセレクションモー
#         ドになってないと無視される */
			glInitNames();

#      /* ネームスタックの先頭に仮の名前を詰めておく．ネーム
#         スタック自体は複数のオブジェクトが選択できるように
#         スタック構造になっているが，今回は１個のオブジェク
#         トしか選択しないので，ネームスタックの先頭の要素だ
#         けを取り替えながら描画すればよい．そこで，あらかじ
#         めネームスタックの先頭に仮の名前 (-1) に詰めておい
#         て，そこを使い回す． */
			glPushName(-1);

#      /* セレクションの処理は視点座標系で行う */
			glMatrixMode(GL_PROJECTION);

#      /* 現在の透視変換マトリクスを保存する */
			glPushMatrix();

#      /* 透視変換マトリクスを初期化する */
			glLoadIdentity();

#      /* 現在のビューポート設定を得る */
			@vp = glGetIntegerv_p(GL_VIEWPORT);

#      /* 表示領域がマウスポインタの周囲だけになるように変換
#         行列を設定する．マウスの座標系は，スクリーンの座標
#         系に対して上下が反転しているので，それを補正する */
			gluPickMatrix_p($x, $vp[3] - $y - 1, 1, 1, @vp);

#      /* 通常の描画と同じ透視変換マトリクスを設定する．ウィ
#         ンドウ全体をビューポートにしているので，アスペクト
#         比は vp[2] / vp[3] で得られる．*/
			gluPerspective(30.0, $vp[2] / $vp[3], 1.0, 100.0);

#      /* モデルビューマトリクスに切替える */
			glMatrixMode(GL_MODELVIEW);

#      /* ここで一旦モデルビューマトリクスに切替えて，ビュー
#         イング変換やモデリング変換の設定をするのだが，直前
#         に描画された図形に対してセレクションを行なうなら，
#         その時に使ったモデルビューマトリクスがそのまま使え
#         る（はず）．だから今は以下の処理 (#if 0 〜 #endif) 
#         を省略しても大丈夫（だと思う）*/

if(0) {
#      /* モデルビュー変換行列の初期化 */
	glLoadIdentity();

#      /* 視点の設定 */
	gluLookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}

#      /* もう一度シーンを描画する */
			for (my $i = 0 ; $i < $NOBJECTS ; $i++) {
#	/* ネームスタックの先頭にこれから描くオブジェクトの
#           番号を設定する */
				glLoadName($i);
#	/* オブジェクトを描画する（画面には表示されない）*/
				glCallList($objects + $i);
			}

#      /* 再び透視変換マトリクスに切替える */
			glMatrixMode(GL_PROJECTION);

#      /* 透視変換マトリクスを元に戻す */
			glPopMatrix();

#      /* モデルビューマトリクスに戻す */
			glMatrixMode(GL_MODELVIEW);

#      /* レンダリングモードを元に戻す */
			$hits = glRenderMode(GL_RENDER);
print "hits = $hits\n";
#print "sel=$selection\n";
			my $len = $selection->elements();
#print "len=$len\n";
			my @SelArray = $selection->retrieve(0, $len-1);
#print "a=", join(',', @SelArray), "\n";

#      /* セレクションバッファにはいくつのデータが入っている
#         のかわからないので，とりあえず先頭のポインタを取り
#         出しておく */
			$ptr = 0;

#      /* hits にはセレクションバッファに格納されたデータの数
#         が入る．データがセレクションバッファに入り切らなけ
#         れば hits < 0 となる */
			for (my $i = 0; $i < $hits ; $i++) {
#	/* セレクションバッファの先頭の要素は、選択されたオ
#           ブジェクトの数 */
				my $j;
				my $n = $SelArray[0];
print "n[0]=$n\n";

#	/* 続く２つの要素は、選択された位置に置ける奥行き値
#           の最小値と最大値を符号なし整数で表したもの */
				my $near = $SelArray[1] / 0x7fffffff;
				my $far  = $SelArray[2] / 0x7fffffff;
print "  Z=$near, $far\n";

#	/* セレクションバッファの４つ目の要素（添字＝３）か
#           ら選択されたオブジェクトの番号が入っている */
				$ptr += 3;
				for (my $j = 0; $j < $n ; $j++) {
print "iObjblue[$j]=$SelArray[$ptr]\n";
					$color[$SelArray[$ptr]] = $blue; #/* オブジェクトの色を青にする */
					$ptr++;
				}
			}
		}
		else {
			my $len = $selection->elements();
#print "len=$len\n";
			my @SelArray = $selection->retrieve(0, $len-1);
#print "a=", join(',', @SelArray), "\n";
			my $ptr = 0;

#      /* selection[] の内容はマウスの左ボタンをクリックした
#         時にしか変更されないはずだから，まだ残っているはず */
			for(my $i = 0; $i < $hits ; $i++) {
				my $n = $SelArray[0];

				$ptr += 3;
				for(my $j = 0; $j < $n ; $j++) {
print "iObjgray[$j]=$SelArray[$ptr]\n";
					$color[$SelArray[$ptr]] = $gray;
					$ptr++;
				}
			}
		}
#    /* 画面を書き換えてみる */
		glutPostRedisplay();
	}
	elsif($button == GLUT_MIDDLE_BUTTON) {
	}
	elsif($button == GLUT_RIGHT_BUTTON) {
	}
	else {
	}
}

sub keyboard
{
	my ($key, $x, $y) = @_;
#  /* '\033' は ESC の ASCII コード */
	if($key == 'q' or $key == 'Q' or $key == 0x33) {
		exit;
	}
	else {
	}
}

sub resize
{
	my (my $w, my $h) = @_;
#  /* ウィンドウ全体をビューポートにする */
	glViewport(0, 0, $w, $h);

#  /* 透視変換行列を設定する */
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(30.0, $w / $h, 1.0, 100.0);

#  /* モデルビュー変換行列を指定しておく */
	glMatrixMode(GL_MODELVIEW);
}

sub scene
{
	my $width  = 4.0;
	my $center = $width / 2.0;
	my $step   = $width / ($NOBJECTS - 1);

#glColor3d(0.0, 0.0, 0.0);

#  /* セレクションの対象になるオブジェクトを生成して，ディス
#     プレイリストを作っておく*/
	for (my $i = 0; $i < $NOBJECTS; $i++) {
#  /* 図形をディスプレイリストに登録 */
		glNewList($objects + $i, GL_COMPILE);
print "i=$i\n";
#  /* 球を描く */
		glPushMatrix();
		glTranslated($i * $step - $center, (($i & 1) * 2 - 1) * $step, 0.0);
		glutSolidSphere(0.5, 10, 5);
		glPopMatrix();
		glEndList();
	}
}

sub display {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();
	gluLookAt(0.0, 0.0, 10.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
#  /* 一旦シーンを描画する */
	for (my $i = 0; $i < $NOBJECTS ; $i++) {
		my $p = $color[$i];
		glMaterialfv_p(GL_FRONT, GL_DIFFUSE, @$p);
		glCallList($objects + $i);
	}
	glFlush();
}

sub init
{
#glClearColor(1.0, 1.0, 1.0, 0.0);
#glutMainLoop();
#return;

#  /* 初期設定 */
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_CULL_FACE);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHTING);

#  /* 各オブジェクトのディスプレイリストを作る */
	$objects = glGenLists($NOBJECTS);
	if($objects <= 0) {
		printf("Can't create so many objects: %d.\n", $NOBJECTS);
		exit(1);
	}

#  /* 各オブジェクトの色を初期化 */
	for (my $i = 0; $i < $NOBJECTS; $i++) {
		$color[$i] = $gray;
	}
}