diff trunk/tests/ChipmunkDemos/drawSpace.d @ 16:af2f61a96318

ported chipmunk demos
author Extrawurst
date Sat, 04 Dec 2010 02:02:29 +0100
parents
children f897d96cc7c9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/trunk/tests/ChipmunkDemos/drawSpace.d	Sat Dec 04 02:02:29 2010 +0100
@@ -0,0 +1,510 @@
+/* Copyright (c) 2007 Scott Lembcke
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+module drawSpace;
+
+import derelict.opengl.gl;
+
+import chipmunkd.chipmunk;
+
+import std.math:PI;
+import std.stdio;
+
+struct drawSpaceOptions {
+	int drawHash;
+	int drawBBs;
+	int drawShapes;
+	float collisionPointSize;
+	float bodyPointSize;
+	float lineThickness;
+}
+
+/*
+	IMPORTANT - READ ME!
+	
+	This file sets up a simple interface that the individual demos can use to get
+	a Chipmunk space running and draw what's in it. In order to keep the Chipmunk
+	examples clean and simple, they contain no graphics code. All drawing is done
+	by accessing the Chipmunk structures at a very low level. It is NOT
+	recommended to write a game or application this way as it does not scale
+	beyond simple shape drawing and is very dependent on implementation details
+	about Chipmunk which may change with little to no warning.
+*/
+
+enum float[3] LINE_COLOR = [0,0,0];
+enum float[3] COLLISION_COLOR = [1,0,0];
+enum float[3] BODY_COLOR = [0,0,1];
+
+static void
+glColor_from_pointer(void *ptr)
+{
+	ulong val = cast(long)ptr;
+	
+	// hash the pointer up nicely
+	val = (val+0x7ed55d16) + (val<<12);
+	val = (val^0xc761c23c) ^ (val>>19);
+	val = (val+0x165667b1) + (val<<5);
+	val = (val+0xd3a2646c) ^ (val<<9);
+	val = (val+0xfd7046c5) + (val<<3);
+	val = (val^0xb55a4f09) ^ (val>>16);
+	
+//	GLfloat v = (GLfloat)val/(GLfloat)ULONG_MAX;
+//	v = 0.95f - v*0.15f;
+//	
+//	glColor3f(v, v, v);
+
+	GLubyte r = (val>>0) & 0xFF;
+	GLubyte g = (val>>8) & 0xFF;
+	GLubyte b = (val>>16) & 0xFF;
+	
+	GLubyte max = r>g ? (r>b ? r : b) : (g>b ? g : b);
+	
+	const int mult = 127;
+	const int add = 63;
+	r = cast(ubyte)((r*mult)/max + add);
+	g = cast(ubyte)((g*mult)/max + add);
+	b = cast(ubyte)((b*mult)/max + add);
+	
+	glColor3ub(r, g, b);
+}
+
+static void
+glColor_for_shape(cpShape *shape, cpSpace *space)
+{
+	cpBody *_body = shape._body;
+	if(_body){
+		if(_body.node.next){
+			GLfloat v = 0.25f;
+			glColor3f(v,v,v);
+			return;
+		} else if(_body.node.idleTime > space.sleepTimeThreshold) {
+			GLfloat v = 0.9f;
+			glColor3f(v,v,v);
+			return;
+		}
+	}
+	
+	glColor_from_pointer(shape);
+}
+
+enum GLfloat[] circleVAR = [
+	 0.0000f,  1.0000f,
+	 0.2588f,  0.9659f,
+	 0.5000f,  0.8660f,
+	 0.7071f,  0.7071f,
+	 0.8660f,  0.5000f,
+	 0.9659f,  0.2588f,
+	 1.0000f,  0.0000f,
+	 0.9659f, -0.2588f,
+	 0.8660f, -0.5000f,
+	 0.7071f, -0.7071f,
+	 0.5000f, -0.8660f,
+	 0.2588f, -0.9659f,
+	 0.0000f, -1.0000f,
+	-0.2588f, -0.9659f,
+	-0.5000f, -0.8660f,
+	-0.7071f, -0.7071f,
+	-0.8660f, -0.5000f,
+	-0.9659f, -0.2588f,
+	-1.0000f, -0.0000f,
+	-0.9659f,  0.2588f,
+	-0.8660f,  0.5000f,
+	-0.7071f,  0.7071f,
+	-0.5000f,  0.8660f,
+	-0.2588f,  0.9659f,
+	 0.0000f,  1.0000f,
+	 0.0f, 0.0f, // For an extra line to see the rotation.
+];
+enum int circleVAR_count = circleVAR.length / 2;
+
+static void
+drawCircleShape(cpBody *_body, cpCircleShape *circle, cpSpace *space)
+{
+	glVertexPointer(2, GL_FLOAT, 0, circleVAR.ptr);
+	
+	glPushMatrix(); {
+		cpVect center = circle.tc;
+		glTranslatef(center.x, center.y, 0.0f);
+		glRotatef(_body.a*180.0f/PI, 0.0f, 0.0f, 1.0f);
+		glScalef(circle.r, circle.r, 1.0f);
+		
+		if(!circle.shape.sensor){
+			glColor_for_shape(cast(cpShape *)circle, space);
+			glDrawArrays(GL_TRIANGLE_FAN, 0, circleVAR_count - 1);
+		}
+		
+		glColor3fv(LINE_COLOR.ptr);
+		glDrawArrays(GL_LINE_STRIP, 0, circleVAR_count);
+	} glPopMatrix();
+}
+
+enum GLfloat[] pillVAR = [
+	 0.0000f,  1.0000f, 1.0f,
+	 0.2588f,  0.9659f, 1.0f,
+	 0.5000f,  0.8660f, 1.0f,
+	 0.7071f,  0.7071f, 1.0f,
+	 0.8660f,  0.5000f, 1.0f,
+	 0.9659f,  0.2588f, 1.0f,
+	 1.0000f,  0.0000f, 1.0f,
+	 0.9659f, -0.2588f, 1.0f,
+	 0.8660f, -0.5000f, 1.0f,
+	 0.7071f, -0.7071f, 1.0f,
+	 0.5000f, -0.8660f, 1.0f,
+	 0.2588f, -0.9659f, 1.0f,
+	 0.0000f, -1.0000f, 1.0f,
+
+	 0.0000f, -1.0000f, 0.0f,
+	-0.2588f, -0.9659f, 0.0f,
+	-0.5000f, -0.8660f, 0.0f,
+	-0.7071f, -0.7071f, 0.0f,
+	-0.8660f, -0.5000f, 0.0f,
+	-0.9659f, -0.2588f, 0.0f,
+	-1.0000f, -0.0000f, 0.0f,
+	-0.9659f,  0.2588f, 0.0f,
+	-0.8660f,  0.5000f, 0.0f,
+	-0.7071f,  0.7071f, 0.0f,
+	-0.5000f,  0.8660f, 0.0f,
+	-0.2588f,  0.9659f, 0.0f,
+	 0.0000f,  1.0000f, 0.0f,
+];
+enum int pillVAR_count = pillVAR.length/3;
+
+static void
+drawSegmentShape(cpBody *_body, cpSegmentShape *seg, cpSpace *space)
+{
+	cpVect a = seg.ta;
+	cpVect b = seg.tb;
+	
+	if(seg.r){
+		glVertexPointer(3, GL_FLOAT, 0, pillVAR.ptr);
+		glPushMatrix(); {
+			cpVect d = cpvsub(b, a);
+			cpVect r = cpvmult(d, seg.r/cpvlength(d));
+
+			GLfloat matrix[] = [
+				 r.x, r.y, 0.0f, 0.0f,
+				-r.y, r.x, 0.0f, 0.0f,
+				 d.x, d.y, 0.0f, 0.0f,
+				 a.x, a.y, 0.0f, 1.0f,
+			];
+			glMultMatrixf(matrix.ptr);
+			
+			if(!seg.shape.sensor){
+				glColor_for_shape(cast(cpShape *)seg, space);
+				glDrawArrays(GL_TRIANGLE_FAN, 0, pillVAR_count);
+			}
+			
+			glColor3fv(LINE_COLOR.ptr);
+			glDrawArrays(GL_LINE_LOOP, 0, pillVAR_count);
+		} glPopMatrix();
+	} else {
+		glColor3fv(LINE_COLOR.ptr);
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	}
+}
+
+static void
+drawPolyShape(cpBody *_body, cpPolyShape *poly, cpSpace *space)
+{
+	int count = poly.numVerts;
+version(CP_USE_DOUBLES)
+{
+	glVertexPointer(2, GL_DOUBLE, 0, poly.tVerts);
+}
+else
+{
+	glVertexPointer(2, GL_FLOAT, 0, poly.tVerts);
+}
+	
+	if(!poly.shape.sensor){
+		glColor_for_shape(cast(cpShape *)poly, space);
+		glDrawArrays(GL_TRIANGLE_FAN, 0, count);
+	}
+	
+	glColor3fv(LINE_COLOR.ptr);
+	glDrawArrays(GL_LINE_LOOP, 0, count);
+}
+
+static void
+drawObject(cpShape *shape, cpSpace *space)
+{
+	cpBody *_body = shape._body;
+	
+	switch(shape.klass.type){
+		case cpShapeType.CP_CIRCLE_SHAPE:
+			drawCircleShape(_body, cast(cpCircleShape *)shape, space);
+			break;
+		case cpShapeType.CP_SEGMENT_SHAPE:
+			drawSegmentShape(_body, cast(cpSegmentShape *)shape, space);
+			break;
+		case cpShapeType.CP_POLY_SHAPE:
+			drawPolyShape(_body, cast(cpPolyShape *)shape, space);
+			break;
+		default:
+			writefln("Bad enumeration in drawObject().");
+	}
+}
+
+enum GLfloat[] springVAR = [
+	0.00f, 0.0f,
+	0.20f, 0.0f,
+	0.25f, 3.0f,
+	0.30f,-6.0f,
+	0.35f, 6.0f,
+	0.40f,-6.0f,
+	0.45f, 6.0f,
+	0.50f,-6.0f,
+	0.55f, 6.0f,
+	0.60f,-6.0f,
+	0.65f, 6.0f,
+	0.70f,-3.0f,
+	0.75f, 6.0f,
+	0.80f, 0.0f,
+	1.00f, 0.0f,
+];
+enum int springVAR_count = springVAR.length / 2;
+
+static void
+drawSpring(cpDampedSpring *spring, cpBody *body_a, cpBody *body_b)
+{
+	cpVect a = cpvadd(body_a.p, cpvrotate(spring.anchr1, body_a.rot));
+	cpVect b = cpvadd(body_b.p, cpvrotate(spring.anchr2, body_b.rot));
+
+	glPointSize(5.0f);
+	glBegin(GL_POINTS); {
+		glVertex2f(a.x, a.y);
+		glVertex2f(b.x, b.y);
+	} glEnd();
+
+	cpVect delta = cpvsub(b, a);
+
+	glVertexPointer(2, GL_FLOAT, 0, springVAR.ptr);
+	glPushMatrix(); {
+		GLfloat x = a.x;
+		GLfloat y = a.y;
+		GLfloat cos = delta.x;
+		GLfloat sin = delta.y;
+		GLfloat s = 1.0f/cpvlength(delta);
+
+		GLfloat matrix[] = [
+				 cos,    sin, 0.0f, 0.0f,
+			-sin*s,  cos*s, 0.0f, 0.0f,
+				0.0f,   0.0f, 1.0f, 0.0f,
+					 x,      y, 0.0f, 1.0f,
+		];
+		
+		glMultMatrixf(matrix.ptr);
+		glDrawArrays(GL_LINE_STRIP, 0, springVAR_count);
+	} glPopMatrix();
+}
+
+static void
+drawConstraint(cpConstraint *constraint)
+{
+	cpBody *body_a = constraint.a;
+	cpBody *body_b = constraint.b;
+
+	const cpConstraintClass *klass = constraint.klass;
+	if(klass == cpPinJointGetClass()){
+		cpPinJoint *joint = cast(cpPinJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
+		cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+
+		glPointSize(5.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpSlideJointGetClass()){
+		cpSlideJoint *joint = cast(cpSlideJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
+		cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+	
+		glPointSize(5.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpPivotJointGetClass()){
+		cpPivotJoint *joint = cast(cpPivotJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
+		cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+
+		glPointSize(10.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpGrooveJointGetClass()){
+		cpGrooveJoint *joint = cast(cpGrooveJoint *)constraint;
+	
+		cpVect a = cpvadd(body_a.p, cpvrotate(joint.grv_a, body_a.rot));
+		cpVect b = cpvadd(body_a.p, cpvrotate(joint.grv_b, body_a.rot));
+		cpVect c = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
+	
+		glPointSize(5.0f);
+		glBegin(GL_POINTS); {
+			glVertex2f(c.x, c.y);
+		} glEnd();
+		
+		glBegin(GL_LINES); {
+			glVertex2f(a.x, a.y);
+			glVertex2f(b.x, b.y);
+		} glEnd();
+	} else if(klass == cpDampedSpringGetClass()){
+		drawSpring(cast(cpDampedSpring *)constraint, body_a, body_b);
+	} else {
+//		printf("Cannot draw constraint\n");
+	}
+}
+
+static void
+drawBB(cpShape *shape, void *unused)
+{
+	glBegin(GL_LINE_LOOP); {
+		glVertex2f(shape.bb.l, shape.bb.b);
+		glVertex2f(shape.bb.l, shape.bb.t);
+		glVertex2f(shape.bb.r, shape.bb.t);
+		glVertex2f(shape.bb.r, shape.bb.b);
+	} glEnd();
+}
+
+// copied from cpSpaceHash.c
+static cpHashValue
+hash_func(cpHashValue x, cpHashValue y, cpHashValue n)
+{
+	return cast(cpHashValue)((x*1640531513uL ^ y*2654435789uL) % n);
+}
+
+static void
+drawSpatialHash(cpSpaceHash *hash)
+{
+	cpBB bb = cpBBNew(-320, -240, 320, 240);
+	
+	cpFloat dim = hash.celldim;
+	int n = hash.numcells;
+	
+	int l = cast(int)floorf(bb.l/dim);
+	int r = cast(int)floorf(bb.r/dim);
+	int b = cast(int)floorf(bb.b/dim);
+	int t = cast(int)floorf(bb.t/dim);
+	
+	for(int i=l; i<=r; i++){
+		for(int j=b; j<=t; j++){
+			int cell_count = 0;
+			
+			int index = hash_func(i,j,n);
+			for(cpSpaceHashBin *bin = hash.table[index]; bin; bin = bin.next)
+				cell_count++;
+			
+			GLfloat v = 1.0f - cast(GLfloat)cell_count/10.0f;
+			glColor3f(v,v,v);
+			glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim);
+		}
+	}
+}
+
+void
+DrawSpace(cpSpace *space, const drawSpaceOptions *options)
+{
+	if(options.drawHash){
+		glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
+		drawSpatialHash(space.activeShapes);
+		glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
+		drawSpatialHash(space.staticShapes);
+		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+	}
+	
+	glLineWidth(options.lineThickness);
+	if(options.drawShapes){
+		cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&drawObject, space);
+		cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&drawObject, space);
+	}
+	
+	glLineWidth(1.0f);
+	if(options.drawBBs){
+		glColor3f(0.3f, 0.5f, 0.3f);
+		cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&drawBB, null);
+		cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&drawBB, null);
+	}
+
+	cpArray *constraints = space.constraints;
+
+	glColor3f(0.5f, 1.0f, 0.5f);
+	for(int i=0, count = constraints.num; i<count; i++){
+		drawConstraint(cast(cpConstraint *)constraints.arr[i]);
+	}
+	
+	if(options.bodyPointSize){
+		glPointSize(options.bodyPointSize);
+		
+		glBegin(GL_POINTS); {
+			glColor3fv(LINE_COLOR.ptr);
+			cpArray *bodies = space.bodies;
+			for(int i=0, count = bodies.num; i<count; i++){
+				cpBody *_body = cast(cpBody *)bodies.arr[i];
+				glVertex2f(_body.p.x, _body.p.y);
+			}
+			
+//			glColor3f(0.5f, 0.5f, 0.5f);
+//			cpArray *components = space.components;
+//			for(int i=0; i<components.num; i++){
+//				cpBody *root = components.arr[i];
+//				cpBody *body = root, *next;
+//				do {
+//					next = body.node.next;
+//					glVertex2f(body.p.x, body.p.y);
+//				} while((body = next) != root);
+//			}
+		} glEnd();
+	}
+
+	if(options.collisionPointSize){
+		glPointSize(options.collisionPointSize);
+		glBegin(GL_POINTS); {
+			cpArray *arbiters = space.arbiters;
+			for(int i=0; i<arbiters.num; i++){
+				cpArbiter *arb = cast(cpArbiter*)arbiters.arr[i];
+				
+				glColor3fv(COLLISION_COLOR.ptr);
+				foreach(j; 0..arb.numContacts){
+					cpVect v = arb.contacts[j].p;
+					glVertex2f(v.x, v.y);
+				}
+			}
+		} glEnd();
+	}
+}