1 
2 // written in the D programming language
3 
4 module drawSpace;
5 
6 import derelict.opengl.gl;
7 
8 import dchip.all;
9 
10 import std.math:PI;
11 import std.stdio;
12 
13 struct drawSpaceOptions {
14     int drawHash;
15     int drawBBs;
16     int drawShapes;
17     float collisionPointSize;
18     float bodyPointSize;
19     float lineThickness;
20 }
21 
22 /*
23     IMPORTANT - READ ME!
24 
25     This file sets up a simple interface that the individual demos can use to get
26     a Chipmunk space running and draw what's in it. In order to keep the Chipmunk
27     examples clean and simple, they contain no graphics code. All drawing is done
28     by accessing the Chipmunk structures at a very low level. It is NOT
29     recommended to write a game or application this way as it does not scale
30     beyond simple shape drawing and is very dependent on implementation details
31     about Chipmunk which may change with little to no warning.
32 */
33 
34 enum float[3] LINE_COLOR = [0,0,0];
35 
36 static void
37 glColor_from_hash(cpHashValue hash)
38 {
39     ulong val = cast(ulong)hash;
40 
41     // scramble the bits up using Robert Jenkins' 32 bit integer hash function
42     val = (val+0x7ed55d16) + (val<<12);
43     val = (val^0xc761c23c) ^ (val>>19);
44     val = (val+0x165667b1) + (val<<5);
45     val = (val+0xd3a2646c) ^ (val<<9);
46     val = (val+0xfd7046c5) + (val<<3);
47     val = (val^0xb55a4f09) ^ (val>>16);
48 
49     GLubyte r = (val>>0) & 0xFF;
50     GLubyte g = (val>>8) & 0xFF;
51     GLubyte b = (val>>16) & 0xFF;
52 
53     GLubyte max = (r > g ? (r > b ? r : b) : (g > b ? g : b));
54 
55     // saturate and scale the colors
56     enum int mult = 255;
57     enum int add = 0;
58     r = cast(ubyte)((r*mult)/max + add);
59     g = cast(ubyte)((g*mult)/max + add);
60     b = cast(ubyte)((b*mult)/max + add);
61 
62     glColor4ub(r, g, b, 196);
63 }
64 
65 static void
66 glColor_for_shape(cpShape *shape, cpSpace *space)
67 {
68     cpBody *body_ = shape.body_;
69     if(body_){
70         if(cpBodyIsSleeping(body_)){
71             GLfloat v = 0.2f;
72             glColor3f(v,v,v);
73             return;
74         } else if(body_.node.idleTime > space.sleepTimeThreshold) {
75             GLfloat v = 0.66f;
76             glColor3f(v,v,v);
77             return;
78         }
79     }
80 
81     glColor_from_hash(shape.hashid);
82 }
83 
84 enum GLfloat[] circleVAR = [
85      0.0000f,  1.0000f,
86      0.2588f,  0.9659f,
87      0.5000f,  0.8660f,
88      0.7071f,  0.7071f,
89      0.8660f,  0.5000f,
90      0.9659f,  0.2588f,
91      1.0000f,  0.0000f,
92      0.9659f, -0.2588f,
93      0.8660f, -0.5000f,
94      0.7071f, -0.7071f,
95      0.5000f, -0.8660f,
96      0.2588f, -0.9659f,
97      0.0000f, -1.0000f,
98     -0.2588f, -0.9659f,
99     -0.5000f, -0.8660f,
100     -0.7071f, -0.7071f,
101     -0.8660f, -0.5000f,
102     -0.9659f, -0.2588f,
103     -1.0000f, -0.0000f,
104     -0.9659f,  0.2588f,
105     -0.8660f,  0.5000f,
106     -0.7071f,  0.7071f,
107     -0.5000f,  0.8660f,
108     -0.2588f,  0.9659f,
109      0.0000f,  1.0000f,
110      0.0f, 0.0f, // For an extra line to see the rotation.
111 ];
112 enum int circleVAR_count = circleVAR.length / 2;
113 
114 static void
115 drawCircleShape(cpBody *body_, cpCircleShape *circle, cpSpace *space)
116 {
117     glVertexPointer(2, GL_FLOAT, 0, circleVAR.ptr);
118 
119     glPushMatrix(); {
120         cpVect center = circle.tc;
121         glTranslatef(center.x, center.y, 0.0f);
122         glRotatef(body_.a*180.0f/PI, 0.0f, 0.0f, 1.0f);
123         glScalef(circle.r, circle.r, 1.0f);
124 
125         if(!circle.shape.sensor){
126             glColor_for_shape(cast(cpShape *)circle, space);
127             glDrawArrays(GL_TRIANGLE_FAN, 0, circleVAR_count - 1);
128         }
129 
130         glColor3fv(LINE_COLOR.ptr);
131         glDrawArrays(GL_LINE_STRIP, 0, circleVAR_count);
132     } glPopMatrix();
133 }
134 
135 enum GLfloat[] pillVAR = [
136      0.0000f,  1.0000f, 1.0f,
137      0.2588f,  0.9659f, 1.0f,
138      0.5000f,  0.8660f, 1.0f,
139      0.7071f,  0.7071f, 1.0f,
140      0.8660f,  0.5000f, 1.0f,
141      0.9659f,  0.2588f, 1.0f,
142      1.0000f,  0.0000f, 1.0f,
143      0.9659f, -0.2588f, 1.0f,
144      0.8660f, -0.5000f, 1.0f,
145      0.7071f, -0.7071f, 1.0f,
146      0.5000f, -0.8660f, 1.0f,
147      0.2588f, -0.9659f, 1.0f,
148      0.0000f, -1.0000f, 1.0f,
149 
150      0.0000f, -1.0000f, 0.0f,
151     -0.2588f, -0.9659f, 0.0f,
152     -0.5000f, -0.8660f, 0.0f,
153     -0.7071f, -0.7071f, 0.0f,
154     -0.8660f, -0.5000f, 0.0f,
155     -0.9659f, -0.2588f, 0.0f,
156     -1.0000f, -0.0000f, 0.0f,
157     -0.9659f,  0.2588f, 0.0f,
158     -0.8660f,  0.5000f, 0.0f,
159     -0.7071f,  0.7071f, 0.0f,
160     -0.5000f,  0.8660f, 0.0f,
161     -0.2588f,  0.9659f, 0.0f,
162      0.0000f,  1.0000f, 0.0f,
163 ];
164 enum int pillVAR_count = pillVAR.length/3;
165 
166 static void
167 drawSegmentShape(cpBody *body_, cpSegmentShape *seg, cpSpace *space)
168 {
169     cpVect a = seg.ta;
170     cpVect b = seg.tb;
171 
172     if(seg.r){
173         glVertexPointer(3, GL_FLOAT, 0, pillVAR.ptr);
174         glPushMatrix(); {
175             cpVect d = cpvsub(b, a);
176             cpVect r = cpvmult(d, seg.r/cpvlength(d));
177 
178             GLfloat matrix[] = [
179                  r.x, r.y, 0.0f, 0.0f,
180                 -r.y, r.x, 0.0f, 0.0f,
181                  d.x, d.y, 0.0f, 0.0f,
182                  a.x, a.y, 0.0f, 1.0f,
183             ];
184             glMultMatrixf(matrix.ptr);
185 
186             if(!seg.shape.sensor){
187                 glColor_for_shape(cast(cpShape *)seg, space);
188                 glDrawArrays(GL_TRIANGLE_FAN, 0, pillVAR_count);
189             }
190 
191             glColor3fv(LINE_COLOR.ptr);
192             glDrawArrays(GL_LINE_LOOP, 0, pillVAR_count);
193         } glPopMatrix();
194     } else {
195         glColor3fv(LINE_COLOR.ptr);
196         glBegin(GL_LINES); {
197             glVertex2f(a.x, a.y);
198             glVertex2f(b.x, b.y);
199         } glEnd();
200     }
201 }
202 
203 static void
204 drawPolyShape(cpBody *body_, cpPolyShape *poly, cpSpace *space)
205 {
206     int count = poly.numVerts;
207 version(CP_USE_DOUBLES)
208 {
209     glVertexPointer(2, GL_DOUBLE, 0, poly.tVerts);
210 }
211 else
212 {
213     glVertexPointer(2, GL_FLOAT, 0, poly.tVerts);
214 }
215 
216     if(!poly.shape.sensor){
217         glColor_for_shape(cast(cpShape *)poly, space);
218         glDrawArrays(GL_TRIANGLE_FAN, 0, count);
219     }
220 
221     glColor3fv(LINE_COLOR.ptr);
222     glDrawArrays(GL_LINE_LOOP, 0, count);
223 }
224 
225 static void
226 drawObject(cpShape *shape, cpSpace *space)
227 {
228     cpBody *body_ = shape.body_;
229 
230     switch(shape.klass.type){
231         case cpShapeType.CP_CIRCLE_SHAPE:
232             drawCircleShape(body_, cast(cpCircleShape *)shape, space);
233             break;
234         case cpShapeType.CP_SEGMENT_SHAPE:
235             drawSegmentShape(body_, cast(cpSegmentShape *)shape, space);
236             break;
237         case cpShapeType.CP_POLY_SHAPE:
238             drawPolyShape(body_, cast(cpPolyShape *)shape, space);
239             break;
240         default:
241             writefln("Bad enumeration in drawObject().");
242     }
243 }
244 
245 enum GLfloat[] springVAR = [
246     0.00f, 0.0f,
247     0.20f, 0.0f,
248     0.25f, 3.0f,
249     0.30f,-6.0f,
250     0.35f, 6.0f,
251     0.40f,-6.0f,
252     0.45f, 6.0f,
253     0.50f,-6.0f,
254     0.55f, 6.0f,
255     0.60f,-6.0f,
256     0.65f, 6.0f,
257     0.70f,-3.0f,
258     0.75f, 6.0f,
259     0.80f, 0.0f,
260     1.00f, 0.0f,
261 ];
262 enum int springVAR_count = springVAR.length / 2;
263 
264 static void
265 drawSpring(cpDampedSpring *spring, cpBody *body_a, cpBody *body_b)
266 {
267     cpVect a = cpvadd(body_a.p, cpvrotate(spring.anchr1, body_a.rot));
268     cpVect b = cpvadd(body_b.p, cpvrotate(spring.anchr2, body_b.rot));
269 
270     glPointSize(5.0f);
271     glBegin(GL_POINTS); {
272         glVertex2f(a.x, a.y);
273         glVertex2f(b.x, b.y);
274     } glEnd();
275 
276     cpVect delta = cpvsub(b, a);
277 
278     glVertexPointer(2, GL_FLOAT, 0, springVAR.ptr);
279     glPushMatrix(); {
280         GLfloat x = a.x;
281         GLfloat y = a.y;
282         GLfloat cos = delta.x;
283         GLfloat sin = delta.y;
284         GLfloat s = 1.0f/cpvlength(delta);
285 
286         GLfloat matrix[] = [
287                  cos,    sin, 0.0f, 0.0f,
288             -sin*s,  cos*s, 0.0f, 0.0f,
289                 0.0f,   0.0f, 1.0f, 0.0f,
290                      x,      y, 0.0f, 1.0f,
291         ];
292 
293         glMultMatrixf(matrix.ptr);
294         glDrawArrays(GL_LINE_STRIP, 0, springVAR_count);
295     } glPopMatrix();
296 }
297 
298 static void
299 drawConstraint(cpConstraint *constraint)
300 {
301     cpBody *body_a = constraint.a;
302     cpBody *body_b = constraint.b;
303 
304     const cpConstraintClass *klass = constraint.klass;
305     if(klass == cpPinJointGetClass()){
306         cpPinJoint *joint = cast(cpPinJoint *)constraint;
307 
308         cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
309         cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
310 
311         glPointSize(5.0f);
312         glBegin(GL_POINTS); {
313             glVertex2f(a.x, a.y);
314             glVertex2f(b.x, b.y);
315         } glEnd();
316 
317         glBegin(GL_LINES); {
318             glVertex2f(a.x, a.y);
319             glVertex2f(b.x, b.y);
320         } glEnd();
321     } else if(klass == cpSlideJointGetClass()){
322         cpSlideJoint *joint = cast(cpSlideJoint *)constraint;
323 
324         cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
325         cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
326 
327         glPointSize(5.0f);
328         glBegin(GL_POINTS); {
329             glVertex2f(a.x, a.y);
330             glVertex2f(b.x, b.y);
331         } glEnd();
332 
333         glBegin(GL_LINES); {
334             glVertex2f(a.x, a.y);
335             glVertex2f(b.x, b.y);
336         } glEnd();
337     } else if(klass == cpPivotJointGetClass()){
338         cpPivotJoint *joint = cast(cpPivotJoint *)constraint;
339 
340         cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
341         cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
342 
343         glPointSize(10.0f);
344         glBegin(GL_POINTS); {
345             glVertex2f(a.x, a.y);
346             glVertex2f(b.x, b.y);
347         } glEnd();
348     } else if(klass == cpGrooveJointGetClass()){
349         cpGrooveJoint *joint = cast(cpGrooveJoint *)constraint;
350 
351         cpVect a = cpvadd(body_a.p, cpvrotate(joint.grv_a, body_a.rot));
352         cpVect b = cpvadd(body_a.p, cpvrotate(joint.grv_b, body_a.rot));
353         cpVect c = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
354 
355         glPointSize(5.0f);
356         glBegin(GL_POINTS); {
357             glVertex2f(c.x, c.y);
358         } glEnd();
359 
360         glBegin(GL_LINES); {
361             glVertex2f(a.x, a.y);
362             glVertex2f(b.x, b.y);
363         } glEnd();
364     } else if(klass == cpDampedSpringGetClass()){
365         drawSpring(cast(cpDampedSpring *)constraint, body_a, body_b);
366     } else {
367 //		printf("Cannot draw constraint\n");
368     }
369 }
370 
371 static void
372 drawBB(cpShape *shape, void *unused)
373 {
374     glBegin(GL_LINE_LOOP); {
375         glVertex2f(shape.bb.l, shape.bb.b);
376         glVertex2f(shape.bb.l, shape.bb.t);
377         glVertex2f(shape.bb.r, shape.bb.t);
378         glVertex2f(shape.bb.r, shape.bb.b);
379     } glEnd();
380 }
381 
382 void
383 DrawSpace(cpSpace *space, const drawSpaceOptions *options)
384 {
385     glEnable(GL_BLEND);
386     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
387 
388     if(options.drawHash){
389 //		glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
390 //		drawSpatialHash(space->activeShapes);
391 //		glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
392 //		drawSpatialHash(space->staticShapes);
393 //		glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
394 
395 //		glColor3f(0.5, 0.5, 0.5);
396 //		cpBBTreeRenderDebug(space->staticShapes);
397 //		glColor3f(0, 1, 0);
398 //		cpBBTreeRenderDebug(space->activeShapes);
399     }
400 
401     glLineWidth(options.lineThickness);
402     if(options.drawShapes){
403         cpSpatialIndexEach(space.activeShapes, cast(cpSpatialIndexIteratorFunc)&drawObject, space);
404         cpSpatialIndexEach(space.staticShapes, cast(cpSpatialIndexIteratorFunc)&drawObject, space);
405     }
406 
407     glLineWidth(1.0f);
408     if(options.drawBBs){
409         glColor3f(0.3f, 0.5f, 0.3f);
410         cpSpatialIndexEach(space.activeShapes, cast(cpSpatialIndexIteratorFunc)&drawBB, null);
411         cpSpatialIndexEach(space.staticShapes, cast(cpSpatialIndexIteratorFunc)&drawBB, null);
412     }
413 
414     cpArray *constraints = space.constraints;
415 
416     glColor3f(0.0f, 0.0f, 0.5f);
417     for(int i=0, count = constraints.num; i<count; i++){
418         drawConstraint(cast(cpConstraint *)constraints.arr[i]);
419     }
420 
421     if(options.bodyPointSize){
422         glPointSize(options.bodyPointSize);
423 
424         glBegin(GL_POINTS); {
425             glColor3fv(LINE_COLOR.ptr);
426             cpArray *bodies = space.bodies;
427             for(int i=0, count = bodies.num; i<count; i++){
428                 cpBody *body_ = cast(cpBody *)bodies.arr[i];
429                 glVertex2f(body_.p.x, body_.p.y);
430             }
431 
432 //			glColor3f(0.5f, 0.5f, 0.5f);
433 //			cpArray *components = space.components;
434 //			for(int i=0; i<components.num; i++){
435 //				cpBody *root = components.arr[i];
436 //				cpBody *body = root, *next;
437 //				do {
438 //					next = body.node.next;
439 //					glVertex2f(body.p.x, body.p.y);
440 //				} while((body = next) != root);
441 //			}
442         } glEnd();
443     }
444 
445     if(options.collisionPointSize){
446         cpArray* arbiters = space.arbiters;
447 
448         glColor3f(0.0f, 1.0f, 0.0f);
449         glPointSize(2.0f*options.collisionPointSize);
450 
451         glBegin(GL_POINTS); {
452             for(int i=0; i<arbiters.num; i++){
453                 cpArbiter *arb = cast(cpArbiter*)arbiters.arr[i];
454                 if(arb.state != cpArbiterState.cpArbiterStateFirstColl) continue;
455 
456                 for(int j=0; j<arb.numContacts; j++){
457                     cpVect v = arb.contacts[j].p;
458                     glVertex2f(v.x, v.y);
459                 }
460             }
461         } glEnd();
462 
463         glColor3f(1.0f, 0.0f, 0.0f);
464         glPointSize(options.collisionPointSize);
465 
466         glBegin(GL_POINTS); {
467             for(int i=0; i<arbiters.num; i++){
468                 cpArbiter *arb = cast(cpArbiter*)arbiters.arr[i];
469                 if(arb.state == cpArbiterState.cpArbiterStateFirstColl) continue;
470 
471                 for(int j=0; j<arb.numContacts; j++){
472                     cpVect v = arb.contacts[j].p;
473                     glVertex2f(v.x, v.y);
474                 }
475             }
476         } glEnd();
477     }
478 }