1 /*
2  * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 module demo.ChipmunkDebugDraw;
23 
24 /**
25     IMPORTANT - READ ME!
26 
27     This file sets up a simple interface that the individual demos can use to get
28     a Chipmunk space running and draw what's in it. In order to keep the Chipmunk
29     examples clean and simple, they contain no graphics code. All drawing is done
30     by accessing the Chipmunk structures at a very low level. It is NOT
31     recommended to write a game or application this way as it does not scale
32     beyond simple shape drawing and is very dependent on implementation details
33     about Chipmunk which may change with little to no warning.
34 */
35 
36 import core.stdc.config;
37 import core.stdc.stdlib;
38 import core.stdc..string;
39 
40 import glad.gl.all;
41 
42 import demo.dchip;
43 
44 import demo.ChipmunkDemoShaderSupport;
45 import demo.types;
46 
47 const __gshared Color LINE_COLOR = { 200.0f / 255.0f, 210.0f / 255.0f, 230.0f / 255.0f, 1.0f };
48 const __gshared Color CONSTRAINT_COLOR = { 0.0f, 0.75f, 0.0f, 1.0f };
49 const __gshared float SHAPE_ALPHA = 1.0f;
50 
51 __gshared float ChipmunkDebugDrawPointLineScale = 1.0f;
52 __gshared float ChipmunkDebugDrawOutlineWidth   = 1.0f;
53 
54 __gshared GLuint program;
55 
56 __gshared v2f v2f0 = { 0.0f, 0.0f };
57 
58 __gshared GLuint vao = 0;
59 __gshared GLuint vbo = 0;
60 
61 void ChipmunkDebugDrawInit()
62 {
63     // Setup the AA shader.
64     GLint vshader = CompileShader(GL_VERTEX_SHADER,
65         q{
66             #version 110
67 
68             attribute vec2 vertex;
69             attribute vec2 aa_coord;
70             attribute vec4 fill_color;
71             attribute vec4 outline_color;
72 
73             varying vec2 v_aa_coord;
74             varying vec4 v_fill_color;
75             varying vec4 v_outline_color;
76 
77             void main(){
78                 // TODO get rid of the GL 2.x matrix bit eventually?
79                 gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex, 0.0, 1.0);
80 
81                 v_fill_color = fill_color;
82                 v_outline_color = outline_color;
83                 v_aa_coord = aa_coord;
84             }
85         });
86 
87     GLint fshader = CompileShader(GL_FRAGMENT_SHADER,
88         q{
89             #version 110
90 
91             uniform float u_outline_coef;
92 
93             varying vec2 v_aa_coord;
94             varying vec4 v_fill_color;
95 
96             //const vec4 v_fill_color = vec4(0.0, 0.0, 0.0, 1.0);
97             varying vec4 v_outline_color;
98 
99             float aa_step(float t1, float t2, float f)
100             {
101                 //return step(t2, f);
102                 return smoothstep(t1, t2, f);
103             }
104 
105             void main()
106             {
107                 float l = length(v_aa_coord);
108 
109                 // Different pixel size estimations are handy.
110                 //float fw = fwidth(l);
111                 //float fw = length(vec2(dFdx(l), dFdy(l)));
112                 float fw = length(fwidth(v_aa_coord));
113 
114                 // Outline width threshold.
115                 float ow = 1.0 - fw; //*u_outline_coef;
116 
117                                     // Fill/outline color.
118                 float fo_step = aa_step(max(ow - fw, 0.0), ow, l);
119                 vec4 fo_color = mix(v_fill_color, v_outline_color, fo_step);
120 
121                 // Use pre-multiplied alpha.
122                 float alpha = 1.0 - aa_step(1.0 - fw, 1.0, l);
123                 gl_FragColor = fo_color * (fo_color.a * alpha);
124 
125                 //gl_FragColor = vec4(vec3(l), 1);
126             }
127         });
128 
129     program = LinkProgram(vshader, fshader);
130     CheckGLErrors();
131 
132     // Setu VBO and VAO.
133 
134     version (OSX)
135     {
136         glGenVertexArraysAPPLE(1, &vao);
137         glBindVertexArrayAPPLE(vao);
138     }
139     else
140     {
141         glGenVertexArrays(1, &vao);
142         glBindVertexArray(vao);
143     }
144 
145     glGenBuffers(1, &vbo);
146     glBindBuffer(GL_ARRAY_BUFFER, vbo);
147 
148     mixin(SET_ATTRIBUTE("program", "Vertex", "vertex", "GL_FLOAT"));
149     mixin(SET_ATTRIBUTE("program", "Vertex", "aa_coord", "GL_FLOAT"));
150     mixin(SET_ATTRIBUTE("program", "Vertex", "fill_color", "GL_FLOAT"));
151     mixin(SET_ATTRIBUTE("program", "Vertex", "outline_color", "GL_FLOAT"));
152 
153     glBindBuffer(GL_ARRAY_BUFFER, 0);
154 
155     version (OSX)
156     {
157         glBindVertexArrayAPPLE(0);
158     }
159     else
160     {
161         glBindVertexArray(0);
162     }
163 
164     CheckGLErrors();
165 }
166 
167 Color ColorFromHash(cpHashValue hash, float alpha)
168 {
169     c_ulong val = cast(c_ulong)hash;
170 
171     // scramble the bits up using Robert Jenkins' 32 bit integer hash function
172     val = (val + 0x7ed55d16) + (val << 12);
173     val = (val ^ 0xc761c23c) ^ (val >> 19);
174     val = (val + 0x165667b1) + (val << 5);
175     val = (val + 0xd3a2646c) ^ (val << 9);
176     val = (val + 0xfd7046c5) + (val << 3);
177     val = (val ^ 0xb55a4f09) ^ (val >> 16);
178 
179     GLfloat r = cast(GLfloat)((val >> 0) & 0xFF);
180     GLfloat g = cast(GLfloat)((val >> 8) & 0xFF);
181     GLfloat b = cast(GLfloat)((val >> 16) & 0xFF);
182 
183     GLfloat max       = cast(GLfloat)cpfmax(cpfmax(r, g), b);
184     GLfloat min       = cast(GLfloat)cpfmin(cpfmin(r, g), b);
185     GLfloat intensity = 0.75f;
186 
187     // Saturate and scale the color
188     if (min == max)
189     {
190         return RGBAColor(intensity, 0.0f, 0.0f, alpha);
191     }
192     else
193     {
194         GLfloat coef = cast(GLfloat)intensity / (max - min);
195         return RGBAColor(
196             (r - min) * coef,
197             (g - min) * coef,
198             (b - min) * coef,
199             alpha
200             );
201     }
202 }
203 
204 void glColor_from_color(Color color)
205 {
206     glColor4fv(cast(GLfloat*)&color);
207 }
208 
209 Color ColorForShape(cpShape* shape)
210 {
211     if (cpShapeGetSensor(shape))
212     {
213         return LAColor(1.0f, 0.1f);
214     }
215     else
216     {
217         cpBody* body_ = shape.body_;
218 
219         if (cpBodyIsSleeping(body_))
220         {
221             return LAColor(0.2f, 1.0f);
222         }
223         else if (body_.node.idleTime > shape.space.sleepTimeThreshold)
224         {
225             return LAColor(0.66f, 1.0f);
226         }
227         else
228         {
229             return ColorFromHash(shape.hashid, SHAPE_ALPHA);
230         }
231     }
232 }
233 
234 auto MAX(T)(T a, T b)
235 {
236     return a > b ? a : b;
237 }
238 
239 __gshared size_t triangle_capacity = 0;
240 __gshared GLsizei triangle_count    = 0;
241 __gshared Triangle* triangle_buffer = null;
242 
243 Triangle* PushTriangles(size_t count)
244 {
245     if (triangle_count + count > triangle_capacity)
246     {
247         triangle_capacity += MAX(triangle_capacity, count);
248         triangle_buffer    = cast(Triangle*)realloc(triangle_buffer, triangle_capacity * Triangle.sizeof);
249     }
250 
251     Triangle* buffer = triangle_buffer + triangle_count;
252     triangle_count += count;
253     return buffer;
254 }
255 
256 void ChipmunkDebugDrawCircle(cpVect pos, cpFloat angle, cpFloat radius, Color outlineColor, Color fillColor)
257 {
258     Triangle* triangles = PushTriangles(2);
259 
260     cpFloat r = radius + 1.0f / ChipmunkDebugDrawPointLineScale;
261     Vertex  a = { { pos.x - r, pos.y - r }, { -1.0, -1.0 }, fillColor, outlineColor };
262     Vertex  b = { { pos.x - r, pos.y + r }, { -1.0, 1.0 }, fillColor, outlineColor };
263     Vertex  c = { { pos.x + r, pos.y + r }, { 1.0, 1.0 }, fillColor, outlineColor };
264     Vertex  d = { { pos.x + r, pos.y - r }, { 1.0, -1.0 }, fillColor, outlineColor };
265 
266     Triangle t0 = { a, b, c };
267     triangles[0] = t0;
268     Triangle t1 = { a, c, d };
269     triangles[1] = t1;
270 
271     ChipmunkDebugDrawSegment(pos, cpvadd(pos, cpvmult(cpvforangle(angle), radius - ChipmunkDebugDrawPointLineScale * 0.5f)), outlineColor);
272 }
273 
274 void ChipmunkDebugDrawSegment(cpVect a, cpVect b, Color color)
275 {
276     ChipmunkDebugDrawFatSegment(a, b, 0.0f, color, color);
277 }
278 
279 void ChipmunkDebugDrawFatSegment(cpVect a, cpVect b, cpFloat radius, Color outlineColor, Color fillColor)
280 {
281     Triangle* triangles = PushTriangles(6);
282 
283     cpVect n = cpvnormalize(cpvperp(cpvsub(b, a)));
284     cpVect t = cpvperp(n);
285 
286     cpFloat half = 1.0f / ChipmunkDebugDrawPointLineScale;
287     cpFloat r    = radius + half;
288 
289     if (r <= half)
290     {
291         r         = half;
292         fillColor = outlineColor;
293     }
294 
295     cpVect nw     = (cpvmult(n, r));
296     cpVect tw     = (cpvmult(t, r));
297     v2f v0 = v2f(cpvsub(b, cpvadd(nw, tw))); // { 1.0, -1.0}
298     v2f v1 = v2f(cpvadd(b, cpvsub(nw, tw))); // { 1.0,  1.0}
299     v2f v2 = v2f(cpvsub(b, nw));             // { 0.0, -1.0}
300     v2f v3 = v2f(cpvadd(b, nw));             // { 0.0,  1.0}
301     v2f v4 = v2f(cpvsub(a, nw));             // { 0.0, -1.0}
302     v2f v5 = v2f(cpvadd(a, nw));             // { 0.0,  1.0}
303     v2f v6 = v2f(cpvsub(a, cpvsub(nw, tw))); // {-1.0, -1.0}
304     v2f v7 = v2f(cpvadd(a, cpvadd(nw, tw))); // {-1.0,  1.0}
305 
306     Triangle t0 = { { v0, { 1.0f, -1.0f }, fillColor, outlineColor }, { v1, { 1.0f, 1.0f }, fillColor, outlineColor }, { v2, { 0.0f, -1.0f }, fillColor, outlineColor } };
307     triangles[0] = t0;
308     Triangle t1 = { { v3, { 0.0f, 1.0f }, fillColor, outlineColor }, { v1, { 1.0f, 1.0f }, fillColor, outlineColor }, { v2, { 0.0f, -1.0f }, fillColor, outlineColor } };
309     triangles[1] = t1;
310     Triangle t2 = { { v3, { 0.0f, 1.0f }, fillColor, outlineColor }, { v4, { 0.0f, -1.0f }, fillColor, outlineColor }, { v2, { 0.0f, -1.0f }, fillColor, outlineColor } };
311     triangles[2] = t2;
312     Triangle t3 = { { v3, { 0.0f, 1.0f }, fillColor, outlineColor }, { v4, { 0.0f, -1.0f }, fillColor, outlineColor }, { v5, { 0.0f, 1.0f }, fillColor, outlineColor } };
313     triangles[3] = t3;
314     Triangle t4 = { { v6, { -1.0f, -1.0f }, fillColor, outlineColor }, { v4, { 0.0f, -1.0f }, fillColor, outlineColor }, { v5, { 0.0f, 1.0f }, fillColor, outlineColor } };
315     triangles[4] = t4;
316     Triangle t5 = { { v6, { -1.0f, -1.0f }, fillColor, outlineColor }, { v7, { -1.0f, 1.0f }, fillColor, outlineColor }, { v5, { 0.0f, 1.0f }, fillColor, outlineColor } };
317     triangles[5] = t5;
318 }
319 
320 void ChipmunkDebugDrawPolygon(int count, cpVect* verts, cpFloat radius, Color outlineColor, Color fillColor)
321 {
322     struct ExtrudeVerts
323     {
324         cpVect offset, n;
325     }
326 
327     size_t bytes = ExtrudeVerts.sizeof * count;
328     ExtrudeVerts* extrude = cast(ExtrudeVerts*)alloca(bytes);
329     memset(extrude, 0, bytes.sizeof);
330 
331     for (int i = 0; i < count; i++)
332     {
333         cpVect v0 = verts[(i - 1 + count) % count];
334         cpVect v1 = verts[i];
335         cpVect v2 = verts[(i + 1) % count];
336 
337         cpVect n1 = cpvnormalize(cpvperp(cpvsub(v1, v0)));
338         cpVect n2 = cpvnormalize(cpvperp(cpvsub(v2, v1)));
339 
340         cpVect offset         = cpvmult(cpvadd(n1, n2), 1.0 / (cpvdot(n1, n2) + 1.0f));
341         ExtrudeVerts v = { offset, n2 };
342         extrude[i] = v;
343     }
344 
345     //	Triangle *triangles = PushTriangles(6*count);
346     Triangle* triangles = PushTriangles(5 * count - 2);
347     Triangle* cursor    = triangles;
348 
349     cpFloat inset = cpfmax(0.0f, 1.0f / ChipmunkDebugDrawPointLineScale - radius);
350 
351     for (int i = 0; i < count - 2; i++)
352     {
353         v2f v0 = v2f(cpvsub(verts[0], cpvmult(extrude[0].offset, inset)));
354         v2f v1 = v2f(cpvsub(verts[i + 1], cpvmult(extrude[i + 1].offset, inset)));
355         v2f v2 = v2f(cpvsub(verts[i + 2], cpvmult(extrude[i + 2].offset, inset)));
356 
357         Triangle t = { { v0, v2f0, fillColor, fillColor }, { v1, v2f0, fillColor, fillColor }, { v2, v2f0, fillColor, fillColor } };
358         *cursor++ = t;
359     }
360 
361     cpFloat outset = inset + 1.0f / ChipmunkDebugDrawPointLineScale + radius;
362 
363     for (int i = 0, j = count - 1; i < count; j = i, i++)
364     {
365         cpVect vA = verts[i];
366         cpVect vB = verts[j];
367 
368         cpVect nA = extrude[i].n;
369         cpVect nB = extrude[j].n;
370 
371         cpVect offsetA = extrude[i].offset;
372         cpVect offsetB = extrude[j].offset;
373 
374         cpVect innerA = cpvsub(vA, cpvmult(offsetA, inset));
375         cpVect innerB = cpvsub(vB, cpvmult(offsetB, inset));
376 
377         // Admittedly my variable naming sucks here...
378         v2f inner0 = v2f(innerA);
379         v2f inner1 = v2f(innerB);
380         v2f outer0 = v2f(cpvadd(innerA, cpvmult(nB, outset)));
381         v2f outer1 = v2f(cpvadd(innerB, cpvmult(nB, outset)));
382         v2f outer2 = v2f(cpvadd(innerA, cpvmult(offsetA, outset)));
383         v2f outer3 = v2f(cpvadd(innerA, cpvmult(nA, outset)));
384 
385         v2f n0      = v2f(nA);
386         v2f n1      = v2f(nB);
387         v2f offset0 = v2f(offsetA);
388 
389         Triangle t0 = { { inner0, v2f0, fillColor, outlineColor }, { inner1, v2f0, fillColor, outlineColor }, { outer1, n1, fillColor, outlineColor } };
390         *cursor++ = t0;
391         Triangle t1 = { { inner0, v2f0, fillColor, outlineColor }, { outer0, n1, fillColor, outlineColor }, { outer1, n1, fillColor, outlineColor } };
392         *cursor++ = t1;
393         Triangle t2 = { { inner0, v2f0, fillColor, outlineColor }, { outer0, n1, fillColor, outlineColor }, { outer2, offset0, fillColor, outlineColor } };
394         *cursor++ = t2;
395         Triangle t3 = { { inner0, v2f0, fillColor, outlineColor }, { outer2, offset0, fillColor, outlineColor }, { outer3, n0, fillColor, outlineColor } };
396         *cursor++ = t3;
397     }
398 }
399 
400 void ChipmunkDebugDrawDot(cpFloat size, cpVect pos, Color fillColor)
401 {
402     Triangle* triangles = PushTriangles(2);
403 
404     float  r = size * 0.5f / ChipmunkDebugDrawPointLineScale;
405     Vertex a = { { pos.x - r, pos.y - r }, { -1.0f, -1.0f }, fillColor, fillColor };
406     Vertex b = { { pos.x - r, pos.y + r }, { -1.0f, 1.0f }, fillColor, fillColor };
407     Vertex c = { { pos.x + r, pos.y + r }, { 1.0f, 1.0f }, fillColor, fillColor };
408     Vertex d = { { pos.x + r, pos.y - r }, { 1.0f, -1.0f }, fillColor, fillColor };
409 
410     Triangle t0 = { a, b, c };
411     triangles[0] = t0;
412     Triangle t1 = { a, c, d };
413     triangles[1] = t1;
414 }
415 
416 void ChipmunkDebugDrawBB(cpBB bb, Color color)
417 {
418     cpVect[4] verts;
419     verts[0] = cpv(bb.l, bb.b);
420     verts[1] = cpv(bb.l, bb.t);
421     verts[2] = cpv(bb.r, bb.t);
422     verts[3] = cpv(bb.r, bb.b);
423     ChipmunkDebugDrawPolygon(4, verts.ptr, 0.0f, color, LAColor(0, 0));
424 }
425 
426 struct ShapeColors
427 {
428     Color outlineColor, fillColor;
429 }
430 
431 void DrawShape(cpShape* shape, ShapeColors* colors)
432 {
433     cpBody* body_        = shape.body_;
434     Color fill_color    = (colors ? colors.fillColor : ColorForShape(shape));
435     Color outline_color = (colors ? colors.outlineColor : LINE_COLOR);
436 
437     switch (shape.klass.type)
438     {
439         case CP_CIRCLE_SHAPE:
440         {
441             cpCircleShape* circle = cast(cpCircleShape*)shape;
442             ChipmunkDebugDrawCircle(circle.tc, body_.a, circle.r, outline_color, fill_color);
443             break;
444         }
445 
446         case CP_SEGMENT_SHAPE:
447         {
448             cpSegmentShape* seg = cast(cpSegmentShape*)shape;
449             ChipmunkDebugDrawFatSegment(seg.ta, seg.tb, seg.r, outline_color, fill_color);
450             break;
451         }
452 
453         case CP_POLY_SHAPE:
454         {
455             cpPolyShape* poly = cast(cpPolyShape*)shape;
456             ChipmunkDebugDrawPolygon(poly.numVerts, poly.tVerts, poly.r, outline_color, fill_color);
457             break;
458         }
459 
460         default:
461             break;
462     }
463 }
464 
465 void ChipmunkDebugDrawShape(cpShape* shape, Color outlineColor, Color fillColor)
466 {
467     ShapeColors colors = { outlineColor, fillColor };
468     DrawShape(shape, (outlineColor.a == 0.0 && fillColor.a == 0.0 ? null : &colors));
469 }
470 
471 void ChipmunkDebugDrawShapes(cpSpace* space)
472 {
473     cpSpaceEachShape(space, safeCast!cpSpaceShapeIteratorFunc(&DrawShape), null);
474 }
475 
476 immutable cpVect[] spring_verts = [
477     { 0.00f, 0.0f },
478     { 0.20f, 0.0f },
479     { 0.25f, 3.0f },
480     { 0.30f, -6.0f },
481     { 0.35f, 6.0f },
482     { 0.40f, -6.0f },
483     { 0.45f, 6.0f },
484     { 0.50f, -6.0f },
485     { 0.55f, 6.0f },
486     { 0.60f, -6.0f },
487     { 0.65f, 6.0f },
488     { 0.70f, -3.0f },
489     { 0.75f, 6.0f },
490     { 0.80f, 0.0f },
491     { 1.00f, 0.0f },
492 ];
493 
494 immutable int spring_count = spring_verts.length;
495 
496 void drawSpring(cpDampedSpring* spring, cpBody* body_a, cpBody* body_b)
497 {
498     cpVect a = cpvadd(body_a.p, cpvrotate(spring.anchr1, body_a.rot));
499     cpVect b = cpvadd(body_b.p, cpvrotate(spring.anchr2, body_b.rot));
500 
501     ChipmunkDebugDrawDot(5, a, CONSTRAINT_COLOR);
502     ChipmunkDebugDrawDot(5, b, CONSTRAINT_COLOR);
503 
504     cpVect  delta = cpvsub(b, a);
505     GLfloat cos   = delta.x;
506     GLfloat sin   = delta.y;
507     GLfloat s     = 1.0f / cpvlength(delta);
508 
509     cpVect r1 = cpv(cos, -sin * s);
510     cpVect r2 = cpv(sin, cos * s);
511 
512     cpVect* verts = cast(cpVect*)alloca(spring_count * cpVect.sizeof);
513 
514     for (int i = 0; i < spring_count; i++)
515     {
516         cpVect v = spring_verts[i];
517         verts[i] = cpv(cpvdot(v, r1) + a.x, cpvdot(v, r2) + a.y);
518     }
519 
520     for (int i = 0; i < spring_count - 1; i++)
521     {
522         ChipmunkDebugDrawSegment(verts[i], verts[i + 1], CONSTRAINT_COLOR);
523     }
524 }
525 
526 void drawConstraint(cpConstraint* constraint, void* unused)
527 {
528     cpBody* body_a = constraint.a;
529     cpBody* body_b = constraint.b;
530 
531     const cpConstraintClass* klass = constraint.klass;
532 
533     if (klass == cpPinJointGetClass())
534     {
535         cpPinJoint* joint = cast(cpPinJoint*)constraint;
536 
537         cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
538         cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
539 
540         ChipmunkDebugDrawDot(5, a, CONSTRAINT_COLOR);
541         ChipmunkDebugDrawDot(5, b, CONSTRAINT_COLOR);
542         ChipmunkDebugDrawSegment(a, b, CONSTRAINT_COLOR);
543     }
544     else if (klass == cpSlideJointGetClass())
545     {
546         cpSlideJoint* joint = cast(cpSlideJoint*)constraint;
547 
548         cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
549         cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
550 
551         ChipmunkDebugDrawDot(5, a, CONSTRAINT_COLOR);
552         ChipmunkDebugDrawDot(5, b, CONSTRAINT_COLOR);
553         ChipmunkDebugDrawSegment(a, b, CONSTRAINT_COLOR);
554     }
555     else if (klass == cpPivotJointGetClass())
556     {
557         cpPivotJoint* joint = cast(cpPivotJoint*)constraint;
558 
559         cpVect a = cpvadd(body_a.p, cpvrotate(joint.anchr1, body_a.rot));
560         cpVect b = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
561 
562         ChipmunkDebugDrawDot(5, a, CONSTRAINT_COLOR);
563         ChipmunkDebugDrawDot(5, b, CONSTRAINT_COLOR);
564     }
565     else if (klass == cpGrooveJointGetClass())
566     {
567         cpGrooveJoint* joint = cast(cpGrooveJoint*)constraint;
568 
569         cpVect a = cpvadd(body_a.p, cpvrotate(joint.grv_a, body_a.rot));
570         cpVect b = cpvadd(body_a.p, cpvrotate(joint.grv_b, body_a.rot));
571         cpVect c = cpvadd(body_b.p, cpvrotate(joint.anchr2, body_b.rot));
572 
573         ChipmunkDebugDrawDot(5, c, CONSTRAINT_COLOR);
574         ChipmunkDebugDrawSegment(a, b, CONSTRAINT_COLOR);
575     }
576     else if (klass == cpDampedSpringGetClass())
577     {
578         drawSpring(cast(cpDampedSpring*)constraint, body_a, body_b);
579     }
580 }
581 
582 void ChipmunkDebugDrawConstraint(cpConstraint* constraint)
583 {
584     drawConstraint(constraint, null);
585 }
586 
587 void ChipmunkDebugDrawConstraints(cpSpace* space)
588 {
589     cpSpaceEachConstraint(space, &drawConstraint, null);
590 }
591 
592 void ChipmunkDebugDrawCollisionPoints(cpSpace* space)
593 {
594     cpArray* arbiters = space.arbiters;
595     Color color       = RGBAColor(1.0f, 0.0f, 0.0f, 1.0f);
596 
597     for (int i = 0; i < arbiters.num; i++)
598     {
599         cpArbiter* arb = cast(cpArbiter*)arbiters.arr[i];
600 
601         for (int j = 0; j < arb.numContacts; j++)
602         {
603             cpVect  p = arb.contacts[j].p;
604             cpVect  n = arb.contacts[j].n;
605             cpFloat d = 2.0 - arb.contacts[j].dist / 2.0;
606 
607             cpVect a = cpvadd(p, cpvmult(n, d));
608             cpVect b = cpvadd(p, cpvmult(n, -d));
609             ChipmunkDebugDrawSegment(a, b, color);
610         }
611     }
612 }
613 
614 void ChipmunkDebugDrawFlushRenderer()
615 {
616     glBindBuffer(GL_ARRAY_BUFFER, vbo);
617     glBufferData(GL_ARRAY_BUFFER, Triangle.sizeof * triangle_count, triangle_buffer, GL_STREAM_DRAW);
618 
619     glUseProgram(program);
620     glUniform1f(glGetUniformLocation(program, "u_outline_coef"), ChipmunkDebugDrawPointLineScale);
621 
622     version (OSX)
623     {
624         glBindVertexArrayAPPLE(vao);
625     }
626     else
627     {
628         glBindVertexArray(vao);
629     }
630 
631     glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3);
632 
633     CheckGLErrors();
634 }
635 
636 void ChipmunkDebugDrawClearRenderer()
637 {
638     triangle_count = 0;
639 }
640 
641 __gshared GLsizei pushed_triangle_count = 0;
642 
643 void ChipmunkDebugDrawPushRenderer()
644 {
645     pushed_triangle_count = triangle_count;
646 }
647 
648 void ChipmunkDebugDrawPopRenderer()
649 {
650     triangle_count = pushed_triangle_count;
651 }