All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ode4j.drawstuff.internal.DrawStuffGL Maven / Gradle / Ivy

/*************************************************************************
 *                                                                       *
 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.       *
 * All rights reserved.  Email: [email protected]   Web: www.q12.org          *
 * Open Dynamics Engine 4J, Copyright (C) 2009-2014 Tilmann Zaeschke     *
 * All rights reserved.  Email: [email protected]   Web: www.ode4j.org        *
 *                                                                       *
 * This library is free software; you can redistribute it and/or         *
 * modify it under the terms of EITHER:                                  *
 *   (1) The GNU Lesser General Public License as published by the Free  *
 *       Software Foundation; either version 2.1 of the License, or (at  *
 *       your option) any later version. The text of the GNU Lesser      *
 *       General Public License is included with this library in the     *
 *       file LICENSE.TXT.                                               *
 *   (2) The BSD-style license that is included with this library in     *
 *       the file ODE-LICENSE-BSD.TXT and ODE4J-LICENSE-BSD.TXT.         *
 *                                                                       *
 * This library is distributed in the hope that it will be useful,       *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files    *
 * LICENSE.TXT, ODE-LICENSE-BSD.TXT and ODE4J-LICENSE-BSD.TXT for more   *
 * details.                                                              *
 *                                                                       *
 *************************************************************************/
package org.ode4j.drawstuff.internal;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.lang.Math;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL30;
import org.ode4j.drawstuff.DrawStuff.dsFunctions;
import org.ode4j.math.DMatrix3;
import org.ode4j.math.DMatrix3C;
import org.ode4j.math.DVector3;
import org.ode4j.math.DVector3C;

import static org.ode4j.drawstuff.DrawStuff.*;

/**
 *
 * simple graphics.
 * 
 * the following command line flags can be used (typically under unix)
 * 	-notex		Do not use any textures
 * 	-noshadow[s]	Do not draw any shadows
 * 	-pause		Start the simulation paused
 *  -texturepath [path] Inform an alternative textures path
 *
 * TODO
 * ----
 * 
 * manage openGL state changes better
 * 
 */
//public class DrawStuff extends Swing implements All {
public class DrawStuffGL extends LwJGL implements DrawStuffApi {

	
	// ***************************************************************************
	// misc

	private static final String DEFAULT_PATH_TO_TEXTURES;
	static {
		String s = System.getProperty("file.separator");
		DEFAULT_PATH_TO_TEXTURES = ".." + s + "textures" + s;
	}

	private static final double M_PI = Math.PI;

	// constants to convert degrees to radians and the reverse
	//private static final double RAD_TO_DEG = 180.0/Math.PI;
	private static final double DEG_TO_RAD = Math.PI/180.0; 
	//#define RAD_TO_DEG (180.0/M_PI)
	//#define DEG_TO_RAD (M_PI/180.0)

	// light vector. LIGHTZ is implicitly 1
	private static final float LIGHTX = 1.0f;
	private static final float LIGHTY = 0.4f;
	//#define LIGHTX (1.0f)
	//#define LIGHTY (0.4f)

	// ground and sky
	private static final float SHADOW_INTENSITY = 0.65f;
	// ground color for when there's no texture
	private static final float GROUND_R = 0.5f;
	private static final float GROUND_G = 0.5f;
	private static final float GROUND_B = 0.3f;
	//#define SHADOW_INTENSITY (0.65f)
	//#define GROUND_R (0.5f) 	// ground color for when there's no texture
	//#define GROUND_G (0.5f)
	//#define GROUND_B (0.3f)

	private static final float ground_scale = 1.0f/1.0f;	// ground texture scale (1/size)
	private static final float ground_ofsx = 0.5f;		// offset of ground texture
	private static final float ground_ofsy = 0.5f;
	private static final float sky_scale = 1.0f/4.0f;	// sky texture scale (1/size)
	private static final float sky_height = 1.0f;		// sky height above viewpoint

	// ***************************************************************************
	// misc mathematics stuff


	private void normalizeVector3 (float[] v)//[3])
	{
		float len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
		if (len <= 0.0f) {
			v[0] = 1;
			v[1] = 0;
			v[2] = 0;
		}
		else {
			len = 1.0f / (float)Math.sqrt(len);
			v[0] *= len;
			v[1] *= len;
			v[2] *= len;
		}
	}
	
	private static void crossProduct3(float[] res, float[] a, float[] b)
	{
	  float res_0 = a[1]*b[2] - a[2]*b[1];
	  float res_1 = a[2]*b[0] - a[0]*b[2];
	  float res_2 = a[0]*b[1] - a[1]*b[0];
	  // Only assign after all the calculations are over to avoid incurring memory aliasing
	  res[0] = res_0;
	  res[1] = res_1;
	  res[2] = res_2;
	}

	//***************************************************************************
	// PPM image object

	private interface Image {

		int height();

		int width();

		ByteBuffer data();
		
	}
	
	private static class ImageIO implements Image {
		private int image_width, image_height;
		private ByteBuffer image_data;
		//public:
		//Image (String filename);
		// load from PPM file
		//  ~Image();
		@Override
		public int width() { return image_width; }
		@Override
		public int height() { return image_height; }
		@Override
		public ByteBuffer data() { return image_data; }



		// skip over whitespace and comments in a stream.

		private void skipWhiteSpace (String filename, PushbackInputStream f) throws IOException
		{
			int c,d;
			while(true) {//for(;;) {
				c = f.read();//fgetc(f);
				if (c==-1) dsError ("unexpected end of file in \"%s\"",filename);

				// skip comments
				if (c == '#') {
					do {
						d = f.read();//fgetc(f);
						if (d==-1) dsError ("unexpected end of file in \"%s\"",filename);
					} while (d != '\n');
					continue;
				}

				if (c > ' ') {
					f.unread(c);//ungetc (c,f);
					return;
				}
			}
		}


		// read a number from a stream, this return 0 if there is none (that's okay
		// because 0 is a bad value for all PPM numbers anyway).

		private int readNumber (String filename, PushbackInputStream f) throws IOException
		{
			int c,n=0;
			for(;;) {
				c = f.read();//fgetc(f);
				if (c==-1) dsError ("unexpected end of file in \"%s\"",filename);
				if (c >= '0' && c <= '9') n = n*10 + (c - '0');
				else {
					f.unread(c);//ungetc (c,f);
					return n;
				}
			}
		}


		ImageIO (String filename)
		{
			PushbackInputStream f = null;
			try {
				InputStream is = getClass().getResourceAsStream(filename); 
				if (is == null) throw new IllegalArgumentException("File not found: " + filename);
				f = new PushbackInputStream( new BufferedInputStream( is )); 
				//if (f == null) dsError ("Can't open image file `%s'",filename);

				// read in header
				//if (fgetcC(f) != 'P' || fgetcC(f) != '6')
				if (f.read() != 'P' || f.read() != '6')
					dsError ("image file \"%s\" is not a binary PPM (no P6 header)",filename);
				skipWhiteSpace (filename,f);

				// read in image parameters
				image_width = readNumber (filename,f);
				skipWhiteSpace (filename,f);
				image_height = readNumber (filename,f);
				skipWhiteSpace (filename,f);
				int max_value = readNumber (filename,f);

				// check values
				if (image_width < 1 || image_height < 1)
					dsError ("bad image file \"%s\"",filename);
				if (max_value != 255)
					dsError ("image file \"%s\" must have color range of 255",filename);

				// read either nothing, LF (10), or CR,LF (13,10)
				int c = f.read();//fgetc(f);
				if (c == 10) {
					// LF
				}
				else if (c == 13) {
					// CR
					c = f.read();//fgetc(f);
					if (c != 10) f.unread(c);//ungetc (c,f);
				}
				else f.unread(c);//ungetc (c,f);

				// read in rest of data
				//image_data = new byte [image_width*image_height*3];
				image_data = BufferUtils.createByteBuffer(image_width*image_height*3);
				//TODO TZ read directly into direct buffer!
				byte[] buf = new byte [image_width*image_height*3];
				if (f.read (buf, 0, image_width*image_height*3 * 1) 
						!= image_width*image_height*3)
					dsError ("Can not read data from image file `%s'",filename);
				image_data.put(buf);
				image_data.flip();
			} catch (IOException e) {
				System.err.println("Error reading file: \"" + filename + "\"");
				e.printStackTrace();
				if (f != null) {
					try {
						f.close ();
					} catch (IOException e2) {
						e2.printStackTrace();
					}
				}
				throw new RuntimeException(e);
			}
		}
	}
	
	//***************************************************************************
	// Texture object.

