2 * Copyright (c) 2006-2007 Erin Catto http://box2d.org
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
;
17 import iv
.glbinds
.utils
;
22 bool optShowTimes
= false;
23 bool optDrawBVH
= false;
26 // ////////////////////////////////////////////////////////////////////////// //
31 // ////////////////////////////////////////////////////////////////////////// //
32 __gshared BodyContact
[] contacts
;
35 // ////////////////////////////////////////////////////////////////////////// //
36 // random number in range [-1,1]
37 public VFloat
rnd () {
39 import std
.random
: uniform
;
40 return cast(VFloat
)uniform
!"[]"(-VFloatNum
!1, VFloatNum
!1);
43 public VFloat
rnd (VFloat lo
, VFloat hi
) {
45 import std
.random
: uniform
;
46 return cast(VFloat
)uniform
!"[]"(lo
, hi
);
50 // ////////////////////////////////////////////////////////////////////////// //
51 __gshared BodyBase bomb
= null;
53 __gshared VFloat timeStep
= VFloatNum
!1/VFloatNum
!60;
54 __gshared
int iterations
= 10;
55 __gshared Vec2 gravity
= Vec2(VFloatNum
!0, -VFloatNum
!10);
57 __gshared
int demoIndex
= 0;
58 __gshared string demoTitle
;
60 __gshared
bool onlyOneBomb
= false;
61 __gshared
int launchesLeft
= 0;
63 __gshared World world
;
64 shared static this () { world
= new World(gravity
, iterations
); }
67 // ////////////////////////////////////////////////////////////////////////// //
68 void launchBomb (SimpleWindow sdwin
) {
69 if (!onlyOneBomb || bomb
is null) {
71 foreach (int ang
; 0..10) {
72 import std
.math
: cos
, sin
;
73 import std
.random
: uniform
;
74 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));
77 bomb
= new PolyBody(world
, vtx
, 50);
78 //bomb = PolyBody.Box(Vec2(VFloatNum!1, VFloatNum!1), VFloatNum!50);
79 bomb
.friction
= VFloatNum
!(0.2);
81 bomb
.setPosition(rnd(-VFloatNum
!15, VFloatNum
!15), VFloatNum
!15);
82 bomb
.rotation
= rnd(-VFloatNum
!(1.5), VFloatNum
!(1.5));
83 bomb
.velocity
= bomb
.position
*(-VFloatNum
!(1.5));
84 bomb
.angularVelocity
= rnd(-VFloatNum
!20, VFloatNum
!20);
85 import std
.format
: format
;
86 sdwin
.title
= "%s -- bodies: %s".format(demoTitle
, world
.bodies
.length
);
90 // ////////////////////////////////////////////////////////////////////////// //
91 void drawBody (BodyBase bodyb
) {
92 if (auto booody
= cast(PolyBody
)bodyb
) {
94 glColor3f(VFloatNum
!(0.4), VFloatNum
!(0.9), VFloatNum
!(0.4));
96 glColor3f(VFloatNum
!(0.8), VFloatNum
!(0.8), VFloatNum
!(0.9));
98 auto rmt
= booody
.rmat
;
99 glBegin(GL_LINE_LOOP
);
100 foreach (const ref vx
; booody
.verts
) {
101 auto vp
= booody
.position
+rmt
*vx
;
102 glVertex2f(vp
.x
, vp
.y
);
109 void drawJoint (Joint joint
) {
110 auto b1
= joint
.body0
;
111 auto b2
= joint
.body1
;
116 Vec2 x1
= b1
.position
;
117 Vec2 p1
= x1
+r1
*joint
.localAnchor1
;
119 Vec2 x2
= b2
.position
;
120 Vec2 p2
= x2
+r2
*joint
.localAnchor2
;
122 glColor3f(VFloatNum
!(0.5), VFloatNum
!(0.5), VFloatNum
!(0.8));
124 glVertex2f(x1
.x
, x1
.y
);
125 glVertex2f(p1
.x
, p1
.y
);
126 glVertex2f(x2
.x
, x2
.y
);
127 glVertex2f(p2
.x
, p2
.y
);
133 glMatrixMode(GL_MODELVIEW
);
135 glTranslatef(VFloatNum
!0, -VFloatNum
!7, -VFloatNum
!25);
138 foreach (BodyBase b
; world
.bodies
) b
.drawBody();
139 foreach (Joint j
; world
.joints
) j
.drawJoint();
144 glPointSize(VFloatNum
!4);
145 glColor3f(VFloatNum
!1, VFloatNum
!0, VFloatNum
!0);
147 foreach (const ref cxy
; contacts
) glVertex2f(cxy
.position
.x
, cxy
.position
.y
);
149 glPointSize(VFloatNum
!1);
154 world
.drawBVH((Vec2 min
, Vec2 max
) {
155 glColor3f(VFloatNum
!1, VFloatNum
!1, VFloatNum
!0);
156 glBegin(GL_LINE_LOOP
);
157 glVertex2f(min
.x
, min
.y
);
158 glVertex2f(max
.x
, min
.y
);
159 glVertex2f(max
.x
, max
.y
);
160 glVertex2f(min
.x
, max
.y
);
167 // ////////////////////////////////////////////////////////////////////////// //
169 enum BoxW
= VFloatNum
!100;
170 enum BoxH
= VFloatNum
!20;
172 struct DemoInfo
{ string dsc
; }
176 @DemoInfo("A Single Box") void demo1 () {
177 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
178 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
180 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!200, (b
, size
) {
181 b
.setPosition(VFloatNum
!0, VFloatNum
!4);
187 @DemoInfo("Simple Pendulum") void demo2 () {
188 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
189 b
.friction
= VFloatNum
!(0.2);
190 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
191 b
.rotation
= VFloatNum
!0;
194 auto b2
= PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!100, (b
, size
) {
195 b
.friction
= VFloatNum
!(0.2);
196 b
.setPosition(VFloatNum
!9, VFloatNum
!11);
197 b
.rotation
= VFloatNum
!0;
200 new Joint(world
, b1
, b2
, Vec2(VFloatNum
!0, VFloatNum
!11));
204 // varying friction coefficients
205 @DemoInfo("Varying Friction Coefficients") void demo3 () {
206 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
207 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
210 PolyBody
.Box(world
, Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
, (b
, size
) {
211 b
.setPosition(-VFloatNum
!2, VFloatNum
!11);
212 b
.rotation
= -VFloatNum
!(0.25);
215 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.25), VFloatNum
!1), VFloat
.max
, (b
, size
) {
216 b
.setPosition(VFloatNum
!(5.25), VFloatNum
!(9.5));
219 PolyBody
.Box(world
, Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
, (b
, size
) {
220 b
.setPosition(VFloatNum
!2, VFloatNum
!7);
221 b
.rotation
= VFloatNum
!(0.25);
224 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.25), VFloatNum
!1), VFloat
.max
, (b
, size
) {
225 b
.setPosition(-VFloatNum
!(5.25), VFloatNum
!(5.5));
228 PolyBody
.Box(world
, Vec2(VFloatNum
!13, VFloatNum
!(0.25)), VFloat
.max
, (b
, size
) {
229 b
.setPosition(-VFloatNum
!2, VFloatNum
!3);
230 b
.rotation
= -VFloatNum
!(0.25);
233 static immutable VFloat
[5] frictions
= [
240 foreach (immutable idx
, VFloat frc
; frictions
) {
241 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25, (b
, size
) {
243 b
.setPosition(-VFloatNum
!(7.5)+VFloatNum
!2*cast(int)idx
, VFloatNum
!14);
250 @DemoInfo("Randomized Stacking") void demo4 () {
251 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
252 b
.friction
= VFloatNum
!(0.2);
253 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
254 b
.rotation
= VFloatNum
!0;
257 foreach (int idx
; 0..10) {
258 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!1, (b
, size
) {
259 b
.friction
= VFloatNum
!(0.2);
260 VFloat x
= rnd(-VFloatNum
!(0.1), VFloatNum
!(0.1));
261 b
.setPosition(x
, VFloatNum
!(0.51)+VFloatNum
!(1.05)*idx
);
268 @DemoInfo("Pyramid Stacking") void demo5 () {
269 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
270 b
.friction
= VFloatNum
!(0.2);
271 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
272 b
.rotation
= VFloatNum
!0;
275 Vec2 x
= Vec2(-VFloatNum
!6, VFloatNum
!(0.75));
276 foreach (int idx
; 0..12) {
278 foreach (int j
; idx
..12) {
279 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!10, (b
, size
) {
280 b
.friction
= VFloatNum
!(0.2);
283 y
+= Vec2(VFloatNum
!(1.125), VFloatNum
!0);
285 //x += Vec2(VFloatNum!(0.5625), VFloatNum!(1.125));
286 x
+= Vec2(VFloatNum
!(0.5625), VFloatNum
!2);
292 @DemoInfo("A Teeter") void demo6 () {
293 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
294 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
297 auto b2
= PolyBody
.Box(world
, Vec2(VFloatNum
!12, VFloatNum
!(0.25)), VFloatNum
!100, (b
, size
) {
298 b
.setPosition(VFloatNum
!0, VFloatNum
!1);
301 new Joint(world
, b1
, b2
, Vec2(VFloatNum
!0, VFloatNum
!1));
303 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25, (b
, size
) {
304 b
.setPosition(-VFloatNum
!5, VFloatNum
!2);
307 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!25, (b
, size
) {
308 b
.setPosition(-VFloatNum
!(5.5), VFloatNum
!2);
311 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!1), VFloatNum
!100, (b
, size
) {
312 b
.setPosition(VFloatNum
!(5.5), VFloatNum
!15);
317 // a suspension bridge
318 @DemoInfo("A Suspension Bridge") void demo7 () {
319 import std
.math
: PI
;
321 PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
322 b
.friction
= VFloatNum
!(0.2);
323 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
324 b
.rotation
= VFloatNum
!0;
328 VFloat mass
= VFloatNum
!50;
329 foreach (int idx
; 0..numPlanks
) {
330 PolyBody
.Box(world
, Vec2(VFloatNum
!1, VFloatNum
!(0.25)), mass
, (b
, size
) {
331 b
.friction
= VFloatNum
!(0.2);
332 b
.setPosition(-VFloatNum
!(8.5)+VFloatNum
!(1.25)*idx
, VFloatNum
!5);
337 VFloat frequencyHz
= VFloatNum
!2;
338 VFloat dampingRatio
= VFloatNum
!(0.7);
339 // frequency in radians
340 VFloat omega
= VFloatNum
!2*PI
*frequencyHz
;
341 // damping coefficient
342 VFloat d
= VFloatNum
!2*mass
*dampingRatio
*omega
;
344 VFloat k
= mass
*omega
*omega
;
346 VFloat softnesss
= VFloatNum
!1/(d
+timeStep
*k
);
347 VFloat biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
349 foreach (int idx
; 0..numPlanks
) {
350 new Joint(world
, world
.bodies
[idx
], world
.bodies
[idx
+1], Vec2(-VFloatNum
!(9.125)+VFloatNum
!(1.25)*idx
, VFloatNum
!5), (j
) {
351 j
.softness
= softnesss
;
352 j
.biasFactor
= biasFactorr
;
356 new Joint(world
, world
.bodies
[numPlanks
], world
.bodies
[0], Vec2(-VFloatNum
!(9.125)+VFloatNum
!(1.25)*numPlanks
, VFloatNum
!5), (j
) {
357 j
.softness
= softnesss
;
358 j
.biasFactor
= biasFactorr
;
364 @DemoInfo("Dominos") void demo8 () {
365 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
366 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
369 PolyBody
.Box(world
, Vec2(VFloatNum
!12, VFloatNum
!(0.5)), VFloat
.max
, (b
, size
) {
370 b
.setPosition(-VFloatNum
!(1.5), VFloatNum
!10);
373 foreach (int idx
; 0..10) {
374 PolyBody
.Box(world
, Vec2(VFloatNum
!(0.2), VFloatNum
!2), VFloatNum
!100, (b
, size
) {
375 b
.setPosition(-VFloatNum
!6+VFloatNum
!1*idx
, VFloatNum
!(11.125));
376 b
.friction
= VFloatNum
!(0.1);
380 PolyBody
.Box(world
, Vec2(VFloatNum
!14, VFloatNum
!(0.5)), VFloat
.max
, (b
, size
) {
381 b
.setPosition(VFloatNum
!1, VFloatNum
!6);
382 b
.rotation
= VFloatNum
!(0.3);
385 auto b2
= PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!3), VFloat
.max
, (b
, size
) {
386 b
.setPosition(-VFloatNum
!7, VFloatNum
!4);
389 auto b3
= PolyBody
.Box(world
, Vec2(VFloatNum
!12, VFloatNum
!(0.25)), VFloatNum
!20, (b
, size
) {
390 b
.setPosition(-VFloatNum
!(0.9), VFloatNum
!1);
393 new Joint(world
, b1
, b3
, Vec2(-VFloatNum
!2, VFloatNum
!1));
395 auto b4
= PolyBody
.Box(world
, Vec2(VFloatNum
!(0.5), VFloatNum
!(0.5)), VFloatNum
!10, (b
, size
) {
396 b
.setPosition(-VFloatNum
!10, VFloatNum
!15);
399 new Joint(world
, b2
, b4
, Vec2(-VFloatNum
!7, VFloatNum
!15));
401 auto b5
= PolyBody
.Box(world
, Vec2(VFloatNum
!2, VFloatNum
!2), VFloatNum
!12, (b
, size
) {
402 b
.setPosition(VFloatNum
!6, VFloatNum
!(2.5));
403 b
.friction
= VFloatNum
!(0.1);
406 new Joint(world
, b1
, b5
, Vec2(VFloatNum
!6, VFloatNum
!(2.6)));
409 auto b6
= PolyBody
.Box(world
, Vec2(VFloatNum
!2, VFloatNum
!(0.2)), VFloatNum
!10, (b
, size
) {
410 b
.setPosition(VFloatNum
!6, VFloatNum
!(3.6));
413 new Joint(world
, b5
, b6
, Vec2(VFloatNum
!7, VFloatNum
!(3.5)));
418 @DemoInfo("Multi-pendulum") void demo9 () {
419 import std
.math
: PI
;
421 auto b1
= PolyBody
.Box(world
, Vec2(BoxW
, BoxH
), VFloat
.max
, (b
, size
) {
422 b
.friction
= VFloatNum
!(0.2);
423 b
.setPosition(VFloatNum
!0, -VFloatNum
!(0.5)*size
.y
);
424 b
.rotation
= VFloatNum
!0;
427 enum VFloat mass
= VFloatNum
!10;
430 enum VFloat frequencyHz
= VFloatNum
!4;
431 enum VFloat dampingRatio
= VFloatNum
!(0.7);
432 // frequency in radians
433 enum VFloat omega
= VFloatNum
!2*PI
*frequencyHz
;
434 // damping coefficient
435 enum VFloat d
= VFloatNum
!2*mass
*dampingRatio
*omega
;
437 enum VFloat k
= mass
*omega
*omega
;
439 VFloat softnesss
= VFloatNum
!1/(d
+timeStep
*k
);
440 VFloat biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
442 enum VFloat y
= VFloatNum
!12;
443 foreach (int idx
; 0..15) {
444 auto x
= Vec2(VFloatNum
!(0.5)+idx
, y
);
445 auto b
= PolyBody
.Box(world
, Vec2(VFloatNum
!(0.75), VFloatNum
!(0.25)), mass
, (b
, size
) {
446 b
.friction
= VFloatNum
!(0.2);
448 b
.rotation
= VFloatNum
!0;
451 new Joint(world
, b1
, b
, Vec2(idx
, y
), (j
) {
452 j
.softness
= softnesss
;
453 j
.biasFactor
= biasFactorr
;
461 // ////////////////////////////////////////////////////////////////////////// //
462 __gshared
bool paused
= false;
463 __gshared
bool doOneStep
= false;
464 __gshared
bool slowmo
= false;
465 __gshared
int slowmocount
= 0;
468 bool setupDemo (int index
, SimpleWindow w
) {
470 alias sms
= getSymbolsByUDA
!(mixin(__MODULE__
), DemoInfo
);
471 foreach (immutable idx
, const memb
; sms
) {
473 demoTitle
= getUDAs
!(memb
, DemoInfo
)[0].dsc
;
486 // ////////////////////////////////////////////////////////////////////////// //
487 void simulationStep (SimpleWindow sdwin
) {
488 if (launchesLeft
> 0) {
490 auto oob
= onlyOneBomb
;
492 scope(exit
) onlyOneBomb
= oob
;
496 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
);
498 glMatrixMode(GL_MODELVIEW
);
500 glTranslatef(VFloatNum
!0, -VFloatNum
!7, -VFloatNum
!25);
503 contacts
.assumeSafeAppend
;
507 if (optShowTimes
) stt
= MonoTime
.currTime
;
508 world
.step(timeStep
);
510 auto tm
= (MonoTime
.currTime
-stt
).total
!"msecs";
511 { import core
.stdc
.stdio
; printf("step: %d msecs\n", cast(int)tm
); }
513 sdwin
.redrawOpenGlSceneNow();
517 // ////////////////////////////////////////////////////////////////////////// //
520 writeln("simulation options:");
521 writeln(" accumutate impulses: ", World
.accumulateImpulses
);
522 writeln(" position correction: ", World
.positionCorrection
);
523 writeln(" warm starting : ", World
.warmStarting
);
527 // ////////////////////////////////////////////////////////////////////////// //
529 //setOpenGLContextVersion(3, 2); // up to GLSL 150
530 //openGLContextCompatible = false;
532 b2dlDrawContactsCB
= delegate (in ref BodyContact ctx
) {
536 auto sdwin
= new SimpleWindow(GWidth
, GHeight
, "Box2DLite Physics", OpenGlOptions
.yes
, Resizablity
.fixedSize
);
537 //sdwin.hideCursor();
539 sdwin
.redrawOpenGlScene
= delegate () {
540 glClear(GL_COLOR_BUFFER_BIT
);
544 sdwin
.visibleForTheFirstTime
= delegate () {
547 sdwin
.setAsCurrentOpenGlContext(); // make this window active
548 glViewport(0, 0, GWidth
, GHeight
);
549 glMatrixMode(GL_PROJECTION
);
551 oglPerspective(45.0, cast(VFloat
)GWidth
/cast(VFloat
)GHeight
, 0.1, 100.0);
552 sdwin
.redrawOpenGlScene();
555 sdwin
.eventLoop(1000/60,
557 if (sdwin
.closed || world
is null) return;
561 if (--slowmocount
< 0) {
562 simulationStep(sdwin
);
566 simulationStep(sdwin
);
569 } else if (doOneStep
) {
571 simulationStep(sdwin
);
574 delegate (KeyEvent event
) {
575 if (sdwin
.closed
) return;
576 if (!event
.pressed
) return;
578 case Key
.Escape
: sdwin
.close(); break;
582 delegate (MouseEvent event
) {
584 delegate (dchar ch
) {
585 if (ch
== 'q') { sdwin
.close(); return; }
586 if (ch
== 'r') { setupDemo(demoIndex
, sdwin
); return; }
587 if (ch
== ' ') { paused
= !paused
; return; }
588 if (ch
== 's') { slowmo
= !slowmo
; return; }
589 if (ch
>= '1' && ch
<= '9') { setupDemo(ch
-'1', sdwin
); return; }
590 if (ch
== '0') { setupDemo(10, sdwin
); return; }
591 if (ch
== 'a') { World
.accumulateImpulses
= !World
.accumulateImpulses
; showOpts(); return; }
592 if (ch
== 'p') { World
.positionCorrection
= !World
.positionCorrection
; showOpts(); return; }
593 if (ch
== 'w') { World
.warmStarting
= !World
.warmStarting
; showOpts(); return; }
594 if (ch
== '\n' || ch
== '\r') { launchBomb(sdwin
); return; }
595 if (ch
== '+') { setupDemo(demoIndex
+1, sdwin
); return; }
596 if (ch
== '-') { if (demoIndex
> 0) setupDemo(demoIndex
-1, sdwin
); return; }
597 if (ch
== 'z') { doOneStep
= true; return; }
598 if (ch
== 'T') { optShowTimes
= !optShowTimes
; return; }
599 if (ch
== 'A') { optDrawBVH
= !optDrawBVH
; return; }
600 if (ch
== 'O') { onlyOneBomb
= !onlyOneBomb
; return; }
601 if (ch
== '!') { launchesLeft
= 1100; return; }