even more cosmetix
[b2ld.git] / xmain.d
blob888ba860889337847d61631517ecbc71cbb02f85
1 /*
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
13 module xmain;
15 import arsd.simpledisplay;
16 import arsd.color;
17 import arsd.image;
19 import iv.glbinds.utils;
20 import iv.vmath;
22 import b2dlite;
24 bool optShowTimes = false;
25 bool optDrawBVH = false;
28 // ////////////////////////////////////////////////////////////////////////// //
29 enum GWidth = 800;
30 enum GHeight = 600;
33 // ////////////////////////////////////////////////////////////////////////// //
34 __gshared BodyContact[] contacts;
37 // ////////////////////////////////////////////////////////////////////////// //
38 // random number in range [-1,1]
39 public VFloat rnd () {
40 pragma(inline, true);
41 import std.random : uniform;
42 return cast(VFloat)uniform!"[]"(-VFloatNum!1, VFloatNum!1);
45 public VFloat rnd (VFloat lo, VFloat hi) {
46 pragma(inline, true);
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) {
71 Vec2[] vtx;
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));
76 vtx ~= v;
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) {
94 if (booody is bomb) {
95 glColor3f(VFloatNum!(0.4), VFloatNum!(0.9), VFloatNum!(0.4));
96 } else {
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);
105 glEnd();
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));
124 glBegin(GL_LINES);
125 glVertex2f(x1.x, x1.y);
126 glVertex2f(p1.x, p1.y);
127 glVertex2f(x2.x, x2.y);
128 glVertex2f(p2.x, p2.y);
129 glEnd();
133 void drawWorld () {
134 glMatrixMode(GL_MODELVIEW);
135 glLoadIdentity();
136 glTranslatef(VFloatNum!0, -VFloatNum!7, -VFloatNum!25);
138 // draw world
139 foreach (BodyBase b; world.bodies) b.drawBody();
140 foreach (Joint j; world.joints) j.drawJoint();
142 // draw contacts
144 import iv.glbinds;
145 glPointSize(VFloatNum!4);
146 glColor3f(VFloatNum!1, VFloatNum!0, VFloatNum!0);
147 glBegin(GL_POINTS);
148 foreach (const ref cxy; contacts) glVertex2f(cxy.position.x, cxy.position.y);
149 glEnd();
150 glPointSize(VFloatNum!1);
153 if (optDrawBVH) {
154 // draw BVH
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);
162 glEnd();
168 // ////////////////////////////////////////////////////////////////////////// //
169 // demos
170 enum BoxW = VFloatNum!100;
171 enum BoxH = VFloatNum!20;
173 struct DemoInfo { string dsc; }
176 // single box
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);
187 // a simple pendulum
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 = [
235 VFloatNum!(0.75),
236 VFloatNum!(0.5),
237 VFloatNum!(0.35),
238 VFloatNum!(0.1),
239 VFloatNum!0
241 foreach (immutable idx, VFloat frc; frictions) {
242 PolyBody.Box(world, Vec2(VFloatNum!(0.5), VFloatNum!(0.5)), VFloatNum!25, (b, size) {
243 b.friction = frc;
244 b.setPosition(-VFloatNum!(7.5)+VFloatNum!2*cast(int)idx, VFloatNum!14);
250 // a vertical stack
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);
268 // a pyramid
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) {
278 auto y = x;
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);
282 b.position = y;
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);
292 // a teeter
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;
328 enum numPlanks = 15;
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);
337 // tuning
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;
344 // spring stifness
345 VFloat k = mass*omega*omega;
346 // magic formulas
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;
364 // dominos
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)));
409 // box cap
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)));
418 // a multi-pendulum
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;
430 // tuning
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;
437 // spring stiffness
438 enum VFloat k = mass*omega*omega;
439 // magic formulas
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);
448 b.position = x;
449 b.rotation = VFloatNum!0;
452 new Joint(world, b1, b, Vec2(idx, y), (j) {
453 j.softness = softnesss;
454 j.biasFactor = biasFactorr;
457 b1 = b;
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) {
470 import std.traits;
471 alias sms = getSymbolsByUDA!(mixin(__MODULE__), DemoInfo);
472 foreach (immutable idx, const memb; sms) {
473 if (index == idx) {
474 demoTitle = getUDAs!(memb, DemoInfo)[0].dsc;
475 w.title = demoTitle;
476 world.clear();
477 bomb = null;
478 memb();
479 demoIndex = index;
480 return true;
483 return false;
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);
494 glLoadIdentity();
495 glTranslatef(VFloatNum!0, -VFloatNum!7, -VFloatNum!25);
497 contacts.length = 0;
498 contacts.assumeSafeAppend;
500 import core.time;
501 MonoTime stt;
502 if (optShowTimes) stt = MonoTime.currTime;
503 world.step(timeStep);
504 if (optShowTimes) {
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 // ////////////////////////////////////////////////////////////////////////// //
513 void showOpts () {
514 import std.stdio;
515 writeln("simulation options:");
516 writeln(" accumutate impulses: ", World.accumulateImpulses);
517 writeln(" position correction: ", World.positionCorrection);
518 writeln(" warm starting : ", World.warmStarting);
522 // ////////////////////////////////////////////////////////////////////////// //
523 void main () {
524 //setOpenGLContextVersion(3, 2); // up to GLSL 150
525 //openGLContextCompatible = false;
527 b2dlDrawContactsCB = delegate (in ref BodyContact ctx) {
528 contacts ~= 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);
536 drawWorld();
539 sdwin.visibleForTheFirstTime = delegate () {
540 showOpts();
541 setupDemo(0, sdwin);
542 sdwin.setAsCurrentOpenGlContext(); // make this window active
543 glViewport(0, 0, GWidth, GHeight);
544 glMatrixMode(GL_PROJECTION);
545 glLoadIdentity();
546 oglPerspective(45.0, cast(VFloat)GWidth/cast(VFloat)GHeight, 0.1, 100.0);
547 sdwin.redrawOpenGlScene();
550 sdwin.eventLoop(1000/60,
551 delegate () {
552 if (sdwin.closed || world is null) return;
553 if (!paused) {
554 doOneStep = false;
555 if (slowmo) {
556 if (--slowmocount < 0) {
557 simulationStep(sdwin);
558 slowmocount = 10;
560 } else {
561 simulationStep(sdwin);
562 slowmocount = 0;
564 } else if (doOneStep) {
565 doOneStep = false;
566 simulationStep(sdwin);
569 delegate (KeyEvent event) {
570 if (sdwin.closed) return;
571 if (!event.pressed) return;
572 switch (event.key) {
573 case Key.Escape: sdwin.close(); break;
574 default:
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; }