	private static class Texture {
		private Image image;
		private int name; // GLuint TZ
		//public:
		//Texture (String filename);
		//  ~Texture();
		//void bind (int modulate);


		public Texture (String filename)
		{
			image = new ImageIO (filename);
//			GL11.glGenTextures (1,name);
			IntBuffer ib = BufferUtils.createIntBuffer(1);
			ib.put(name).flip();  //Necessary ? TZ
			GL11.glGenTextures (ib);
			name = ib.get(0);
			GL11.glBindTexture (GL11.GL_TEXTURE_2D,name);

			// set pixel unpacking mode
			GL11.glPixelStorei (GL11.GL_UNPACK_SWAP_BYTES, 0);
			GL11.glPixelStorei (GL11.GL_UNPACK_ROW_LENGTH, 0);
			GL11.glPixelStorei (GL11.GL_UNPACK_ALIGNMENT, 1);
			GL11.glPixelStorei (GL11.GL_UNPACK_SKIP_ROWS, 0);
			GL11.glPixelStorei (GL11.GL_UNPACK_SKIP_PIXELS, 0);

			//GL11.glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height);
			//GL11.glTexSubImage2D(GL11.GL_TEXTURE_2D, 0​, 0, 0, image.width(), image.height(), GL11.GL_BGRA, GL11.GL_UNSIGNED_BYTE, image.data());

//			glTexImage2D (GL11.GL_TEXTURE_2D, 0, 3, image->width(), image->height(), 0,
//					   GL_RGB, GL_UNSIGNED_BYTE, image->data());
			// TODO CHECK-TZ
			// GL11.GL_RGBA8
			GL11.glTexImage2D (GL11.GL_TEXTURE_2D, 0, 3, image.width(), image.height(), 0,
					GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, image.data());//ByteBuffer.wrap(image.data().array()));
			//TZ this is the original:
//			GLX.gluBuild2DMipmaps (GL11.GL_TEXTURE_2D, 3, image.width(), image.height(),
//			GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, image.data());
//			GLU.gluBuild2DMipmaps (GL11.GL_TEXTURE_2D, 3, image.width(), image.height(),
//			GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, image.data());
			// TZ Please comment this out if it doesn´t work for you.
			//  The demos will still work, albeit without textures.
			GL30.glGenerateMipmap(GL30.GL_TEXTURE_2D);
			
			// set texture parameters - will these also be bound to the texture???
			GL11.glTexParameterf (GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
			GL11.glTexParameterf (GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);

			GL11.glTexParameterf (GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
			GL11.glTexParameterf (GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER,
					GL11.GL_LINEAR_MIPMAP_LINEAR);

			GL11.glTexEnvf (GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL);
		}


		//Texture::~Texture()
		//{
		//  delete image;
		//  glDeleteTextures (1,&name);
		//}
		//		@Override
		//		protected void finalize() throws Throwable {
		//			image = null;
		////			GL11.glDeleteTextures (1, name));
		//			GL11.glDeleteTextures (IntBuffer.wrap(new int[]{name}));
		//			super.finalize();
		//		}

		public void bind (boolean modulate)
		{
			GL11.glBindTexture (GL11.GL_TEXTURE_2D,name);
			GL11.glTexEnvi (GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE,
					modulate ? GL11.GL_MODULATE : GL11.GL_DECAL);
		}
	}


	//***************************************************************************
	// the current drawing state (for when the user's step function is drawing)

	private static float[] color = {0,0,0,0};	// current r,g,b,alpha color
	private static DS_TEXTURE_NUMBER tnum = DS_TEXTURE_NUMBER.DS_NONE;			// current texture number

	//***************************************************************************
	// OpenGL utility stuff

	private void setCamera (float x, float y, float z, float h, float p, float r)
	{
		GL11.glMatrixMode (GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		GL11.glRotatef (90, 0,0,1);
		GL11.glRotatef (90, 0,1,0);
		GL11.glRotatef (r, 1,0,0);
		GL11.glRotatef (p, 0,1,0);
		GL11.glRotatef (-h, 0,0,1);
		GL11.glTranslatef (-x,-y,-z);
	}


	// sets the material color, not the light color

	private FloatBuffer light_ambient2 = BufferUtils.createFloatBuffer(4);
	private FloatBuffer light_diffuse2 = BufferUtils.createFloatBuffer(4);
	private FloatBuffer light_specular2 = BufferUtils.createFloatBuffer(4);
	private void setColor (float r, float g, float b, float alpha)
	{
		//GLfloat light_ambient[4],light_diffuse[4],light_specular[4];
		light_ambient2.put( new float[]{ r*0.3f, g*0.3f, b*0.3f, alpha }).flip();
		light_diffuse2.put( new float[]{ r*0.7f, g*0.7f, b*0.7f, alpha }).flip();
		light_specular2.put( new float[]{ r*0.2f, g*0.2f, b*0.2f, alpha }).flip();
		GL11.glMaterialfv (GL11.GL_FRONT_AND_BACK, GL11.GL_AMBIENT, light_ambient2);
		GL11.glMaterialfv (GL11.GL_FRONT_AND_BACK, GL11.GL_DIFFUSE, light_diffuse2);
		GL11.glMaterialfv (GL11.GL_FRONT_AND_BACK, GL11.GL_SPECULAR, light_specular2);
		GL11.glMaterialf (GL11.GL_FRONT_AND_BACK, GL11.GL_SHININESS, 5.0f);
	}


	private FloatBuffer matrixF = BufferUtils.createFloatBuffer(16);
	
//	static void setTransform (final float pos[3], final float R[12])
	private void setTransform (final float[] pos, final float[] R)
	{
		//GLfloat
		float[] matrix=new float[16];
		matrix[0]=R[0];
		matrix[1]=R[4];
		matrix[2]=R[8];
		matrix[3]=0;
		matrix[4]=R[1];
		matrix[5]=R[5];
		matrix[6]=R[9];
		matrix[7]=0;
		matrix[8]=R[2];
		matrix[9]=R[6];
		matrix[10]=R[10];
		matrix[11]=0;
		matrix[12]=pos[0];
		matrix[13]=pos[1];
		matrix[14]=pos[2];
		matrix[15]=1;
		matrixF.put(matrix);
		matrixF.flip();
		GL11.glPushMatrix();
		GL11.glMultMatrixf (matrixF);
	}
	
	private DoubleBuffer matrixD = BufferUtils.createDoubleBuffer(16);
	
//	static void setTransformD (final double pos[3], final double R[12])
	private void setTransform (final DVector3C pos, final DMatrix3C R)
	{
		//GLdouble
		double[] matrix=new double[16];
		matrix[0]=R.get00();
		matrix[1]=R.get10();
		matrix[2]=R.get20();
		matrix[3]=0;
		matrix[4]=R.get01();
		matrix[5]=R.get11();
		matrix[6]=R.get21();
		matrix[7]=0;
		matrix[8]=R.get02();
		matrix[9]=R.get12();
		matrix[10]=R.get22();
		matrix[11]=0;
		matrix[12]=pos.get0();
		matrix[13]=pos.get1();
		matrix[14]=pos.get2();
		matrix[15]=1;
		matrixD.put(matrix);
		matrixD.flip();
		GL11.glPushMatrix();
		GL11.glMultMatrixd (matrixD);
	}


	// set shadow projection transform

	private FloatBuffer matrixSST = BufferUtils.createFloatBuffer(16);
	private void setShadowTransform()
	{
		//GLfloat
		float[] matrix=new float[16];
		for (int i=0; i<16; i++) matrix[i] = 0;
		matrix[0]=1;
		matrix[5]=1;
		matrix[8]=-LIGHTX;
		matrix[9]=-LIGHTY;
		matrix[15]=1;
		matrixSST.put( matrix );
//		for (int i=0; i < 16; i++) matrixSST.put(i, 0);
//		matrixSST.put(0, 1);
//		matrixSST.put(5, 1);
//		matrixSST.put(8, -LIGHTX);
//		matrixSST.put(9, -LIGHTY);
//		matrixSST.put(15, 1);
		matrixSST.flip();
		GL11.glPushMatrix();
		GL11.glMultMatrixf (matrixSST);
	}

//	static void drawConvex (float *_planes,unsigned int _planecount,
//			float *_points,
//			unsigned int _pointcount,
//			unsigned int *_polygons)
	private void drawConvex (float[] _planes, int _planecount,
			float[] _points,
			int _pointcount,
			int[] _polygons)
	{
		//unsigned 
		int polyindex=0;
		for(int i=0;i<_planecount;++i)
		{
			//unsigned 
			int pointcount=_polygons[polyindex];
			polyindex++;
			GL11.glBegin (GL11.GL_POLYGON);      
			GL11.glNormal3f(_planes[(i*4)+0],
					_planes[(i*4)+1],
					_planes[(i*4)+2]);
			for(int j=0;j 0) {
			float[] q1=new float[3],q2=new float[3],q3=new float[3];		 // sub-vertices
			for (i=0; i<3; i++) {
				q1[i] = 0.5f*(p1[i]+p2[i]);
				q2[i] = 0.5f*(p2[i]+p3[i]);
				q3[i] = 0.5f*(p3[i]+p1[i]);
			}
			float length1 = (float)(1.0/Math.sqrt(q1[0]*q1[0]+q1[1]*q1[1]+q1[2]*q1[2]));
			float length2 = (float)(1.0/Math.sqrt(q2[0]*q2[0]+q2[1]*q2[1]+q2[2]*q2[2]));
			float length3 = (float)(1.0/Math.sqrt(q3[0]*q3[0]+q3[1]*q3[1]+q3[2]*q3[2]));
			for (i=0; i<3; i++) {
				q1[i] *= length1;
				q2[i] *= length2;
				q3[i] *= length3;
			}
			drawPatch (p1,q1,q3,level-1);
			drawPatch (q1,p2,q2,level-1);
			drawPatch (q1,q2,q3,level-1);
			drawPatch (q3,q2,p3,level-1);
		}
		else {
			GL11.glNormal3f (p1[0],p1[1],p1[2]);
			GL11.glVertex3f (p1[0],p1[1],p1[2]);
			GL11.glNormal3f (p2[0],p2[1],p2[2]);
			GL11.glVertex3f (p2[0],p2[1],p2[2]);
			GL11.glNormal3f (p3[0],p3[1],p3[2]);
			GL11.glVertex3f (p3[0],p3[1],p3[2]);
		}
	}


	// draw a sphere of radius 1

	private int sphere_quality = 1;

	private static final float ICX = 0.525731112119133606f;
	private static final float ICZ = 0.850650808352039932f;
	private static final float[][] idata = { //GLfloat [12][3] = {
		{-ICX, 0, ICZ},
		{ICX, 0, ICZ},
		{-ICX, 0, -ICZ},
		{ICX, 0, -ICZ},
		{0, ICZ, ICX},
		{0, ICZ, -ICX},
		{0, -ICZ, ICX},
		{0, -ICZ, -ICX},
		{ICZ, ICX, 0},
		{-ICZ, ICX, 0},
		{ICZ, -ICX, 0},
		{-ICZ, -ICX, 0}
	};
	private static final int[][] index = {//[20][3] = {
		{0, 4, 1},	  {0, 9, 4},
		{9, 5, 4},	  {4, 5, 8},
		{4, 8, 1},	  {8, 10, 1},
		{8, 3, 10},   {5, 3, 8},
		{5, 2, 3},	  {2, 7, 3},
		{7, 10, 3},   {7, 6, 10},
		{7, 11, 6},   {11, 0, 6},
		{0, 1, 6},	  {6, 1, 10},
		{9, 0, 11},   {9, 11, 2},
		{9, 2, 5},	  {7, 2, 11},
	};
	private static int listnum = 0; //GLunint TZ
	private void drawSphere()
	{
		// icosahedron data for an icosahedron of radius 1.0
//		# define ICX 0.525731112119133606f
//		# define ICZ 0.850650808352039932f
		if (listnum==0) {
			listnum = GL11.glGenLists (1);
			GL11.glNewList (listnum,GL11.GL_COMPILE);
			GL11.glBegin (GL11.GL_TRIANGLES);
			for (int i=0; i<20; i++) {
//				drawPatch (&idata[index[i][2]][0],&idata[index[i][1]][0],
//						&idata[index[i][0]][0],sphere_quality);
				drawPatch (idata[index[i][2]],idata[index[i][1]],
						idata[index[i][0]],sphere_quality);
			}
			GL11.glEnd();
			GL11.glEndList();
		}
		GL11.glCallList (listnum);
	}


//	private static int init=0;
	private static boolean init=false;
	private static float len2,len1,scale;
	private void drawSphereShadow (float px, float py, float pz, float radius)
	{
		// calculate shadow constants based on light vector
		if (!init) {
			len2 = LIGHTX*LIGHTX + LIGHTY*LIGHTY;
			len1 = 1.0f/(float)Math.sqrt(len2);
			scale = (float) Math.sqrt(len2 + 1);
			init = true;
		}

		// map sphere center to ground plane based on light vector
		px -= LIGHTX*pz;
		py -= LIGHTY*pz;

		final float kx = 0.96592582628907f;
		final float ky = 0.25881904510252f;
		float x=radius, y=0;

		GL11.glBegin (GL11.GL_TRIANGLE_FAN);
		for (int i=0; i<24; i++) {
			// for all points on circle, scale to elongated rotated shadow and draw
			float x2 = (LIGHTX*x*scale - LIGHTY*y)*len1 + px;
			float y2 = (LIGHTY*x*scale + LIGHTX*y)*len1 + py;
			GL11.glTexCoord2f (x2*ground_scale+ground_ofsx,y2*ground_scale+ground_ofsy);
			GL11.glVertex3f (x2,y2,0);

			// rotate [x,y] vector
			float xtmp = kx*x - ky*y;
			y = ky*x + kx*y;
			x = xtmp;
		}
		GL11.glEnd();
	}


//	static void drawTriangle (final float *v0, final float *v1, final float *v2, int solid)
	private void drawTriangle (final float []vAll, final int v0, final int v1, 
			final int v2, boolean solid)
	{
		float[] u=new float[3],v=new float[3],normal=new float[3];
		u[0] = vAll[v1] - vAll[v0];//v1[0] - v0[0];
		u[1] = vAll[v1+1] - vAll[v0+1];//v1[1] - v0[1];
		u[2] = vAll[v1+2] - vAll[v0+2];//v1[2] - v0[2];
		v[0] = vAll[v2] - vAll[v0];//v2[0] - v0[0];
		v[1] = vAll[v2+1] - vAll[v0+1];//v2[1] - v0[1];
		v[2] = vAll[v2+2] - vAll[v0+2];//v2[2] - v0[2];
		crossProduct3(normal,u,v);
		normalizeVector3 (normal);

		GL11.glBegin(solid ? GL11.GL_TRIANGLES : GL11.GL_LINE_STRIP);
		GL11.glNormal3f (normal[0], normal[1], normal[2]);
		GL11.glVertex3f (vAll[v0], vAll[v0+1], vAll[v0+2]);//, v0[0], v0[1], v0[2]);
		GL11.glVertex3f (vAll[v1], vAll[v1+1], vAll[v1+2]);//v1[0], v1[1], v1[2]);
		GL11.glVertex3f (vAll[v2], vAll[v2+1], vAll[v2+2]);//v2[0], v2[1], v2[2]);
		GL11.glEnd();
	}

	private void drawTriangle (final float []v0, final float []v1, 
			final float []v2, boolean solid)
	{
		float[] u=new float[3],v=new float[3],normal=new float[3];
		u[0] = v1[0] - v0[0];
		u[1] = v1[1] - v0[1];
		u[2] = v1[2] - v0[2];
		v[0] = v2[0] - v0[0];
		v[1] = v2[1] - v0[1];
		v[2] = v2[2] - v0[2];
		crossProduct3 (normal,u,v);
		normalizeVector3 (normal);

		GL11.glBegin(solid ? GL11.GL_TRIANGLES : GL11.GL_LINE_STRIP);
		GL11.glNormal3f (normal[0], normal[1], normal[2]);
		GL11.glVertex3f (v0[0], v0[1], v0[2]);
		GL11.glVertex3f (v1[0], v1[1], v1[2]);
		GL11.glVertex3f (v2[0], v2[1], v2[2]);
		GL11.glEnd();
	}

//	private static void drawTriangleD (final double []v0, final double []v1, 
//			final double []v2, boolean solid)
//	{
//		float[] u=new float[3],v=new float[3],normal=new float[3];
//		u[0] = (float) ( v1[0] - v0[0] );
//		u[1] = (float) ( v1[1] - v0[1] );
//		u[2] = (float) ( v1[2] - v0[2] );
//		v[0] = (float) ( v2[0] - v0[0] );
//		v[1] = (float) ( v2[1] - v0[1] );
//		v[2] = (float) ( v2[2] - v0[2] );
//		OdeMath.dCROSS (normal,OP.EQ,u,v);
//		normalizeVector3 (normal);
//
//		GL11.glBegin(solid ? GL11.GL_TRIANGLES : GL11.GL_LINE_STRIP);
//		GL11.glNormal3f (normal[0], normal[1], normal[2]);
//		GL11.glVertex3d (v0[0], v0[1], v0[2]);
//		GL11.glVertex3d (v1[0], v1[1], v1[2]);
//		GL11.glVertex3d (v2[0], v2[1], v2[2]);
//		GL11.glEnd();
//	}

	private void drawTriangle (final DVector3C v0, final DVector3C v1, 
			final DVector3C v2, boolean solid)
	{
		float[] u=new float[3],v=new float[3],normal=new float[3];
		u[0] = (float) ( v1.get0() - v0.get0() );
		u[1] = (float) ( v1.get1() - v0.get1() );
		u[2] = (float) ( v1.get2() - v0.get2() );
		v[0] = (float) ( v2.get0() - v0.get0() );
		v[1] = (float) ( v2.get1() - v0.get1() );
		v[2] = (float) ( v2.get2() - v0.get2() );
		crossProduct3 (normal,u,v);
		normalizeVector3 (normal);

		GL11.glBegin(solid ? GL11.GL_TRIANGLES : GL11.GL_LINE_STRIP);
		GL11.glNormal3f (normal[0], normal[1], normal[2]);
		GL11.glVertex3d (v0.get0(), v0.get1(), v0.get2());
		GL11.glVertex3d (v1.get0(), v1.get1(), v1.get2());
		GL11.glVertex3d (v2.get0(), v2.get1(), v2.get2());
		GL11.glEnd();
	}


	// draw a capped cylinder of length l and radius r, aligned along the x axis

	private int capped_cylinder_quality = 3;

	private void drawCapsule (float l, float r)
	{
		int i,j;
		float tmp,nx,ny,nz,start_nx,start_ny,a,ca,sa;
		// number of sides to the cylinder (divisible by 4):
		final int n = capped_cylinder_quality*4;

		l *= 0.5;
		a = (float) ((M_PI*2.0)/n);
		sa = (float) Math.sin(a);
		ca = (float) Math.cos(a);

		// draw cylinder body
		ny=1; nz=0;		  // normal vector = (0,ny,nz)
		GL11.glBegin (GL11.GL_TRIANGLE_STRIP);
		for (i=0; i<=n; i++) {
			GL11.glNormal3d (ny,nz,0);
			GL11.glVertex3d (ny*r,nz*r,l);
			GL11.glNormal3d (ny,nz,0);
			GL11.glVertex3d (ny*r,nz*r,-l);
			// rotate ny,nz
			tmp = ca*ny - sa*nz;
			nz = sa*ny + ca*nz;
			ny = tmp;
		}
		GL11.glEnd();

		// draw first cylinder cap
		start_nx = 0;
		start_ny = 1;
		for (j=0; j<(n/4); j++) {
			// get start_n2 = rotated start_n
			float start_nx2 =  ca*start_nx + sa*start_ny;
			float start_ny2 = -sa*start_nx + ca*start_ny;
			// get n=start_n and n2=start_n2
			nx = start_nx; ny = start_ny; nz = 0;
			float nx2 = start_nx2, ny2 = start_ny2, nz2 = 0;
			GL11.glBegin (GL11.GL_TRIANGLE_STRIP);
			for (i=0; i<=n; i++) {
				GL11.glNormal3d (ny2,nz2,nx2);
				GL11.glVertex3d (ny2*r,nz2*r,l+nx2*r);
				GL11.glNormal3d (ny,nz,nx);
				GL11.glVertex3d (ny*r,nz*r,l+nx*r);
				// rotate n,n2
				tmp = ca*ny - sa*nz;
				nz = sa*ny + ca*nz;
				ny = tmp;
				tmp = ca*ny2- sa*nz2;
				nz2 = sa*ny2 + ca*nz2;
				ny2 = tmp;
			}
			GL11.glEnd();
			start_nx = start_nx2;
			start_ny = start_ny2;
		}

		// draw second cylinder cap
		start_nx = 0;
		start_ny = 1;
		for (j=0; j<(n/4); j++) {
			// get start_n2 = rotated start_n
			float start_nx2 = ca*start_nx - sa*start_ny;
			float start_ny2 = sa*start_nx + ca*start_ny;
			// get n=start_n and n2=start_n2
			nx = start_nx; ny = start_ny; nz = 0;
			float nx2 = start_nx2, ny2 = start_ny2, nz2 = 0;
			GL11.glBegin (GL11.GL_TRIANGLE_STRIP);
			for (i=0; i<=n; i++) {
				GL11.glNormal3d (ny,nz,nx);
				GL11.glVertex3d (ny*r,nz*r,-l+nx*r);
				GL11.glNormal3d (ny2,nz2,nx2);
				GL11.glVertex3d (ny2*r,nz2*r,-l+nx2*r);
				// rotate n,n2
				tmp = ca*ny - sa*nz;
				nz = sa*ny + ca*nz;
				ny = tmp;
				tmp = ca*ny2- sa*nz2;
				nz2 = sa*ny2 + ca*nz2;
				ny2 = tmp;
			}
			GL11.glEnd();
			start_nx = start_nx2;
			start_ny = start_ny2;
		}
	}


	// draw a cylinder of length l and radius r, aligned along the z axis

	private void drawCylinder (float l, float r, float zoffset)
	{
		int i;
		float tmp,ny,nz,a,ca,sa;
		final int n = 24;	// number of sides to the cylinder (divisible by 4)

		l *= 0.5;
		a = (float)(M_PI*2.0/n);
		sa = (float) Math.sin(a);
		ca = (float) Math.cos(a);

		// draw cylinder body
		ny=1; nz=0;		  // normal vector = (0,ny,nz)
		GL11.glBegin (GL11.GL_TRIANGLE_STRIP);
		for (i=0; i<=n; i++) {
			GL11.glNormal3d (ny,nz,0);
			GL11.glVertex3d (ny*r,nz*r,l+zoffset);
			GL11.glNormal3d (ny,nz,0);
			GL11.glVertex3d (ny*r,nz*r,-l+zoffset);
			// rotate ny,nz
			tmp = ca*ny - sa*nz;
			nz = sa*ny + ca*nz;
			ny = tmp;
		}
		GL11.glEnd();

		// draw top cap
		GL11.glShadeModel (GL11.GL_FLAT);
		ny=1; nz=0;		  // normal vector = (0,ny,nz)
		GL11.glBegin (GL11.GL_TRIANGLE_FAN);
		GL11.glNormal3d (0,0,1);
		GL11.glVertex3d (0,0,l+zoffset);
		for (i=0; i<=n; i++) {
			if (i==1 || i==n/2+1)
				setColor (color[0]*0.75f,color[1]*0.75f,color[2]*0.75f,color[3]);
			GL11.glNormal3d (0,0,1);
			GL11.glVertex3d (ny*r,nz*r,l+zoffset);
			if (i==1 || i==n/2+1)
				setColor (color[0],color[1],color[2],color[3]);

			// rotate ny,nz
			tmp = ca*ny - sa*nz;
			nz = sa*ny + ca*nz;
			ny = tmp;
		}
		GL11.glEnd();

		// draw bottom cap
		ny=1; nz=0;		  // normal vector = (0,ny,nz)
		GL11.glBegin (GL11.GL_TRIANGLE_FAN);
		GL11.glNormal3d (0,0,-1);
		GL11.glVertex3d (0,0,-l+zoffset);
		for (i=0; i<=n; i++) {
			if (i==1 || i==n/2+1)
				setColor (color[0]*0.75f,color[1]*0.75f,color[2]*0.75f,color[3]);
			GL11.glNormal3d (0,0,-1);
			GL11.glVertex3d (ny*r,nz*r,-l+zoffset);
			if (i==1 || i==n/2+1)
				setColor (color[0],color[1],color[2],color[3]);

			// rotate ny,nz
			tmp = ca*ny + sa*nz;
			nz = -sa*ny + ca*nz;
			ny = tmp;
		}
		GL11.glEnd();
	}

	//***************************************************************************
	// motion model

	// current camera position and orientation
	private float[] view_xyz = new float[3];	// position x,y,z
	private float[] view_hpr = new float[3];	// heading, pitch, roll (degrees)


	// initialize the above variables

	private void initMotionModel()
	{
		view_xyz[0] = 2;
		view_xyz[1] = 0;
		view_xyz[2] = 1;
		view_hpr[0] = 180;
		view_hpr[1] = 0;
		view_hpr[2] = 0;
	}


	private void wrapCameraAngles()
	{
		for (int i=0; i<3; i++) {
			while (view_hpr[i] > 180) view_hpr[i] -= 360;
			while (view_hpr[i] < -180) view_hpr[i] += 360;
		}
	}


	/**
	 * Call this to update the current camera position. the bits in `mode' say
	 * if the left (1), middle (2) or right (4) mouse button is pressed, and
	 * (deltax,deltay) is the amount by which the mouse pointer has moved.
	 */
	@Override
	void dsMotion (int mode, int deltax, int deltay)
	{
		float side = 0.01f * deltax;
		float fwd = (mode==4) ? (0.01f * deltay) : 0.0f;
		float s = (float) Math.sin (view_hpr[0]*DEG_TO_RAD);
		float c = (float) Math.cos (view_hpr[0]*DEG_TO_RAD);

		if (mode==1) {
			view_hpr[0] += deltax * 0.5f;
			view_hpr[1] += deltay * 0.5f;
		}
		else {
			view_xyz[0] += -s*side + c*fwd;
			view_xyz[1] += c*side + s*fwd;
			if (mode==2 || mode==5) view_xyz[2] += 0.01f * deltay;
		}
		wrapCameraAngles();
	}

	//***************************************************************************
	// drawing loop stuff

	// the current state:
	//    0 = uninitialized
	//    1 = dsSimulationLoop() called
	//    2 = dsDrawFrame() called
	private static int current_state = 0;

	// textures and shadows
	private static boolean use_textures=true;		// 1 if textures to be drawn
	private static boolean use_shadows=true;		// 1 if shadows to be drawn
	private static Texture sky_texture = null;
	private static Texture ground_texture = null;
	private static Texture wood_texture = null;
	private static Texture checkered_texture = null;
	private static Texture[] texture = new Texture[4+1]; // +1 since index 0 is not used



//	#ifndef macintosh

//	void dsStartGraphics (int width, int height, dsFunctions *fn)
	@Override
	void dsStartGraphics (int width, int height, dsFunctions fn)
	{

		String prefix = DEFAULT_PATH_TO_TEXTURES;
		if (fn.dsGetVersion() >= 2 && fn.dsGetPathToTextures()!=null) 
			prefix = fn.dsGetPathToTextures();
//		char *s = (char*) alloca (strlen(prefix) + 20);
		
//		strcpy (s,prefix);
//		strcat (s,"/sky.ppm");
		sky_texture = new Texture (prefix+"/sky.ppm");
		texture[DS_TEXTURE_NUMBER.DS_SKY.ordinal()] = sky_texture;

//		strcpy (s,prefix);
//		strcat (s,"/ground.ppm");
		ground_texture = new Texture (prefix+"/ground.ppm");
		texture[DS_TEXTURE_NUMBER.DS_GROUND.ordinal()] = ground_texture;

//		strcpy (s,prefix);
//		strcat (s,"/wood.ppm");
		wood_texture = new Texture (prefix+"/wood.ppm");
		texture[DS_TEXTURE_NUMBER.DS_WOOD.ordinal()] = wood_texture; 

//		strcpy (s,prefix);
//		strcat (s,"/checkered.ppm");
		checkered_texture = new Texture (prefix+"/checkered.ppm");
		texture[DS_TEXTURE_NUMBER.DS_CHECKERED.ordinal()] = checkered_texture; 
	}


	@Override
	void dsStopGraphics()
	{
//		delete sky_texture;
//		delete ground_texture;
//		delete wood_texture;
//		sky_texture = 0;
//		ground_texture = 0;
//		wood_texture = 0;
		sky_texture = null;
		ground_texture = null;
		wood_texture = null;
	}


	private static float offset = 0.0f;
//	static void drawSky (float view_xyz[3])
	private void drawSky (float[] view_xyz)
	{
		GL11.glDisable (GL11.GL_LIGHTING);
		if (use_textures) {
			GL11.glEnable (GL11.GL_TEXTURE_2D);
			sky_texture.bind (false);
		}
		else {
			GL11.glDisable (GL11.GL_TEXTURE_2D);
			GL11.glColor3f (0f,0.5f,1.0f);
		}

		// make sure sky depth is as far back as possible
		GL11.glShadeModel (GL11.GL_FLAT);
		GL11.glEnable (GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc (GL11.GL_LEQUAL);
		GL11.glDepthRange (1,1);

		final float ssize = 1000.0f;

		float x = ssize*sky_scale;
		float z = view_xyz[2] + sky_height;

		GL11.glBegin (GL11.GL_QUADS);
		GL11.glNormal3f (0,0,-1);
		GL11.glTexCoord2f (-x+offset,-x+offset);
		GL11.glVertex3f (-ssize+view_xyz[0],-ssize+view_xyz[1],z);
		GL11.glTexCoord2f (-x+offset,x+offset);
		GL11.glVertex3f (-ssize+view_xyz[0],ssize+view_xyz[1],z);
		GL11.glTexCoord2f (x+offset,x+offset);
		GL11.glVertex3f (ssize+view_xyz[0],ssize+view_xyz[1],z);
		GL11.glTexCoord2f (x+offset,-x+offset);
		GL11.glVertex3f (ssize+view_xyz[0],-ssize+view_xyz[1],z);
		GL11.glEnd();

		offset = offset + 0.002f;
		if (offset > 1) offset -= 1;

		GL11.glDepthFunc (GL11.GL_LESS);
		GL11.glDepthRange (0,1);
	}


	private void drawGround()
	{
		GL11.glDisable (GL11.GL_LIGHTING);
		GL11.glShadeModel (GL11.GL_FLAT);
		GL11.glEnable (GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc (GL11.GL_LESS);
		// GL11.glDepthRange (1,1);

		if (use_textures) {
			GL11.glEnable (GL11.GL_TEXTURE_2D);
			ground_texture.bind (false);
		}
		else {
			GL11.glDisable (GL11.GL_TEXTURE_2D);
			GL11.glColor3f (GROUND_R,GROUND_G,GROUND_B);
		}

		// ground fog seems to cause problems with TNT2 under windows
		/*
  GLfloat fogColor[4] = {0.5, 0.5, 0.5, 1};
  GL11.glEnable (GL_FOG);
  GL11.glFogi (GL_FOG_MODE, GL_EXP2);
  GL11.glFogfv (GL_FOG_COLOR, fogColor);
  GL11.glFogf (GL_FOG_DENSITY, 0.05f);
  GL11.glHint (GL_FOG_HINT, GL_NICEST); // GL_DONT_CARE);
  GL11.glFogf (GL_FOG_START, 1.0);
  GL11.glFogf (GL_FOG_END, 5.0);
		 */

		final float gsize = 100.0f;
		final float offset = 0; // -0.001f; ... polygon offsetting doesn't work well

		GL11.glBegin (GL11.GL_QUADS);
		GL11.glNormal3f (0,0,1);
		GL11.glTexCoord2f (-gsize*ground_scale + ground_ofsx,
				-gsize*ground_scale + ground_ofsy);
		GL11.glVertex3f (-gsize,-gsize,offset);
		GL11.glTexCoord2f (gsize*ground_scale + ground_ofsx,
				-gsize*ground_scale + ground_ofsy);
		GL11.glVertex3f (gsize,-gsize,offset);
		GL11.glTexCoord2f (gsize*ground_scale + ground_ofsx,
				gsize*ground_scale + ground_ofsy);
		GL11.glVertex3f (gsize,gsize,offset);
		GL11.glTexCoord2f (-gsize*ground_scale + ground_ofsx,
				gsize*ground_scale + ground_ofsy);
		GL11.glVertex3f (-gsize,gsize,offset);
		GL11.glEnd();

		GL11.glDisable (GL11.GL_FOG);
	}


	private void drawPyramidGrid()
	{
		// setup stuff
		GL11.glEnable (GL11.GL_LIGHTING);
		GL11.glDisable (GL11.GL_TEXTURE_2D);
		GL11.glShadeModel (GL11.GL_FLAT);
		GL11.glEnable (GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc (GL11.GL_LESS);

		// draw the pyramid grid
		for (int i=-1; i<=1; i++) {
			for (int j=-1; j<=1; j++) {
				GL11.glPushMatrix();
				GL11.glTranslatef (i,j,0);
				if (i==1 && j==0) setColor (1,0,0,1);
				else if (i==0 && j==1) setColor (0,0,1,1);
				else setColor (1,1,0,1);
				final float k = 0.03f;
				GL11.glBegin (GL11.GL_TRIANGLE_FAN);
				GL11.glNormal3f (0,-1,1);
				GL11.glVertex3f (0,0,k);
				GL11.glVertex3f (-k,-k,0);
				GL11.glVertex3f ( k,-k,0);
				GL11.glNormal3f (1,0,1);
				GL11.glVertex3f ( k, k,0);
				GL11.glNormal3f (0,1,1);
				GL11.glVertex3f (-k, k,0);
				GL11.glNormal3f (-1,0,1);
				GL11.glVertex3f (-k,-k,0);
				GL11.glEnd();
				GL11.glPopMatrix();
			}
		}
	}


	//static GLfloat 
	private static final FloatBuffer light_position =  BufferUtils.createFloatBuffer(4);
	private static final FloatBuffer light_ambient = BufferUtils.createFloatBuffer(4);
	private static final FloatBuffer light_diffuse = BufferUtils.createFloatBuffer(4);
	private static final FloatBuffer light_specular = BufferUtils.createFloatBuffer(4);
	static {
		light_position.put(new float[] { LIGHTX, LIGHTY, 1.0f, 0.0f }).flip();
		light_ambient.put(new float[]{ 0.5f, 0.5f, 0.5f, 1.0f }).flip();
		light_diffuse.put(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }).flip();
		light_specular.put(new float[] { 1.0f, 1.0f, 1.0f, 1.0f }).flip();
	}

	@Override
	//	void dsDrawFrame (int width, int height, dsFunctions *fn, int pause)
	void dsDrawFrame (int width, int height, dsFunctions fn, boolean pause)
	{
		if (current_state < 1) dsDebug ("internal error");
		current_state = 2;

		// setup stuff
		GL11.glEnable (GL11.GL_LIGHTING);
		GL11.glEnable (GL11.GL_LIGHT0);
		GL11.glDisable (GL11.GL_TEXTURE_2D);
		GL11.glDisable (GL11.GL_TEXTURE_GEN_S);
		GL11.glDisable (GL11.GL_TEXTURE_GEN_T);
		GL11.glShadeModel (GL11.GL_FLAT);
		GL11.glEnable (GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc (GL11.GL_LESS);
		GL11.glEnable (GL11.GL_CULL_FACE);
		GL11.glCullFace (GL11.GL_BACK);
		GL11.glFrontFace (GL11.GL_CCW);

		// setup viewport
		GL11.glViewport (0,0,width,height);
		GL11.glMatrixMode (GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		final float vnear = 0.1f;
		final float vfar = 100.0f;
		final float k = 0.8f;     // view scale, 1 = +/- 45 degrees
		if (width >= height) {
			float k2 = (float)height/(float)width;
			GL11.glFrustum (-vnear*k,vnear*k,-vnear*k*k2,vnear*k*k2,vnear,vfar);
		}
		else {
			float k2 = (float)width/(float)height;
			GL11.glFrustum (-vnear*k*k2,vnear*k*k2,-vnear*k,vnear*k,vnear,vfar);
		}
		
		// setup lights. it makes a difference whether this is done in the
		// GL_PROJECTION matrix mode (lights are scene relative) or the
		// GL_MODELVIEW matrix mode (lights are camera relative, bad!).
//		static GLfloat light_ambient[] = { 0.5, 0.5, 0.5, 1.0 };
//		static GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
//		static GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };
		GL11.glLightfv (GL11.GL_LIGHT0, GL11.GL_AMBIENT, light_ambient);
		GL11.glLightfv (GL11.GL_LIGHT0, GL11.GL_DIFFUSE, light_diffuse);
		GL11.glLightfv (GL11.GL_LIGHT0, GL11.GL_SPECULAR, light_specular);
		GL11.glColor3f (1.0f, 1.0f, 1.0f);

		// clear the window
		GL11.glClearColor (0.5f ,0.5f ,0.5f ,0);
		GL11.glClear (GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

		// snapshot camera position (in MS Windows it is changed by the GUI thread)
		float[] view2_xyz=view_xyz.clone();
		float[] view2_hpr=view_hpr.clone();
//		memcpy (view2_xyz,view_xyz);//,sizeof(float)*3);
//		memcpy (view2_hpr,view_hpr);//,sizeof(float)*3);

		// go to GL_MODELVIEW matrix mode and set the camera
		GL11.glMatrixMode (GL11.GL_MODELVIEW);
		GL11.glLoadIdentity();
		setCamera (view2_xyz[0],view2_xyz[1],view2_xyz[2],
				view2_hpr[0],view2_hpr[1],view2_hpr[2]);

		// set the light position (for some reason we have to do this in model view.
//		static GLfloat light_position[] = { LIGHTX, LIGHTY, 1.0, 0.0 };
		GL11.glLightfv (GL11.GL_LIGHT0, GL11.GL_POSITION, light_position);

		// draw the background (ground, sky etc)
		drawSky (view2_xyz);
		drawGround();

		// draw the little markers on the ground
		drawPyramidGrid();

		// leave openGL in a known state - flat shaded white, no textures
		GL11.glEnable (GL11.GL_LIGHTING);
		GL11.glDisable (GL11.GL_TEXTURE_2D);
		GL11.glShadeModel (GL11.GL_FLAT);
		GL11.glEnable (GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc (GL11.GL_LESS);
		GL11.glColor3f (1,1,1);
		setColor (1,1,1,1);

		// draw the rest of the objects. set drawing state first.
		color[0] = 1;
		color[1] = 1;
		color[2] = 1;
		color[3] = 1;
		tnum = DS_TEXTURE_NUMBER.DS_NONE;
		//if (fn.step) 
		fn.step(pause);
	}


	@Override
	boolean dsGetShadows()
	{
		return use_shadows;
	}


	@Override
	void dsSetShadows (boolean a) {
		use_shadows = a;
	}


	@Override
	boolean dsGetTextures()
	{
		return use_textures;
	}


	@Override
	void dsSetTextures (boolean a) {
		use_textures = a;
	}

	//***************************************************************************
	// C interface

	// sets lighting and texture modes, sets current color
	private static final FloatBuffer s_params_SDM = BufferUtils.createFloatBuffer(4);
	private static final FloatBuffer t_params_SDM = BufferUtils.createFloatBuffer(4);
	static {
		s_params_SDM.put(new float[]{1.0f,1.0f,0.0f,1}).flip();
		t_params_SDM.put(new float[]{0.817f,-0.817f,0.817f,1}).flip();
	}
	private void setupDrawingMode()
	{
		GL11.glEnable (GL11.GL_LIGHTING);
		if (tnum != DS_TEXTURE_NUMBER.DS_NONE) {
			if (use_textures) {
				GL11.glEnable (GL11.GL_TEXTURE_2D);
				texture[tnum.ordinal()].bind (true);
				GL11.glEnable (GL11.GL_TEXTURE_GEN_S);
				GL11.glEnable (GL11.GL_TEXTURE_GEN_T);
				GL11.glTexGeni (GL11.GL_S,GL11.GL_TEXTURE_GEN_MODE,GL11.GL_OBJECT_LINEAR);
				GL11.glTexGeni (GL11.GL_T,GL11.GL_TEXTURE_GEN_MODE,GL11.GL_OBJECT_LINEAR);
//				static GLfloat s_params[4] = {1.0f,1.0f,0.0f,1};
//				static GLfloat t_params[4] = {0.817f,-0.817f,0.817f,1};
				GL11.glTexGenfv (GL11.GL_S,GL11.GL_OBJECT_PLANE,s_params_SDM);
				GL11.glTexGenfv (GL11.GL_T,GL11.GL_OBJECT_PLANE,t_params_SDM);
			}
			else {
				GL11.glDisable (GL11.GL_TEXTURE_2D);
			}
		}
		else {
			GL11.glDisable (GL11.GL_TEXTURE_2D);
		}
		setColor (color[0],color[1],color[2],color[3]);

		if (color[3] < 1) {
			GL11.glEnable (GL11.GL_BLEND);
			GL11.glBlendFunc (GL11.GL_SRC_ALPHA,GL11.GL_ONE_MINUS_SRC_ALPHA);
		}
		else {
			GL11.glDisable (GL11.GL_BLEND);
		}
	}


	private static final FloatBuffer s_params_SSDM = BufferUtils.createFloatBuffer(4);
	private static final FloatBuffer t_params_SSDM = BufferUtils.createFloatBuffer(4);
	static {
		s_params_SSDM.put(new float[]{ground_scale,0,0,ground_ofsx}).flip();
		t_params_SSDM.put(new float[]{0,ground_scale,0,ground_ofsy}).flip();
	}
	private void setShadowDrawingMode()
	{
		GL11.glDisable (GL11.GL_LIGHTING);
		if (use_textures) {
			GL11.glEnable (GL11.GL_TEXTURE_2D);
			ground_texture.bind (true);
			GL11.glColor3f (SHADOW_INTENSITY,SHADOW_INTENSITY,SHADOW_INTENSITY);
			GL11.glEnable (GL11.GL_TEXTURE_2D);
			GL11.glEnable (GL11.GL_TEXTURE_GEN_S);
			GL11.glEnable (GL11.GL_TEXTURE_GEN_T);
			GL11.glTexGeni (GL11.GL_S,GL11.GL_TEXTURE_GEN_MODE,GL11.GL_EYE_LINEAR);
			GL11.glTexGeni (GL11.GL_T,GL11.GL_TEXTURE_GEN_MODE,GL11.GL_EYE_LINEAR);
//			static GLfloat s_params[4] = {ground_scale,0,0,ground_ofsx};
//			static GLfloat t_params[4] = {0,ground_scale,0,ground_ofsy};
			GL11.glTexGenfv (GL11.GL_S,GL11.GL_EYE_PLANE,s_params_SSDM);
			GL11.glTexGenfv (GL11.GL_T,GL11.GL_EYE_PLANE,t_params_SSDM);
		}
		else {
			GL11.glDisable (GL11.GL_TEXTURE_2D);
			GL11.glColor3f (GROUND_R*SHADOW_INTENSITY,GROUND_G*SHADOW_INTENSITY,
					GROUND_B*SHADOW_INTENSITY);
		}
		GL11.glDepthRange (0,0.9999);
	}


	
	//extern "C" 
	/**
	 * If you filter out arguments beforehand, simply set them to "".
	 * @see org.ode4j.drawstuff.DrawStuff#dsSimulationLoop(String[], int, int, dsFunctions)
	 */
	@Override
	public void dsSimulationLoop (String[] args,
			int window_width, int window_height,
			dsFunctions fn)
	{
		if (current_state != 0) dsError ("dsSimulationLoop() called more than once");
		current_state = 1;

		// look for flags that apply to us
		boolean initial_pause = false;
		for (int i=0; i DS_VERSION)
			dsDebug ("bad version number in dsFunctions structure");

		initMotionModel();
		dsPlatformSimLoop (window_width,window_height,fn,initial_pause);

		current_state = 0;
	}


	//extern "C" 
	//void dsSetViewpoint (float xyz[3], float hpr[3])
	@Override
	public void dsSetViewpoint (float[] xyz, float[] hpr)
	{
		if (current_state < 1) dsError ("dsSetViewpoint() called before simulation started");
		if (xyz!=null) {
			view_xyz[0] = xyz[0];
			view_xyz[1] = xyz[1];
			view_xyz[2] = xyz[2];
		}
		if (hpr!=null) {
			view_hpr[0] = hpr[0];
			view_hpr[1] = hpr[1];
			view_hpr[2] = hpr[2];
			wrapCameraAngles();
		}
	}


	//extern "C" 
	//void dsGetViewpoint (float xyz[3], float hpr[3])
	@Override
	public void dsGetViewpoint (float[] xyz, float[] hpr)
	{
		if (current_state < 1) dsError ("dsGetViewpoint() called before simulation started");
		if (xyz!=null) {
			xyz[0] = view_xyz[0];
			xyz[1] = view_xyz[1];
			xyz[2] = view_xyz[2];
		}
		if (hpr!=null) {
			hpr[0] = view_hpr[0];
			hpr[1] = view_hpr[1];
			hpr[2] = view_hpr[2];
		}
	}


	//extern "C" 
	/* (non-Javadoc)
	 * @see org.ode4j.drawstuff.internal.DrawStuff#dsSetTexture(org.ode4j.drawstuff.DS_API.DS_TEXTURE_NUMBER)
	 */
	@Override
	public void dsSetTexture (DS_TEXTURE_NUMBER texture_number)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		tnum = texture_number;
	}


	//extern "C" 
	/* (non-Javadoc)
	 * @see org.ode4j.drawstuff.internal.DrawStuff#dsSetColor(float, float, float)
	 */
	@Override
	public void dsSetColor (float red, float green, float blue)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		color[0] = red;
		color[1] = green;
		color[2] = blue;
		color[3] = 1;
	}


	//extern "C" 
	@Override
	public void dsSetColorAlpha (float red, float green, float blue, float alpha)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		color[0] = red;
		color[1] = green;
		color[2] = blue;
		color[3] = alpha;
	}


	//extern "C" 
//	void dsDrawBox (final float pos[3], final float R[12],
//			final float sides[3])
	/* (non-Javadoc)
	 * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawBox(float[], float[], float[])
	 */
	@Override
	public void dsDrawBox (final float[] pos, final float[] R,
			final float[] sides)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawBox (sides);
		GL11.glPopMatrix();

		if (use_shadows) {
			setShadowDrawingMode();
			setShadowTransform();
			setTransform (pos,R);
			drawBox (sides);
			GL11.glPopMatrix();
			GL11.glPopMatrix();
			GL11.glDepthRange (0,1);
		}
	}

	//extern "C" 
//	void dsDrawConvex (final float pos[3], final float R[12],
//			float *_planes,unsigned int _planecount,
//			float *_points,
//			unsigned int _pointcount,
//			unsigned int *_polygons)
	void dsDrawConvex (final float[] pos, final float[] R,
			float[] _planes, int _planecount,
			float[] _points,
			int _pointcount,
			int[] _polygons)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawConvex(_planes,_planecount,_points,_pointcount,_polygons);
		GL11.glPopMatrix();
		if (use_shadows) {
			setShadowDrawingMode();
			setShadowTransform();
			setTransform (pos,R);
			drawConvex(_planes,_planecount,_points,_pointcount,_polygons);
			GL11.glPopMatrix();
			GL11.glPopMatrix();
			GL11.glDepthRange (0,1);
		}
	}


	//extern "C" 
//	void dsDrawSphere (final float pos[3], final float R[12],
//			float radius)
	/* (non-Javadoc)
	 * @see org.ode4j.drawstuff.internal.DrawStuff#dsDrawSphere(float[], float[], float)
	 */
	@Override
	public void dsDrawSphere (final float[] pos, final float[] R, float radius)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glEnable (GL11.GL_NORMALIZE);
		GL11.glShadeModel (GL11.GL_SMOOTH);
		setTransform (pos,R);
		GL11.glScaled (radius,radius,radius);
		drawSphere();
		GL11.glPopMatrix();
		GL11.glDisable (GL11.GL_NORMALIZE);

		// draw shadows
		if (use_shadows) {
			GL11.glDisable (GL11.GL_LIGHTING);
			if (use_textures) {
				ground_texture.bind (true);
				GL11.glEnable (GL11.GL_TEXTURE_2D);
				GL11.glDisable (GL11.GL_TEXTURE_GEN_S);
				GL11.glDisable (GL11.GL_TEXTURE_GEN_T);
				GL11.glColor3f (SHADOW_INTENSITY,SHADOW_INTENSITY,SHADOW_INTENSITY);
			}
			else {
				GL11.glDisable (GL11.GL_TEXTURE_2D);
				GL11.glColor3f (GROUND_R*SHADOW_INTENSITY,GROUND_G*SHADOW_INTENSITY,
						GROUND_B*SHADOW_INTENSITY);
			}
			GL11.glShadeModel (GL11.GL_FLAT);
			GL11.glDepthRange (0,0.9999);
			drawSphereShadow (pos[0],pos[1],pos[2],radius);
			GL11.glDepthRange (0,1);
		}
	}


	//extern "C" 
//	void dsDrawTriangle (final float pos[3], final float R[12],
//			final float *v0, final float *v1,
//			final float *v2, int solid)
	void dsDrawTriangle (final float[] pos, final float[] R,
			final float[] v0, final float[] v1,
			final float[] v2, boolean solid)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawTriangle (v0, v1, v2, solid);
		GL11.glPopMatrix();
	}


	void dsDrawTriangle (final float[] pos, final float[] R,
			final float[] vAll, final int v0, final int v1,
			final int v2, boolean solid)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawTriangle (vAll, v0, v1, v2, solid);
		GL11.glPopMatrix();
	}


	@Override
	public void dsDrawTriangle (final DVector3C pos, final DMatrix3C R,
			final float[] vAll, final int v0, final int v1,
			final int v2, boolean solid)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawTriangle (vAll, v0, v1, v2, solid);
		GL11.glPopMatrix();
	}


