view trunk/tests/ChipmunkDemos/drawSpace.d @ 31:4604c914f2ab default tip

removed workaround for dmd-bug #5249
author Extrawurst
date Thu, 30 Dec 2010 13:50:40 +0100
parents f897d96cc7c9
children
line wrap: on
line source


// written in the D programming language

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();
	}
}