2 * Copyright (c) 2006-2007 Erin Catto http://www.gphysics.com
4 * Permission to use, copy, modify, distribute and sell this software
5 * and its documentation for any purpose is hereby granted without fee,
6 * provided that the above copyright notice appear in all copies.
7 * Erin Catto makes no representations about the suitability
8 * of this software for any purpose.
9 * It is provided "as is" without express or implied warranty.
11 * Changes by Ketmar // Invisible Vector
15 import arsd
.simpledisplay
;
19 import iv
.glbinds
.utils
;
24 bool optShowTimes
= false;
25 bool optDrawBVH
= false;
28 // ////////////////////////////////////////////////////////////////////////// //
33 // ////////////////////////////////////////////////////////////////////////// //
34 __gshared BodyContact
[] contacts
;
37 // ////////////////////////////////////////////////////////////////////////// //
38 // random number in range [-1,1]
39 public VFloat
rnd () {
41 import std
.random
: uniform
;
42 return cast(VFloat
)uniform
!"[]"(-VFloatNum
!1, VFloatNum
!1);
45 public VFloat
rnd (VFloat lo
, VFloat hi
) {
47 import std
.random
: uniform
;
48 return cast(VFloat
)uniform
!"[]"(lo
, hi
);
52 // ////////////////////////////////////////////////////////////////////////// //
53 __gshared BodyBase bomb
= null;
55 __gshared VFloat timeStep
= VFloatNum
!1/VFloatNum
!60;
56 __gshared
int iterations
= 10;
57 __gshared Vec2 gravity
= Vec2(VFloatNum
!0, -VFloatNum
!10);
59 __gshared
int demoIndex
= 0;
60 __gshared string demoTitle
;
62 __gshared
bool onlyOneBomb
= false;
64 __gshared World world
;
65 shared static this () { world
= new World(gravity
, iterations
); }
68 // ////////////////////////////////////////////////////////////////////////// //
69 void launchBomb (SimpleWindow sdwin
) {
70 if (!onlyOneBomb || bomb
is null) {
72 foreach (int ang
; 0..10) {
73 import std
.math
: cos
, sin
;
74 import std
.random
: uniform
;
75 Vec2 v
= Vec2(0.6*cos(deg2rad(360/10*ang
))*uniform
!"[]"(0.7, 1.3), 0.6*sin(deg2rad(360/10*ang
))*uniform
!"[]"(0.7, 1.3));
78 bomb
= new PolyBody(world
, vtx
, 50);
79 //bomb = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!50);
80 bomb
.friction
= VFloatNum
!(0.2);
82 bomb
.setPosition(rnd(-VFloatNum
!15, VFloatNum
!15), VFloatNum
!15);
83 bomb
.rotation
= rnd(-VFloatNum
!(1.5), VFloatNum
!(1.5));
84 bomb
.velocity
= bomb
.position
*(-VFloatNum
!(1.5));
85 bomb
.angularVelocity
= rnd(-VFloatNum
!20, VFloatNum
!20);
86 import std
.format
: format
;
87 sdwin
.title
= "%s -- bodies: %s".format(demoTitle
, world
.bodies
.length
);
91 // ////////////////////////////////////////////////////////////////////////// //
92 void drawBody (BodyBase bodyb
) {
93 if (auto booody
= cast(PolyBody
)bodyb
) {
95 glColor3f(VFloatNum
!(0.4), VFloatNum
!(0.9), VFloatNum
!(0.4));
97 glColor3f(VFloatNum
!(0.8), VFloatNum
!(0.8), VFloatNum
!(0.9));
99 auto rmt
= Mat22(booody
.rotation
);
100 glBegin(GL_LINE_LOOP
);
101 foreach (const ref vx
; booody
.verts
) {
102 auto vp
= booody
.position
+rmt
*vx
;
103 glVertex2f(vp
.x
, vp
.y
);
110 void drawJoint (Joint joint
) {
111 auto b1
= joint
.body0
;
112 auto b2
= joint
.body1
;
114 auto r1
= Mat22(b1
.rotation
);
115 auto r2
= Mat22(b2
.rotation
);
117 Vec2 x1
= b1
.position
;
118 Vec2 p1
= x1
+r1
*joint
.localAnchor1
;
120 Vec2 x2
= b2
.position
;
121 Vec2 p2
= x2
+r2
*joint
.localAnchor2
;
123 glColor3f(VFloatNum
!(0.5), VFloatNum
!(0.5), VFloatNum
!(0.8));
125 glVertex2f(x1
.x
, x1
.y
);
126 glVertex2f(p1
.x
, p1
.y
);
127 glVertex2f(x2
.x
, x2
.y
);
128 glVertex2f(p2
.x
, p2
.y
);
134 glMatrixMode(GL_MODELVIEW
);
136 glTranslatef(VFloatNum
!0, -VFloatNum
!7, -VFloatNum
!25);
139 foreach (BodyBase b
; world
.bodies
) b
.drawBody();
140 foreach (Joint j
; world
.joints
) j
.drawJoint();
145 glPointSize(VFloatNum
!4);
146 glColor3f(VFloatNum
!1, VFloatNum
!0, VFloatNum
!0);
148 foreach (const ref cxy
; contacts
) glVertex2f(cxy
.position
.x
, cxy
.position
.y
);
150 glPointSize(VFloatNum
!1);
155 world
.drawBVH((Vec2 min
, Vec2 max
) {
156 glColor3f(VFloatNum
!1, VFloatNum
!1, VFloatNum
!0);
157 glBegin(GL_LINE_LOOP
);
158 glVertex2f(min
.x
, min
.y
);
159 glVertex2f(max
.x
, min
.y
);
160 glVertex2f(max
.x
, max
.y
);
161 glVertex2f(min
.x
, max
.y
);
168 // ////////////////////////////////////////////////////////////////////////// //
170 enum BoxW
= VFloatNum
!100;
171 enum BoxH
= VFloatNum
!20;
173 struct DemoInfo
{ string dsc
; }
177 @DemoInfo("A Single Box") void demo1 () {
178 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
179 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
181 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!200, (b
, size
) {
182 b
.setPosition(VFloatNum
!0, VFloatNum
!4);
188 @DemoInfo("Simple Pendulum") void demo2 () {
189 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
190 b
.friction
= VFloatNum
!(0.2);
191 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
192 b
.rotation
= VFloatNum
!0;
195 auto b2
= PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!100, (b
, size
) {
196 b
.friction
= VFloatNum
!(0.2);
197 b
.setPosition(VFloatNum
!9, VFloatNum
!11);
198 b
.rotation
= VFloatNum
!0;
201 new Joint(world
, b1
, b2
, Vec2(VFloatNum
!0, VFloatNum
!11));
205 // varying friction coefficients
206 @DemoInfo("Varying Friction Coefficients") void demo3 () {
207 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
208 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
211 PolyBody
.Box(world
, Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
, (b
, size
) {
212 b
.setPosition(-VFloatNum
!2, VFloatNum
!11);
213 b
.rotation
= -VFloatNum
!(0.25);
216 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.25), VFloatNum
!1), VFloat
.max
, (b
, size
) {
217 b
.setPosition(VFloatNum
!(5.25), VFloatNum
!(9.5));
220 PolyBody
.Box(world
, Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
, (b
, size
) {
221 b
.setPosition(VFloatNum
!2, VFloatNum
!7);
222 b
.rotation
= VFloatNum
!(0.25);
225 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.25), VFloatNum
!1), VFloat
.max
, (b
, size
) {
226 b
.setPosition(-VFloatNum
!(5.25), VFloatNum
!(5.5));
229 PolyBody
.Box(world
, Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
, (b
, size
) {
230 b
.setPosition(-VFloatNum
!2, VFloatNum
!3);
231 b
.rotation
= -VFloatNum
!(0.25);
234 static immutable VFloat
[5] frictions
= [
241 foreach (immutable idx
, VFloat frc
; frictions
) {
242 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25, (b
, size
) {
244 b
.setPosition(-VFloatNum
!(7.5)+VFloatNum
!2*cast(int)idx
, VFloatNum
!14);
251 @DemoInfo("Randomized Stacking") void demo4 () {
252 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
253 b
.friction
= VFloatNum
!(0.2);
254 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
255 b
.rotation
= VFloatNum
!0;
258 foreach (int idx
; 0..10) {
259 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!1, (b
, size
) {
260 b
.friction
= VFloatNum
!(0.2);
261 VFloat x
= rnd(-VFloatNum
!(0.1), VFloatNum
!(0.1));
262 b
.setPosition(x
, VFloatNum
!(0.51)+VFloatNum
!(1.05)*idx
);
269 @DemoInfo("Pyramid Stacking") void demo5 () {
270 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
271 b
.friction
= VFloatNum
!(0.2);
272 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
273 b
.rotation
= VFloatNum
!0;
276 Vec2 x
= Vec2(-VFloatNum
!6, VFloatNum
!(0.75));
277 foreach (int idx
; 0..12) {
279 foreach (int j
; idx
..12) {
280 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!10, (b
, size
) {
281 b
.friction
= VFloatNum
!(0.2);
284 y
+= Vec2(VFloatNum
!(1.125), VFloatNum
!0);
286 //x += Vec2(VFloatNum!(0.5625), VFloatNum!(1.125));
287 x
+= Vec2(VFloatNum
!(0.5625), VFloatNum
!2);
293 @DemoInfo("A Teeter") void demo6 () {
294 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
295 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
298 auto b2
= PolyBody
.Box(world
, Vec2(VFloatNum
!12, VFloatNum
!(0.25)), VFloatNum
!100, (b
, size
) {
299 b
.setPosition(VFloatNum
!0, VFloatNum
!1);
302 new Joint(world
, b1
, b2
, Vec2(VFloatNum
!0, VFloatNum
!1));
304 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25, (b
, size
) {
305 b
.setPosition(-VFloatNum
!5, VFloatNum
!2);
308 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25, (b
, size
) {
309 b
.setPosition(-VFloatNum
!(5.5), VFloatNum
!2);
312 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!100, (b
, size
) {
313 b
.setPosition(VFloatNum
!(5.5), VFloatNum
!15);
318 // a suspension bridge
319 @DemoInfo("A Suspension Bridge") void demo7 () {
320 import std
.math
: PI
;
322 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
323 b
.friction
= VFloatNum
!(0.2);
324 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
325 b
.rotation
= VFloatNum
!0;
329 VFloat mass
= VFloatNum
!50;
330 foreach (int idx
; 0..numPlanks
) {
331 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!(0.25)), mass
, (b
, size
) {
332 b
.friction
= VFloatNum
!(0.2);
333 b
.setPosition(-VFloatNum
!(8.5)+VFloatNum
!(1.25)*idx
, VFloatNum
!5);
338 VFloat frequencyHz
= VFloatNum
!2;
339 VFloat dampingRatio
= VFloatNum
!(0.7);
340 // frequency in radians
341 VFloat omega
= VFloatNum
!2*PI
*frequencyHz
;
342 // damping coefficient
343 VFloat d
= VFloatNum
!2*mass
*dampingRatio
*omega
;
345 VFloat k
= mass
*omega
*omega
;
347 VFloat softnesss
= VFloatNum
!1/(d
+timeStep
*k
);
348 VFloat biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
350 foreach (int idx
; 0..numPlanks
) {
351 new Joint(world
, world
.bodies
[idx
], world
.bodies
[idx
+1], Vec2(-VFloatNum
!(9.125)+VFloatNum
!(1.25)*idx
, VFloatNum
!5), (j
) {
352 j
.softness
= softnesss
;
353 j
.biasFactor
= biasFactorr
;
357 new Joint(world
, world
.bodies
[numPlanks
], world
.bodies
[0], Vec2(-VFloatNum
!(9.125)+VFloatNum
!(1.25)*numPlanks
, VFloatNum
!5), (j
) {
358 j
.softness
= softnesss
;
359 j
.biasFactor
= biasFactorr
;
365 @DemoInfo("Dominos") void demo8 () {
366 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
367 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
370 PolyBody
.Box(world
, Vec2(VFloatNum
!12, VFloatNum
!(0.5)), VFloat
.max
, (b
, size
) {
371 b
.setPosition(-VFloatNum
!(1.5), VFloatNum
!10);
374 foreach (int idx
; 0..10) {
375 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.2), VFloatNum
!2), VFloatNum
!100, (b
, size
) {
376 b
.setPosition(-VFloatNum
!6+VFloatNum
!1*idx
, VFloatNum
!(11.125));
377 b
.friction
= VFloatNum
!(0.1);
381 PolyBody
.Box(world
, Vec2(VFloatNum
!14, VFloatNum
!(0.5)), VFloat
.max
, (b
, size
) {
382 b
.setPosition(VFloatNum
!1, VFloatNum
!6);
383 b
.rotation
= VFloatNum
!(0.3);
386 auto b2
= PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!3), VFloat
.max
, (b
, size
) {
387 b
.setPosition(-VFloatNum
!7, VFloatNum
!4);
390 auto b3
= PolyBody
.Box(world
, Vec2(VFloatNum
!12, VFloatNum
!(0.25)), VFloatNum
!20, (b
, size
) {
391 b
.setPosition(-VFloatNum
!(0.9), VFloatNum
!1);
394 new Joint(world
, b1
, b3
, Vec2(-VFloatNum
!2, VFloatNum
!1));
396 auto b4
= PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!10, (b
, size
) {
397 b
.setPosition(-VFloatNum
!10, VFloatNum
!15);
400 new Joint(world
, b2
, b4
, Vec2(-VFloatNum
!7, VFloatNum
!15));
402 auto b5
= PolyBody
.Box(world
, Vec2(VFloatNum
!2, VFloatNum
!2), VFloatNum
!12, (b
, size
) {
403 b
.setPosition(VFloatNum
!6, VFloatNum
!(2.5));
404 b
.friction
= VFloatNum
!(0.1);
407 new Joint(world
, b1
, b5
, Vec2(VFloatNum
!6, VFloatNum
!(2.6)));
410 auto b6
= PolyBody
.Box(world
, Vec2(VFloatNum
!2, VFloatNum
!(0.2)), VFloatNum
!10, (b
, size
) {
411 b
.setPosition(VFloatNum
!6, VFloatNum
!(3.6));
414 new Joint(world
, b5
, b6
, Vec2(VFloatNum
!7, VFloatNum
!(3.5)));
419 @DemoInfo("Multi-pendulum") void demo9 () {
420 import std
.math
: PI
;
422 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
423 b
.friction
= VFloatNum
!(0.2);
424 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
425 b
.rotation
= VFloatNum
!0;
428 enum VFloat mass
= VFloatNum
!10;
431 enum VFloat frequencyHz
= VFloatNum
!4;
432 enum VFloat dampingRatio
= VFloatNum
!(0.7);
433 // frequency in radians
434 enum VFloat omega
= VFloatNum
!2*PI
*frequencyHz
;
435 // damping coefficient
436 enum VFloat d
= VFloatNum
!2*mass
*dampingRatio
*omega
;
438 enum VFloat k
= mass
*omega
*omega
;
440 VFloat softnesss
= VFloatNum
!1/(d
+timeStep
*k
);
441 VFloat biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
443 enum VFloat y
= VFloatNum
!12;
444 foreach (int idx
; 0..15) {
445 auto x
= Vec2(VFloatNum
!(0.5)+idx
, y
);
446 auto b
= PolyBody
.Box(world
, Vec2(VFloatNum
!(0.75), VFloatNum
!(0.25)), mass
, (b
, size
) {
447 b
.friction
= VFloatNum
!(0.2);
449 b
.rotation
= VFloatNum
!0;
452 new Joint(world
, b1
, b
, Vec2(idx
, y
), (j
) {
453 j
.softness
= softnesss
;
454 j
.biasFactor
= biasFactorr
;
462 // ////////////////////////////////////////////////////////////////////////// //
463 __gshared
bool paused
= false;
464 __gshared
bool doOneStep
= false;
465 __gshared
bool slowmo
= false;
466 __gshared
int slowmocount
= 0;
469 bool setupDemo (int index
, SimpleWindow w
) {
471 alias sms
= getSymbolsByUDA
!(mixin(__MODULE__
), DemoInfo
);
472 foreach (immutable idx
, const memb
; sms
) {
474 demoTitle
= getUDAs
!(memb
, DemoInfo
)[0].dsc
;
487 // ////////////////////////////////////////////////////////////////////////// //
488 void simulationStep (SimpleWindow sdwin
) {
489 static double accumulator
= 0;
491 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
);
493 glMatrixMode(GL_MODELVIEW
);
495 glTranslatef(VFloatNum
!0, -VFloatNum
!7, -VFloatNum
!25);
498 contacts
.assumeSafeAppend
;
502 if (optShowTimes
) stt
= MonoTime
.currTime
;
503 world
.step(timeStep
);
505 auto tm
= (MonoTime
.currTime
-stt
).total
!"msecs";
506 { import core
.stdc
.stdio
; printf("step: %d msecs\n", cast(int)tm
); }
508 sdwin
.redrawOpenGlSceneNow();
512 // ////////////////////////////////////////////////////////////////////////// //
515 writeln("simulation options:");
516 writeln(" accumutate impulses: ", World
.accumulateImpulses
);
517 writeln(" position correction: ", World
.positionCorrection
);
518 writeln(" warm starting : ", World
.warmStarting
);
522 // ////////////////////////////////////////////////////////////////////////// //
524 //setOpenGLContextVersion(3, 2); // up to GLSL 150
525 //openGLContextCompatible = false;
527 b2dlDrawContactsCB
= delegate (in ref BodyContact ctx
) {
531 auto sdwin
= new SimpleWindow(GWidth
, GHeight
, "Box2DLite Physics", OpenGlOptions
.yes
, Resizablity
.fixedSize
);
532 //sdwin.hideCursor();
534 sdwin
.redrawOpenGlScene
= delegate () {
535 glClear(GL_COLOR_BUFFER_BIT
);
539 sdwin
.visibleForTheFirstTime
= delegate () {
542 sdwin
.setAsCurrentOpenGlContext(); // make this window active
543 glViewport(0, 0, GWidth
, GHeight
);
544 glMatrixMode(GL_PROJECTION
);
546 oglPerspective(45.0, cast(VFloat
)GWidth
/cast(VFloat
)GHeight
, 0.1, 100.0);
547 sdwin
.redrawOpenGlScene();
550 sdwin
.eventLoop(1000/60,
552 if (sdwin
.closed || world
is null) return;
556 if (--slowmocount
< 0) {
557 simulationStep(sdwin
);
561 simulationStep(sdwin
);
564 } else if (doOneStep
) {
566 simulationStep(sdwin
);
569 delegate (KeyEvent event
) {
570 if (sdwin
.closed
) return;
571 if (!event
.pressed
) return;
573 case Key
.Escape
: sdwin
.close(); break;
577 delegate (MouseEvent event
) {
579 delegate (dchar ch
) {
580 if (ch
== 'q') { sdwin
.close(); return; }
581 if (ch
== 'r') { setupDemo(demoIndex
, sdwin
); return; }
582 if (ch
== ' ') { paused
= !paused
; return; }
583 if (ch
== 's') { slowmo
= !slowmo
; return; }
584 if (ch
>= '1' && ch
<= '9') { setupDemo(ch
-'1', sdwin
); return; }
585 if (ch
== '0') { setupDemo(10, sdwin
); return; }
586 if (ch
== 'a') { World
.accumulateImpulses
= !World
.accumulateImpulses
; showOpts(); return; }
587 if (ch
== 'p') { World
.positionCorrection
= !World
.positionCorrection
; showOpts(); return; }
588 if (ch
== 'w') { World
.warmStarting
= !World
.warmStarting
; showOpts(); return; }
589 if (ch
== '\n' || ch
== '\r') { launchBomb(sdwin
); return; }
590 if (ch
== '+') { setupDemo(demoIndex
+1, sdwin
); return; }
591 if (ch
== '-') { if (demoIndex
> 0) setupDemo(demoIndex
-1, sdwin
); return; }
592 if (ch
== 'z') { doOneStep
= true; return; }
593 if (ch
== 'T') { optShowTimes
= !optShowTimes
; return; }
594 if (ch
== 'A') { optDrawBVH
= !optDrawBVH
; return; }
595 if (ch
== 'O') { onlyOneBomb
= !onlyOneBomb
; return; }