	@Override
	public void dsDrawTriangle (final DVector3C pos, final DMatrix3C R,
			final float[] v0, final float[] v1, final float[] v2, boolean solid)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawTriangle (v0, v1, v2, solid);
		GL11.glPopMatrix();
	}

	//	extern "C" void dsDrawTriangles (const float pos[3], const float R[12],
//				const float *v, int n, int solid)
	@Override
	public void dsDrawTriangles(final float[] pos, final float[] R,
								final float[][] v, boolean solid) {
		if (current_state != 2) dsError("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel(GL11.GL_FLAT);
		setTransform(pos, R);
		int n = v.length / 3;
		for (int i = 0; i < n; ++i) {
			drawTriangle(v[3 * i], v[3 * i + 1], v[3 * i + 2], solid);
		}
		GL11.glPopMatrix();
	}

	//extern "C" 
//	void dsDrawCylinder (final float pos[3], final float R[12],
//			float length, float radius)
	/**
	 * @see DrawStuffApi#dsDrawCylinder(float[], float[], float, float)
	 */
	@Override
	public void dsDrawCylinder (final float[] pos, final float[] R,
			float length, float radius)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_SMOOTH);
		setTransform (pos,R);
		drawCylinder (length,radius,0);
		GL11.glPopMatrix();

