program picking;
{$L build/cone.bin.o}
{$L build/cylinder.bin.o}
{$L build/sphere.bin.o}

{$mode objfpc}

uses
  ctypes, nds9;
  
{$include inc/cone.bin.inc}
{$include inc/cylinder.bin.inc}
{$include inc/sphere.bin.inc}

type
  TClickable = ( clNothing, clCone, clCylinder, clSphere);

var
  clicked: TClickable; // what is being clicked
  closeW: integer; // closest distace to camera
  polyCount: integer; // keeps track of the number of polygons drawn

// run before starting to draw an object while picking
procedure startCheck();
begin
	while PosTestBusy() do; // wait for the position test to finish
	while GFX_BUSY do; // wait for all the polygons from the last object to be drawn
	PosTest_Asynch(0,0,0); // start a position test at the current translated position
	polyCount := GFX_POLYGON_RAM_USAGE^; // save the polygon count
end;

// run afer drawing an object while picking
procedure endCheck(obj: TClickable);
begin
	while GFX_BUSY do; // wait for all the polygons to get drawn
	while PosTestBusy() do; // wait for the position test to finish
	if (GFX_POLYGON_RAM_USAGE^ > polyCount) then // if a polygon was drawn
	begin
		if PosTestWresult() <= closeW then
		begin
			// this is currently the closest object under the cursor!
			closeW := PosTestWresult();
			clicked := obj;
		end;
	end;
end;


var
	rotateX: cuint32 = 0;
	rotateY: cuint32 = 0;
  touchXY: touchPosition;
  viewport: array [0..3] of cint32 = (0,0,255,191); // used later for gluPickMatrix()
	keys: u16;

begin
	// initialize gl
	glInit();

	//set mode 0, enable BG0 and set it to 3D
	videoSetMode(MODE_0_3D);
	
	lcdMainOnBottom(); // we are going to be touching the 3D display
	
	// enable edge outlining, this will be used to show which object is selected
	glEnable(GL_OUTLINE);
	
	//set the first outline color to white
	glSetOutlineColor(0,RGB15(31,31,31));

	// setup the rear plane
	glClearColor(0,0,0,0); // set BG to black and clear
	glClearPolyID(0); // the BG and polygons will have the same ID unless a polygon is highlighted
	glClearDepth($7FFF);
	
	// setup the camera
	gluLookAt( 0.0, 0.0, 1.0,		//camera possition
				     0.0, 0.0, 0.0,		//look at
				     0.0, 1.0, 0.0);	//up
	
	glLight(0, RGB15(31,31,31) , 0, floattov10(-1.0), 0); // setup the light
	
	while true do
	begin
		// handle key input
		scanKeys();
		keys := keysHeld();
		if ((keys and KEY_UP)) = 0 then rotateX := rotateX +3;
		if((keys and KEY_DOWN)) = 0 then rotateX := rotateX -3;
		if((keys and KEY_LEFT)) = 0 then rotateY := rotateY +3;
		if((keys and KEY_RIGHT)) = 0 then rotateY := rotateY -3;

		// get touchscreen position
		touchXY := touchReadXY();

		glViewPort(0,0,255,191); // set the viewport to fullscreen

		// setup the projection matrix for regular drawing
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(60, 256.0 / 192.0, 0.1, 20); 
		
		glMatrixMode(GL_MODELVIEW); // use the modelview matrix while drawing
		
		glPushMatrix(); // save the state of the current matrix(the modelview matrix)
			glTranslate3f32(0,0,floattof32(-6));
			glRotateXi(rotateX); // add X rotation to the modelview matrix
			glRotateYi(rotateY); // add Y rotation to the modelview matrix
			
			glPushMatrix(); // save the state of the modelview matrix while making the first pass
				// draw the scene for displaying
				
				glTranslatef32(floattof32(2.9),floattof32(0),floattof32(0)); // translate the modelview matrix to the drawing location
				if (clicked = clCone) then
					glPolyFmt(POLY_ALPHA(31) or POLY_CULL_BACK or POLY_FORMAT_LIGHT0 or POLY_ID(1)) // set a poly ID for outlining
				else
					glPolyFmt(POLY_ALPHA(31) or POLY_CULL_BACK or POLY_FORMAT_LIGHT0 or POLY_ID(0)); // set a poly ID for no outlining (same as BG)

				glCallList((@cone_bin)); // draw a green cone from a predefined packed command list

				
				glTranslatef32(floattof32(-3),floattof32(1.8),floattof32(2)); // translate the modelview matrix to the drawing location
				if (clicked = clCylinder) then
					glPolyFmt(POLY_ALPHA(31) or POLY_CULL_BACK or POLY_FORMAT_LIGHT0 or POLY_ID(1)) // set a poly ID for outlining
				else
					glPolyFmt(POLY_ALPHA(31) or POLY_CULL_BACK or POLY_FORMAT_LIGHT0 or POLY_ID(0)); // set a poly ID for no outlining (same as BG)

        glCallList((@cylinder_bin)); // draw a blue cylinder from a predefined packed command list


				glTranslatef32(floattof32(0.5),floattof32(-2.6),floattof32(-4)); // translate the modelview matrix to the drawing location
				if(clicked = clSphere) then
					glPolyFmt(POLY_ALPHA(31) or POLY_CULL_BACK or POLY_FORMAT_LIGHT0 or POLY_ID(1)) // set a poly ID for outlining
				else
					glPolyFmt(POLY_ALPHA(31) or POLY_CULL_BACK or POLY_FORMAT_LIGHT0 or POLY_ID(0)); // set a poly ID for no outlining (same as BG)

				glCallList((@sphere_bin)); // draw a red sphere from a predefined packed command list

			glPopMatrix(1); // restores the modelview matrix to where it was just rotated
			
			// draw the scene again for picking
				clicked := clNothing; //reset what was clicked on
				closeW := $7FFFFFFF; //reset the distance

				//set the viewport to just off-screen, this hides all rendering that will be done during picking
				glViewport(0,192,0,192);
				
				// setup the projection matrix for picking
				glMatrixMode(GL_PROJECTION);
				glLoadIdentity();
				gluPickMatrix((touchXY.px),(191-touchXY.py),4,4,viewport); // render only what is below the cursor
				gluPerspective(60, 256.0 / 192.0, 0.1, 20); // this must be the same as the original perspective matrix
				
				glMatrixMode(GL_MODELVIEW); // switch back to modifying the modelview matrix for drawing
				
				glTranslatef32(floattof32(2.9),floattof32(0),floattof32(0)); // translate the modelview matrix to the drawing location
				startCheck();
				glCallList((@cone_bin)); // draw a cone from a predefined packed command list
				endCheck(clCone);

				glTranslatef32(floattof32(-3),floattof32(1.8),floattof32(2)); // translate the modelview matrix to the drawing location
				startCheck();
				glCallList((@cylinder_bin)); // draw a cylinder from a predefined packed command list
				endCheck(clCylinder);

				glTranslatef32(floattof32(0.5),floattof32(-2.6),floattof32(-4)); // translate the modelview matrix to the drawing location
				startCheck();
				glCallList((@sphere_bin)); // draw a sphere from a predefined packed command list
				endCheck(clSphere);

		glPopMatrix(1); // restores the modelview matrix to its original state

		glFlush(0); // wait for everything to be drawn before starting on the next frame
	end;

end.
