16
|
1 /* Copyright (c) 2007 Scott Lembcke
|
|
2 *
|
|
3 * Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4 * of this software and associated documentation files (the "Software"), to deal
|
|
5 * in the Software without restriction, including without limitation the rights
|
|
6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7 * copies of the Software, and to permit persons to whom the Software is
|
|
8 * furnished to do so, subject to the following conditions:
|
|
9 *
|
|
10 * The above copyright notice and this permission notice shall be included in
|
|
11 * all copies or substantial portions of the Software.
|
|
12 *
|
|
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19 * SOFTWARE.
|
|
20 */
|
|
21 module drawSpace;
|
|
22
|
|
23 import derelict.opengl.gl;
|
|
24
|
|
25 import chipmunkd.chipmunk;
|
|
26
|
|
27 import std.math:PI;
|
|
28 import std.stdio;
|
|
29
|
|
30 struct drawSpaceOptions {
|
|
31 int drawHash;
|
|
32 int drawBBs;
|
|
33 int drawShapes;
|
|
34 float collisionPointSize;
|
|
35 float bodyPointSize;
|
|
36 float lineThickness;
|
|
37 }
|
|
38
|
|
39 /*
|
|
40 IMPORTANT - READ ME!
|
|
41
|
|
42 This file sets up a simple interface that the individual demos can use to get
|
|
43 a Chipmunk space running and draw what's in it. In order to keep the Chipmunk
|
|
44 examples clean and simple, they contain no graphics code. All drawing is done
|
|
45 by accessing the Chipmunk structures at a very low level. It is NOT
|
|
46 recommended to write a game or application this way as it does not scale
|
|
47 beyond simple shape drawing and is very dependent on implementation details
|
|
48 about Chipmunk which may change with little to no warning.
|
|
49 */
|
|
50
|
|
51 enum float[3] LINE_COLOR = [0,0,0];
|
|
52 enum float[3] COLLISION_COLOR = [1,0,0];
|
|
53 enum float[3] BODY_COLOR = [0,0,1];
|
|
54
|
|
55 static void
|
|
56 glColor_from_pointer(void *ptr)
|
|
57 {
|
|
58 ulong val = cast(long)ptr;
|
|
59
|
|
60 // hash the pointer up nicely
|
|
61 val = (val+0x7ed55d16) + (val<<12);
|
|
62 val = (val^0xc761c23c) ^ (val>>19);
|
|
63 val = (val+0x165667b1) + (val<<5);
|
|
64 val = (val+0xd3a2646c) ^ (val<<9);
|
|
65 val = (val+0xfd7046c5) + (val<<3);
|
|
66 val = (val^0xb55a4f09) ^ (val>>16);
|
|
67
|
|
68 // GLfloat v = (GLfloat)val/(GLfloat)ULONG_MAX;
|
|
69 // v = 0.95f - v*0.15f;
|
|
70 //
|
|
71 // glColor3f(v, v, v);
|
|
72
|
|
73 GLubyte r = (val>>0) & 0xFF;
|
|
74 GLubyte g = (val>>8) & 0xFF;
|
|
75 GLubyte b = (val>>16) & 0xFF;
|
|
76
|
|
77 GLubyte max = r>g ? (r>b ? r : b) : (g>b ? g : b);
|
|
78
|
|
79 const int mult = 127;
|
|
80 const int add = 63;
|
|
81 r = cast(ubyte)((r*mult)/max + add);
|
|
82 g = cast(ubyte)((g*mult)/max + add);
|
|
83 b = cast(ubyte)((b*mult)/max + add);
|
|
84
|
|
85 glColor3ub(r, g, b);
|
|
86 }
|
|
87
|
|
88 static void
|
|
89 glColor_for_shape(cpShape *shape, cpSpace *space)
|
|
90 {
|
|
91 cpBody *_body = shape._body;
|
|
92 if(_body){
|
|
93 if(_body.node.next){
|
|
94 GLfloat v = 0.25f;
|
|
95 glColor3f(v,v,v);
|
|
96 return;
|
|
97 } else if(_body.node.idleTime > space.sleepTimeThreshold) {
|
|
98 GLfloat v = 0.9f;
|
|
99 glColor3f(v,v,v);
|
|
100 return;
|
|
101 }
|
|
102 }
|
|
103
|
|
104 glColor_from_pointer(shape);
|
|
105 }
|
|
106
|
|
107 enum GLfloat[] circleVAR = [
|
|
108 0.0000f, 1.0000f,
|
|
109 0.2588f, 0.9659f,
|
|
110 0.5000f, 0.8660f,
|
|
111 0.7071f, 0.7071f,
|
|
112 0.8660f, 0.5000f,
|
|
113 0.9659f, 0.2588f,
|
|
114 1.0000f, 0.0000f,
|
|
115 0.9659f, -0.2588f,
|
|
116 0.8660f, -0.5000f,
|
|
117 0.7071f, -0.7071f,
|
|
118 0.5000f, -0.8660f,
|
|
119 0.2588f, -0.9659f,
|
|
120 0.0000f, -1.0000f,
|
|
121 -0.2588f, -0.9659f,
|
|
122 -0.5000f, -0.8660f,
|
|
123 -0.7071f, -0.7071f,
|
|
124 -0.8660f, -0.5000f,
|
|
125 -0.9659f, -0.2588f,
|
|
126 -1.0000f, -0.0000f,
|
|
127 -0.9659f, 0.2588f,
|
|
128 -0.8660f, 0.5000f,
|
|
129 -0.7071f, 0.7071f,
|
|
130 -0.5000f, 0.8660f,
|
|
131 -0.2588f, 0.9659f,
|
|
132 0.0000f, 1.0000f,
|
|
133 0.0f, 0.0f, // For an extra line to see the rotation.
|
|
134 ];
|
|
135 enum int circleVAR_count = circleVAR.length / 2;
|
|
136
|
|
137 static void
|
|
138 drawCircleShape(cpBody *_body, cpCircleShape *circle, cpSpace *space)
|
|
139 {
|
|
140 glVertexPointer(2, GL_FLOAT, 0, circleVAR.ptr);
|
|
141
|
|
142 glPushMatrix(); {
|
|
143 cpVect center = circle.tc;
|
|
144 glTranslatef(center.x, center.y, 0.0f);
|
|
145 glRotatef(_body.a*180.0f/PI, 0.0f, 0.0f, 1.0f);
|
|
146 glScalef(circle.r, circle.r, 1.0f);
|
|
147
|
|
148 if(!circle.shape.sensor){
|
|
149 glColor_for_shape(cast(cpShape *)circle, space);
|
|
150 glDrawArrays(GL_TRIANGLE_FAN, 0, circleVAR_count - 1);
|
|
151 }
|
|
152
|
|
153 glColor3fv(LINE_COLOR.ptr);
|
|
154 glDrawArrays(GL_LINE_STRIP, 0, circleVAR_count);
|
|
155 } glPopMatrix();
|
|
156 }
|
|
157
|
|
158 enum GLfloat[] pillVAR = [
|
|
159 0.0000f, 1.0000f, 1.0f,
|
|
160 0.2588f, 0.9659f, 1.0f,
|
|
161 0.5000f, 0.8660f, 1.0f,
|
|
162 0.7071f, 0.7071f, 1.0f,
|
|
163 0.8660f, 0.5000f, 1.0f,
|
|
164 0.9659f, 0.2588f, 1.0f,
|
|
165 1.0000f, 0.0000f, 1.0f,
|
|
166 0.9659f, -0.2588f, 1.0f,
|
|
167 0.8660f, -0.5000f, 1.0f,
|
|
168 0.7071f, -0.7071f, 1.0f,
|
|
169 0.5000f, -0.8660f, 1.0f,
|
|
170 0.2588f, -0.9659f, 1.0f,
|
|
171 0.0000f, -1.0000f, 1.0f,
|
|
172
|
|
173 0.0000f, -1.0000f, 0.0f,
|
|
174 -0.2588f, -0.9659f, 0.0f,
|
|
175 -0.5000f, -0.8660f, 0.0f,
|
|
176 -0.7071f, -0.7071f, 0.0f,
|
|
177 -0.8660f, -0.5000f, 0.0f,
|
|
178 -0.9659f, -0.2588f, 0.0f,
|
|
179 -1.0000f, -0.0000f, 0.0f,
|
|
180 -0.9659f, 0.2588f, 0.0f,
|
|
181 -0.8660f, 0.5000f, 0.0f,
|
|
182 -0.7071f, 0.7071f, 0.0f,
|
|
183 -0.5000f, 0.8660f, 0.0f,
|
|
184 -0.2588f, 0.9659f, 0.0f,
|
|
185 0.0000f, 1.0000f, 0.0f,
|
|
186 ];
|
|
187 enum int pillVAR_count = pillVAR.length/3;
|
|
188
|
|
189 static void
|
|
190 drawSegmentShape(cpBody *_body, cpSegmentShape *seg, cpSpace *space)
|
|
191 {
|
|
192 cpVect a = seg.ta;
|
|
193 cpVect b = seg.tb;
|
|
194
|
|
195 if(seg.r){
|
|
196 glVertexPointer(3, GL_FLOAT, 0, pillVAR.ptr);
|
|
197 glPushMatrix(); {
|
|
198 cpVect d = cpvsub(b, a);
|
|
199 cpVect r = cpvmult(d, seg.r/cpvlength(d));
|
|
200
|
|
201 GLfloat matrix[] = [
|
|
202 r.x, r.y, 0.0f, 0.0f,
|
|
203 -r.y, r.x, 0.0f, 0.0f,
|
|
204 d.x, d.y, 0.0f, 0.0f,
|
|
205 a.x, a.y, 0.0f, 1.0f,
|
|
206 ];
|
|
207 glMultMatrixf(matrix.ptr);
|
|
208
|
|
209 if(!seg.shape.sensor){
|
|
210 glColor_for_shape(cast(cpShape *)seg, space);
|
|
211 glDrawArrays(GL_TRIANGLE_FAN, 0, pillVAR_count);
|
|
212 }
|
|
213
|
|
214 glColor3fv(LINE_COLOR.ptr);
|
|
215 glDrawArrays(GL_LINE_LOOP, 0, pillVAR_count);
|
|
216 } glPopMatrix();
|
|
217 } else {
|
|
218 glColor3fv(LINE_COLOR.ptr);
|
|
219 glBegin(GL_LINES); {
|
|
220 glVertex2f(a.x, a.y);
|
|
221 glVertex2f(b.x, b.y);
|
|
222 } glEnd();
|
|
223 }
|
|
224 }
|
|
225
|
|
226 static void
|
|
227 drawPolyShape(cpBody *_body, cpPolyShape *poly, cpSpace *space)
|
|
228 {
|
|
229 int count = poly.numVerts;
|
|
230 version(CP_USE_DOUBLES)
|
|
231 {
|
|
232 glVertexPointer(2, GL_DOUBLE, 0, poly.tVerts);
|
|
233 }
|
|
234 else
|
|
235 {
|
|
236 glVertexPointer(2, GL_FLOAT, 0, poly.tVerts);
|
|
237 }
|
|
238
|
|
239 if(!poly.shape.sensor){
|
|
240 glColor_for_shape(cast(cpShape *)poly, space);
|
|
241 glDrawArrays(GL_TRIANGLE_FAN, 0, count);
|
|
242 }
|
|
243
|
|
244 glColor3fv(LINE_COLOR.ptr);
|
|
245 glDrawArrays(GL_LINE_LOOP, 0, count);
|
|
246 }
|
|
247
|
|
248 static void
|
|
249 drawObject(cpShape *shape, cpSpace *space)
|
|
250 {
|
|
251 cpBody *_body = shape._body;
|
|
252
|
|
253 switch(shape.klass.type){
|
|
254 case cpShapeType.CP_CIRCLE_SHAPE:
|
|
255 drawCircleShape(_body, cast(cpCircleShape *)shape, space);
|
|
256 break;
|
|
257 case cpShapeType.CP_SEGMENT_SHAPE:
|
|
258 drawSegmentShape(_body, cast(cpSegmentShape *)shape, space);
|
|
259 break;
|
|
260 case cpShapeType.CP_POLY_SHAPE:
|
|
261 drawPolyShape(_body, cast(cpPolyShape *)shape, space);
|
|
262 break;
|
|
263 default:
|
|
264 writefln("Bad enumeration in drawObject().");
|
|
265 }
|
|
266 }
|
|
267
|
|
268 enum GLfloat[] springVAR = [
|
|
269 0.00f, 0.0f,
|
|
270 0.20f, 0.0f,
|
|
271 0.25f, 3.0f,
|
|
272 0.30f,-6.0f,
|
|
273 0.35f, 6.0f,
|
|
274 0.40f,-6.0f,
|
|
275 0.45f, 6.0f,
|
|
276 0.50f,-6.0f,
|
|
277 0.55f, 6.0f,
|
|
278 0.60f,-6.0f,
|
|
279 0.65f, 6.0f,
|
|
280 0.70f,-3.0f,
|
|
281 0.75f, 6.0f,
|
|
282 0.80f, 0.0f,
|
|
283 1.00f, 0.0f,
|
|
284 ];
|
|
285 enum int springVAR_count = springVAR.length / 2;
|
|
286
|
|
287 static void
|
|
288 drawSpring(cpDampedSpring *spring, cpBody *body_a, cpBody *body_b)
|
|
289 {
|
|
290 cpVect a = cpvadd(body_a.p, cpvrotate(spring.anchr1, body_a.rot));
|
|
291 cpVect b = cpvadd(body_b.p, cpvrotate(spring.anchr2, body_b.rot));
|
|
292
|
|
293 glPointSize(5.0f);
|
|
294 glBegin(GL_POINTS); {
|
|
295 glVertex2f(a.x, a.y);
|
|
296 glVertex2f(b.x, b.y);
|
|
297 } glEnd();
|
|
298
|
|
299 cpVect delta = cpvsub(b, a);
|
|
300
|
|
301 glVertexPointer(2, GL_FLOAT, 0, springVAR.ptr);
|
|
302 glPushMatrix(); {
|
|
303 GLfloat x = a.x;
|
|
304 GLfloat y = a.y;
|
|
305 GLfloat cos = delta.x;
|
|
306 GLfloat sin = delta.y;
|
|
307 GLfloat s = 1.0f/cpvlength(delta);
|
|
308
|
|
309 GLfloat matrix[] = [
|
|
310 cos, sin, 0.0f, 0.0f,
|
|
311 -sin*s, cos*s, 0.0f, 0.0f,
|
|
312 0.0f, 0.0f, 1.0f, 0.0f,
|
|
313 x, y, 0.0f, 1.0f,
|
|
314 ];
|
|
315
|
|
316 glMultMatrixf(matrix.ptr);
|
|
317 glDrawArrays(GL_LINE_STRIP, 0, springVAR_count);
|
|
318 } glPopMatrix();
|
|
319 }
|
|
320
|
|
321 static void
|
|
322 drawConstraint(cpConstraint *constraint)
|
|
323 {
|
|
324 cpBody *body_a = constraint.a;
|
|
325 cpBody *body_b = constraint.b;
|
|
326
|
|
327 const cpConstraintClass *klass = constraint.klass;
|
|
328 if(klass == cpPinJointGetClass()){
|
|
329 cpPinJoint *joint = cast(cpPinJoint *)constraint;
|
|
330
|
|
331 cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
|
|
332 cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
|
|
333
|
|
334 glPointSize(5.0f);
|
|
335 glBegin(GL_POINTS); {
|
|
336 glVertex2f(a.x, a.y);
|
|
337 glVertex2f(b.x, b.y);
|
|
338 } glEnd();
|
|
339
|
|
340 glBegin(GL_LINES); {
|
|
341 glVertex2f(a.x, a.y);
|
|
342 glVertex2f(b.x, b.y);
|
|
343 } glEnd();
|
|
344 } else if(klass == cpSlideJointGetClass()){
|
|
345 cpSlideJoint *joint = cast(cpSlideJoint *)constraint;
|
|
346
|
|
347 cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
|
|
348 cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
|
|
349
|
|
350 glPointSize(5.0f);
|
|
351 glBegin(GL_POINTS); {
|
|
352 glVertex2f(a.x, a.y);
|
|
353 glVertex2f(b.x, b.y);
|
|
354 } glEnd();
|
|
355
|
|
356 glBegin(GL_LINES); {
|
|
357 glVertex2f(a.x, a.y);
|
|
358 glVertex2f(b.x, b.y);
|
|
359 } glEnd();
|
|
360 } else if(klass == cpPivotJointGetClass()){
|
|
361 cpPivotJoint *joint = cast(cpPivotJoint *)constraint;
|
|
362
|
|
363 cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
|
|
364 cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
|
|
365
|
|
366 glPointSize(10.0f);
|
|
367 glBegin(GL_POINTS); {
|
|
368 glVertex2f(a.x, a.y);
|
|
369 glVertex2f(b.x, b.y);
|
|
370 } glEnd();
|
|
371 } else if(klass == cpGrooveJointGetClass()){
|
|
372 cpGrooveJoint *joint = cast(cpGrooveJoint *)constraint;
|
|
373
|
|
374 cpVect a = cpvadd(body_a.p, cpvrotate(joint.grv_a, body_a.rot));
|
|
375 cpVect b = cpvadd(body_a.p, cpvrotate(joint.grv_b, body_a.rot));
|
|
376 cpVect c = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
|
|
377
|
|
378 glPointSize(5.0f);
|
|
379 glBegin(GL_POINTS); {
|
|
380 glVertex2f(c.x, c.y);
|
|
381 } glEnd();
|
|
382
|
|
383 glBegin(GL_LINES); {
|
|
384 glVertex2f(a.x, a.y);
|
|
385 glVertex2f(b.x, b.y);
|
|
386 } glEnd();
|
|
387 } else if(klass == cpDampedSpringGetClass()){
|
|
388 drawSpring(cast(cpDampedSpring *)constraint, body_a, body_b);
|
|
389 } else {
|
|
390 // printf("Cannot draw constraint\n");
|
|
391 }
|
|
392 }
|
|
393
|
|
394 static void
|
|
395 drawBB(cpShape *shape, void *unused)
|
|
396 {
|
|
397 glBegin(GL_LINE_LOOP); {
|
|
398 glVertex2f(shape.bb.l, shape.bb.b);
|
|
399 glVertex2f(shape.bb.l, shape.bb.t);
|
|
400 glVertex2f(shape.bb.r, shape.bb.t);
|
|
401 glVertex2f(shape.bb.r, shape.bb.b);
|
|
402 } glEnd();
|
|
403 }
|
|
404
|
|
405 // copied from cpSpaceHash.c
|
|
406 static cpHashValue
|
|
407 hash_func(cpHashValue x, cpHashValue y, cpHashValue n)
|
|
408 {
|
|
409 return cast(cpHashValue)((x*1640531513uL ^ y*2654435789uL) % n);
|
|
410 }
|
|
411
|
|
412 static void
|
|
413 drawSpatialHash(cpSpaceHash *hash)
|
|
414 {
|
|
415 cpBB bb = cpBBNew(-320, -240, 320, 240);
|
|
416
|
|
417 cpFloat dim = hash.celldim;
|
|
418 int n = hash.numcells;
|
|
419
|
|
420 int l = cast(int)floorf(bb.l/dim);
|
|
421 int r = cast(int)floorf(bb.r/dim);
|
|
422 int b = cast(int)floorf(bb.b/dim);
|
|
423 int t = cast(int)floorf(bb.t/dim);
|
|
424
|
|
425 for(int i=l; i<=r; i++){
|
|
426 for(int j=b; j<=t; j++){
|
|
427 int cell_count = 0;
|
|
428
|
|
429 int index = hash_func(i,j,n);
|
|
430 for(cpSpaceHashBin *bin = hash.table[index]; bin; bin = bin.next)
|
|
431 cell_count++;
|
|
432
|
|
433 GLfloat v = 1.0f - cast(GLfloat)cell_count/10.0f;
|
|
434 glColor3f(v,v,v);
|
|
435 glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim);
|
|
436 }
|
|
437 }
|
|
438 }
|
|
439
|
|
440 void
|
|
441 DrawSpace(cpSpace *space, const drawSpaceOptions *options)
|
|
442 {
|
|
443 if(options.drawHash){
|
|
444 glColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_TRUE);
|
|
445 drawSpatialHash(space.activeShapes);
|
|
446 glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE);
|
|
447 drawSpatialHash(space.staticShapes);
|
|
448 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
|
449 }
|
|
450
|
|
451 glLineWidth(options.lineThickness);
|
|
452 if(options.drawShapes){
|
|
453 cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&drawObject, space);
|
|
454 cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&drawObject, space);
|
|
455 }
|
|
456
|
|
457 glLineWidth(1.0f);
|
|
458 if(options.drawBBs){
|
|
459 glColor3f(0.3f, 0.5f, 0.3f);
|
|
460 cpSpaceHashEach(space.activeShapes, cast(cpSpaceHashIterator)&drawBB, null);
|
|
461 cpSpaceHashEach(space.staticShapes, cast(cpSpaceHashIterator)&drawBB, null);
|
|
462 }
|
|
463
|
|
464 cpArray *constraints = space.constraints;
|
|
465
|
|
466 glColor3f(0.5f, 1.0f, 0.5f);
|
|
467 for(int i=0, count = constraints.num; i<count; i++){
|
|
468 drawConstraint(cast(cpConstraint *)constraints.arr[i]);
|
|
469 }
|
|
470
|
|
471 if(options.bodyPointSize){
|
|
472 glPointSize(options.bodyPointSize);
|
|
473
|
|
474 glBegin(GL_POINTS); {
|
|
475 glColor3fv(LINE_COLOR.ptr);
|
|
476 cpArray *bodies = space.bodies;
|
|
477 for(int i=0, count = bodies.num; i<count; i++){
|
|
478 cpBody *_body = cast(cpBody *)bodies.arr[i];
|
|
479 glVertex2f(_body.p.x, _body.p.y);
|
|
480 }
|
|
481
|
|
482 // glColor3f(0.5f, 0.5f, 0.5f);
|
|
483 // cpArray *components = space.components;
|
|
484 // for(int i=0; i<components.num; i++){
|
|
485 // cpBody *root = components.arr[i];
|
|
486 // cpBody *body = root, *next;
|
|
487 // do {
|
|
488 // next = body.node.next;
|
|
489 // glVertex2f(body.p.x, body.p.y);
|
|
490 // } while((body = next) != root);
|
|
491 // }
|
|
492 } glEnd();
|
|
493 }
|
|
494
|
|
495 if(options.collisionPointSize){
|
|
496 glPointSize(options.collisionPointSize);
|
|
497 glBegin(GL_POINTS); {
|
|
498 cpArray *arbiters = space.arbiters;
|
|
499 for(int i=0; i<arbiters.num; i++){
|
|
500 cpArbiter *arb = cast(cpArbiter*)arbiters.arr[i];
|
|
501
|
|
502 glColor3fv(COLLISION_COLOR.ptr);
|
|
503 foreach(j; 0..arb.numContacts){
|
|
504 cpVect v = arb.contacts[j].p;
|
|
505 glVertex2f(v.x, v.y);
|
|
506 }
|
|
507 }
|
|
508 } glEnd();
|
|
509 }
|
|
510 }
|