		if (use_shadows) {
			setShadowDrawingMode();
			setShadowTransform();
			setTransform (pos,R);
			drawCylinder (length,radius,0);
			GL11.glPopMatrix();
			GL11.glPopMatrix();
			GL11.glDepthRange (0,1);
		}
	}


	//extern "C" 
//	void dsDrawCapsule (final float pos[3], final float R[12],
//			float length, float radius)
	/**
	 * @see DrawStuffApi#dsDrawCapsule(float[], float[], float, float)
	 */
	@Override
	public void dsDrawCapsule (final float[] pos, final float[] R,
			float length, float radius)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_SMOOTH);
		setTransform (pos,R);
		drawCapsule (length,radius);
		GL11.glPopMatrix();

		if (use_shadows) {
			setShadowDrawingMode();
			setShadowTransform();
			setTransform (pos,R);
			drawCapsule (length,radius);
			GL11.glPopMatrix();
			GL11.glPopMatrix();
			GL11.glDepthRange (0,1);
		}
	}


//	void dsDrawLine (final float pos1[3], final float pos2[3])
	/** 
	 * @see DrawStuffApi#dsDrawLine(float[], float[])
	 */
	@Override
	public void dsDrawLine (final float[] pos1, final float[] pos2)
	{
		setupDrawingMode();
		GL11.glColor3f (color[0],color[1],color[2]);
		GL11.glDisable (GL11.GL_LIGHTING);
		GL11.glLineWidth (2);
		GL11.glShadeModel (GL11.GL_FLAT);
		GL11.glBegin (GL11.GL_LINES);
		GL11.glVertex3f (pos1[0],pos1[1],pos1[2]);
		GL11.glVertex3f (pos2[0],pos2[1],pos2[2]);
		GL11.glEnd();
	}


//	void dsDrawBoxD (final double pos[3], final double R[12],
//			final double sides[3])
	/**
	 * @see DrawStuffApi#dsDrawBox(float[], float[], float[])
	 */
	@Override
	public void dsDrawBox (DVector3C pos, DMatrix3C R, DVector3C sides)
	{
		float[] pos2=pos.toFloatArray4();
		float[] R2=R.toFloatArray12();
		float[] fsides=sides.toFloatArray4();
		dsDrawBox (pos2,R2,fsides);
	}

	//extern "C" 
//	void dsDrawConvexD (final double pos[3], final double R[12],
//			double *_planes,unsigned int _planecount,
//			double *_points,
//			unsigned int _pointcount,
//			unsigned int *_polygons)
	/**
	 * @see DrawStuffApi#dsDrawConvex(DVector3C, DMatrix3C, double[], int, double[], int, int[])
	 */
	@Override
	public void dsDrawConvex (DVector3C pos, DMatrix3C R,
			double[] _planes, int _planecount,
			double[] _points,
			int _pointcount,
			int[] _polygons)
	{
		if (current_state != 2) dsError ("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawConvexD(_planes,_planecount,_points,_pointcount,_polygons);
		GL11.glPopMatrix();
		if (use_shadows) {
			setShadowDrawingMode();
			setShadowTransform();
			setTransform (pos,R);
			drawConvexD(_planes,_planecount,_points,_pointcount,_polygons);
			GL11.glPopMatrix();
			GL11.glPopMatrix();
			GL11.glDepthRange (0,1);
		}
	}

//	void dsDrawSphereD (final double pos[3], final double R[12], float radius)
	/** 
	 * @see DrawStuffApi#dsDrawSphere(DVector3C, DMatrix3C, float)
	 */
	@Override
	public void dsDrawSphere (final DVector3C pos, final DMatrix3C R, float radius)
	{
		float[] pos2=pos.toFloatArray4();
		float[] R2=R.toFloatArray12();
		dsDrawSphere (pos2,R2,radius);
	}


//	void dsDrawTriangleD (final double pos[3], final double R[12],
//			final double *v0, final double *v1,
//			final double *v2, int solid)
//	void dsDrawTriangleD (final double[] pos, final double[] R,
//			final double[] v0, final double[] v1,
//			final double[] v2, boolean solid)
//	{
//		int i;
//		float[] pos2=new float[3],R2=new float[12];
//		for (i=0; i<3; i++) pos2[i]=(float)pos[i];
//		for (i=0; i<12; i++) R2[i]=(float)R[i];
//
//		setupDrawingMode();
//		GL11.glShadeModel (GL11.GL_FLAT);
//		setTransform (pos2,R2);
//		drawTriangleD (v0, v1, v2, solid);
//		GL11.glPopMatrix();
//	}


	@Override
	public void dsDrawTriangle (final DVector3C pos, final DMatrix3C R,
			final DVector3C v0, final DVector3C v1,
			final DVector3C v2, boolean solid)
	{
		setupDrawingMode();
		GL11.glShadeModel (GL11.GL_FLAT);
		setTransform (pos,R);
		drawTriangle (v0, v1, v2, solid);
		GL11.glPopMatrix();
	}

	//	extern "C" void dsDrawTrianglesD (const double pos[3], const double R[12],
//				const double *v, int n, int solid)
	public void dsDrawTriangles(final DVector3C pos, final DMatrix3C R,
								final DVector3C[] v, boolean solid) {
		int i;
		DVector3C pos2 = new DVector3(pos);
		DMatrix3C R2 = new DMatrix3(R);
//		for (i=0; i<3; i++) pos2[i]=(float)pos[i];
//		for (i=0; i<12; i++) R2[i]=(float)R[i];

		if (current_state != 2) dsError("drawing function called outside simulation loop");
		setupDrawingMode();
		GL11.glShadeModel(GL11.GL_FLAT);
		setTransform(pos2, R2);
		for (i = 0; i < v.length; ++i)
			drawTriangle(v[3 * i], v[3 * i + 1], v[3 * i + 2], solid);
		GL11.glPopMatrix();
	}


//	void dsDrawCylinderD (final double pos[3], final double R[12],
//			float length, float radius)
	/**
	 * @see DrawStuffApi#dsDrawCylinder(DVector3C, DMatrix3C, float, float)
	 */
	@Override
	public void dsDrawCylinder (final DVector3C pos, final DMatrix3C R,
			float length, float radius)
	{
		float[] pos2=pos.toFloatArray4();
		float[] R2=R.toFloatArray12();
		dsDrawCylinder (pos2,R2,length,radius);
	}


//	void dsDrawCapsuleD (final double pos[3], final double R[12],
//			float length, float radius)
	/**
	 * @see DrawStuffApi#dsDrawCapsule(DVector3C, DMatrix3C, float, float)
	 */
	@Override
	public void dsDrawCapsule (final DVector3C pos, final DMatrix3C R,
			float length, float radius)
	{
		float[] pos2=pos.toFloatArray4();
		float[] R2=R.toFloatArray12();
		dsDrawCapsule (pos2,R2,length,radius);
	}


//	void dsDrawLineD (final double _pos1[3], final double _pos2[3])
	/**
	 * @see DrawStuffApi#dsDrawLine(DVector3C, DVector3C)
	 */
	@Override
	public void dsDrawLine (final DVector3C pos1, final DVector3C pos2)
	{
		float[] pos1f=pos1.toFloatArray4();
		float[] pos2f=pos2.toFloatArray4();
		dsDrawLine (pos1f,pos2f);
	}


	@Override
	public void dsSetSphereQuality (int n)
	{
		sphere_quality = n;
	}


	void dsSetCapsuleQuality (int n)
	{
		capped_cylinder_quality = n;
	}

	@Override
	public void dsSetDrawMode(int mode)
	{
		switch(mode)
		{
		case DS_POLYFILL:
			GL11.glPolygonMode(GL11.GL_FRONT,GL11.GL_FILL);
			break;
		case DS_WIREFRAME:
			GL11.glPolygonMode(GL11.GL_FRONT,GL11.GL_LINE);
			break;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy