WIP
[b2ld.git] / xmain.d
blob0a6b31a7bd7417e84de9c8cc4a2683f853aa431f
1 module xmain is aliced;
3 import std.math : PI;
5 import iv.glbinds;
6 import iv.vmath;
7 import arsd.color;
8 import arsd.png;
10 import b2dlite;
13 enum GWidth = 800;
14 enum GHeight = 600;
17 Body bomb = null;
19 Float timeStep = FloatNum!(1.0)/FloatNum!(60.0);
20 int iterations = 10;
21 vec2 gravity = vec2(FloatNum!(0.0), -FloatNum!(10.0));
23 int demoIndex = 0;
25 World world;
26 static this () { world = new World(gravity, iterations); }
29 static void LaunchBomb () {
30 if (bomb is null) {
31 bomb = new Body();
32 bomb.set(vec2(FloatNum!(1.0), FloatNum!(1.0)), FloatNum!(50.0));
33 bomb.friction = FloatNum!(0.2);
34 world.Add(bomb);
36 bomb.position.set(Random(-FloatNum!(15.0), FloatNum!(15.0)), FloatNum!(15.0));
37 bomb.rotation = Random(-FloatNum!(1.5), FloatNum!(1.5));
38 bomb.velocity = bomb.position*(-FloatNum!(1.5));
39 bomb.angularVelocity = Random(-FloatNum!(20.0), FloatNum!(20.0));
43 static void DrawBody (Body body) {
44 auto R = Mat22(body.rotation);
45 vec2 x = body.position;
46 vec2 h = body.width*FloatNum!(0.5);
48 vec2 v1 = x+R*vec2(-h.x, -h.y);
49 vec2 v2 = x+R*vec2( h.x, -h.y);
50 vec2 v3 = x+R*vec2( h.x, h.y);
51 vec2 v4 = x+R*vec2(-h.x, h.y);
53 if (body is bomb) {
54 glColor3f(FloatNum!(0.4), FloatNum!(0.9), FloatNum!(0.4));
55 } else {
56 glColor3f(FloatNum!(0.8), FloatNum!(0.8), FloatNum!(0.9));
59 glBegin(GL_LINE_LOOP);
60 glVertex2f(v1.x, v1.y);
61 glVertex2f(v2.x, v2.y);
62 glVertex2f(v3.x, v3.y);
63 glVertex2f(v4.x, v4.y);
64 glEnd();
68 static void DrawJoint (Joint joint) {
69 Body b1 = joint.body1;
70 Body b2 = joint.body2;
72 auto R1 = Mat22(b1.rotation);
73 auto R2 = Mat22(b2.rotation);
75 vec2 x1 = b1.position;
76 vec2 p1 = x1+R1*joint.localAnchor1;
78 vec2 x2 = b2.position;
79 vec2 p2 = x2+R2*joint.localAnchor2;
81 glColor3f(FloatNum!(0.5), FloatNum!(0.5), FloatNum!(0.8));
82 glBegin(GL_LINES);
83 glVertex2f(x1.x, x1.y);
84 glVertex2f(p1.x, p1.y);
85 glVertex2f(x2.x, x2.y);
86 glVertex2f(p2.x, p2.y);
87 glEnd();
91 // ////////////////////////////////////////////////////////////////////////// //
92 // demos
93 struct DemoInfo { string descr; }
95 {"A Single Box", Demo1},
96 {"Simple Pendulum", Demo2},
97 {"Varying Friction Coefficients", Demo3},
98 {"Randomized Stacking", Demo4},
99 {"Pyramid Stacking", Demo5},
100 {"A Teeter", Demo6},
101 {"A Suspension Bridge", Demo7},
102 {"Dominos", Demo8},
103 {"Multi-pendulum", Demo9},
106 // single box
107 @DemoInfo("A Single Box") void Demo1 () {
108 with (auto b = new Body()) {
109 b.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
110 b.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b.width.y);
111 world.Add(b);
113 with (auto b = new Body()) {
114 b.set(vec2(FloatNum!(1.0), FloatNum!(1.0)), FloatNum!(200.0));
115 b.position.set(FloatNum!(0.0), FloatNum!(4.0));
116 world.Add(b);
121 // a simple pendulum
122 @DemoInfo("Simple Pendulum") void Demo2 () {
123 auto b1 = new Body();
124 b1.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
125 b1.friction = FloatNum!(0.2);
126 b1.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b1.width.y);
127 b1.rotation = FloatNum!(0.0);
128 world.Add(b1);
130 auto b2 = new Body();
131 b2.set(vec2(FloatNum!(1.0), FloatNum!(1.0)), FloatNum!(100.0));
132 b2.friction = FloatNum!(0.2);
133 b2.position.set(FloatNum!(9.0), FloatNum!(11.0));
134 b2.rotation = FloatNum!(0.0);
135 world.Add(b2);
137 with (auto j = new Joint()) {
138 j.set(b1, b2, vec2(FloatNum!(0.0), FloatNum!(11.0)));
139 world.Add(j);
144 // varying friction coefficients
145 @DemoInfo("Varying Friction Coefficients") void Demo3 () {
146 with (auto b = new Body()) {
147 b.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
148 b.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b.width.y);
149 world.Add(b);
152 with (auto b = new Body()) {
153 b.set(vec2(FloatNum!(13.0), FloatNum!(0.25)), Float.max);
154 b.position.set(-FloatNum!(2.0), FloatNum!(11.0));
155 b.rotation = -FloatNum!(0.25);
156 world.Add(b);
159 with (auto b = new Body()) {
160 b.set(vec2(FloatNum!(0.25), FloatNum!(1.0)), Float.max);
161 b.position.set(FloatNum!(5.25), FloatNum!(9.5));
162 world.Add(b);
165 with (auto b = new Body()) {
166 b.set(vec2(FloatNum!(13.0), FloatNum!(0.25)), Float.max);
167 b.position.set(FloatNum!(2.0), FloatNum!(7.0));
168 b.rotation = FloatNum!(0.25);
169 world.Add(b);
172 with (auto b = new Body()) {
173 b.set(vec2(FloatNum!(0.25), FloatNum!(1.0)), Float.max);
174 b.position.set(-FloatNum!(5.25), FloatNum!(5.5));
175 world.Add(b);
178 with (auto b = new Body()) {
179 b.set(vec2(FloatNum!(13.0), FloatNum!(0.25)), Float.max);
180 b.position.set(-FloatNum!(2.0), FloatNum!(3.0));
181 b.rotation = -FloatNum!(0.25);
182 world.Add(b);
185 static immutable Float[5] frictions = [FloatNum!(0.75), FloatNum!(0.5), FloatNum!(0.35), FloatNum!(0.1), FloatNum!(0.0)];
186 for (int i = 0; i < 5; ++i) {
187 with (auto b = new Body()) {
188 b.set(vec2(FloatNum!(0.5), FloatNum!(0.5)), FloatNum!(25.0));
189 b.friction = frictions[i];
190 b.position.set(-FloatNum!(7.5)+FloatNum!(2.0)*i, FloatNum!(14.0));
191 world.Add(b);
197 // a vertical stack
198 @DemoInfo("Randomized Stacking") void Demo4 () {
199 with (auto b = new Body()) {
200 b.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
201 b.friction = FloatNum!(0.2);
202 b.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b.width.y);
203 b.rotation = FloatNum!(0.0);
204 world.Add(b);
207 for (int i = 0; i < 10; ++i) {
208 with (auto b = new Body()) {
209 b.set(vec2(FloatNum!(1.0), FloatNum!(1.0)), FloatNum!(1.0));
210 b.friction = FloatNum!(0.2);
211 Float x = Random(-FloatNum!(0.1), FloatNum!(0.1));
212 b.position.set(x, FloatNum!(0.51)+FloatNum!(1.05)*i);
213 world.Add(b);
219 // a pyramid
220 @DemoInfo("Pyramid Stacking") void Demo5 () {
221 with (auto b = new Body()) {
222 b.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
223 b.friction = FloatNum!(0.2);
224 b.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b.width.y);
225 b.rotation = FloatNum!(0.0);
226 world.Add(b);
229 vec2 x = vec2(-FloatNum!(6.0), FloatNum!(0.75));
230 vec2 y;
232 for (int i = 0; i < 12; ++i) {
233 y = x;
234 for (int j = i; j < 12; ++j) {
235 with (auto b = new Body()) {
236 b.set(vec2(FloatNum!(1.0), FloatNum!(1.0)), FloatNum!(10.0));
237 b.friction = FloatNum!(0.2);
238 b.position = y;
239 world.Add(b);
241 y += vec2(FloatNum!(1.125), FloatNum!(0.0));
243 //x += vec2(FloatNum!(0.5625), FloatNum!(1.125));
244 x += vec2(FloatNum!(0.5625), FloatNum!(2.0));
249 // a teeter
250 @DemoInfo("A Teeter") void Demo6 () {
251 Body b1 = new Body();
252 b1.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
253 b1.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b1.width.y);
254 world.Add(b1);
256 Body b2 = new Body();
257 b2.set(vec2(FloatNum!(12.0), FloatNum!(0.25)), FloatNum!(100.0));
258 b2.position.set(FloatNum!(0.0), FloatNum!(1.0));
259 world.Add(b2);
261 Body b3 = new Body();
262 b3.set(vec2(FloatNum!(0.5), FloatNum!(0.5)), FloatNum!(25.0));
263 b3.position.set(-FloatNum!(5.0), FloatNum!(2.0));
264 world.Add(b3);
266 Body b4 = new Body();
267 b4.set(vec2(FloatNum!(0.5), FloatNum!(0.5)), FloatNum!(25.0));
268 b4.position.set(-FloatNum!(5.5), FloatNum!(2.0));
269 world.Add(b4);
271 Body b5 = new Body();
272 b5.set(vec2(FloatNum!(1.0), FloatNum!(1.0)), FloatNum!(100.0));
273 b5.position.set(FloatNum!(5.5), FloatNum!(15.0));
274 world.Add(b5);
276 with (auto j = new Joint()) {
277 j.set(b1, b2, vec2(FloatNum!(0.0), FloatNum!(1.0)));
278 world.Add(j);
283 // a suspension bridge
284 @DemoInfo("A Suspension Bridge") void Demo7 () {
285 with (auto b = new Body()) {
286 b.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
287 b.friction = FloatNum!(0.2);
288 b.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b.width.y);
289 b.rotation = FloatNum!(0.0);
290 world.Add(b);
293 enum numPlanks = 15;
294 Float mass = FloatNum!(50.0);
296 for (int i = 0; i < numPlanks; ++i) {
297 auto b = new Body();
298 b.set(vec2(FloatNum!(1.0), FloatNum!(0.25)), mass);
299 b.friction = FloatNum!(0.2);
300 b.position.set(-FloatNum!(8.5)+FloatNum!(1.25)*i, FloatNum!(5.0));
301 world.Add(b);
304 // tuning
305 Float frequencyHz = FloatNum!(2.0);
306 Float dampingRatio = FloatNum!(0.7);
308 // frequency in radians
309 Float omega = FloatNum!(2.0)*PI*frequencyHz;
311 // damping coefficient
312 Float d = FloatNum!(2.0)*mass*dampingRatio*omega;
314 // spring stifness
315 Float k = mass*omega*omega;
317 // magic formulas
318 Float softnesss = FloatNum!(1.0)/(d+timeStep*k);
319 Float biasFactorr = timeStep*k/(d+timeStep*k);
321 for (int i = 0; i < numPlanks; ++i) {
322 auto j = new Joint();
323 j.set(world.bodies[i], world.bodies[i+1], vec2(-FloatNum!(9.125)+FloatNum!(1.25)*i, FloatNum!(5.0)));
324 j.softness = softnesss;
325 j.biasFactor = biasFactorr;
326 world.Add(j);
329 with (auto j = new Joint()) {
330 j.set(world.bodies[numPlanks], world.bodies[0], vec2(-FloatNum!(9.125)+FloatNum!(1.25)*numPlanks, FloatNum!(5.0)));
331 j.softness = softnesss;
332 j.biasFactor = biasFactorr;
333 world.Add(j);
338 // dominos
339 @DemoInfo("Dominos") void Demo8 () {
340 Body b1 = new Body();
341 b1.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
342 b1.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b1.width.y);
343 world.Add(b1);
345 with (auto b = new Body()) {
346 b.set(vec2(FloatNum!(12.0), FloatNum!(0.5)), Float.max);
347 b.position.set(-FloatNum!(1.5), FloatNum!(10.0));
348 world.Add(b);
351 for (int i = 0; i < 10; ++i) {
352 with (auto b = new Body()) {
353 b.set(vec2(FloatNum!(0.2), FloatNum!(2.0)), FloatNum!(10.0));
354 b.position.set(-FloatNum!(6.0)+FloatNum!(1.0)*i, FloatNum!(11.125));
355 b.friction = FloatNum!(0.1);
356 world.Add(b);
360 with (auto b = new Body()) {
361 b.set(vec2(FloatNum!(14.0), FloatNum!(0.5)), Float.max);
362 b.position.set(FloatNum!(1.0), FloatNum!(6.0));
363 b.rotation = FloatNum!(0.3);
364 world.Add(b);
367 Body b2 = new Body();
368 b2.set(vec2(FloatNum!(0.5), FloatNum!(3.0)), Float.max);
369 b2.position.set(-FloatNum!(7.0), FloatNum!(4.0));
370 world.Add(b2);
372 Body b3 = new Body();
373 b3.set(vec2(FloatNum!(12.0), FloatNum!(0.25)), FloatNum!(20.0));
374 b3.position.set(-FloatNum!(0.9), FloatNum!(1.0));
375 world.Add(b3);
377 with (auto j = new Joint()) {
378 j.set(b1, b3, vec2(-FloatNum!(2.0), FloatNum!(1.0)));
379 world.Add(j);
382 Body b4 = new Body();
383 b4.set(vec2(FloatNum!(0.5), FloatNum!(0.5)), FloatNum!(10.0));
384 b4.position.set(-FloatNum!(10.0), FloatNum!(15.0));
385 world.Add(b4);
387 with (auto j = new Joint()) {
388 j.set(b2, b4, vec2(-FloatNum!(7.0), FloatNum!(15.0)));
389 world.Add(j);
392 Body b5 = new Body();
393 b5.set(vec2(FloatNum!(2.0), FloatNum!(2.0)), FloatNum!(20.0));
394 b5.position.set(FloatNum!(6.0), FloatNum!(2.5));
395 b5.friction = FloatNum!(0.1);
396 world.Add(b5);
398 with (auto j = new Joint()) {
399 j.set(b1, b5, vec2(FloatNum!(6.0), FloatNum!(2.6)));
400 world.Add(j);
403 // box cap
404 Body b6 = new Body();
405 b6.set(vec2(FloatNum!(2.0), FloatNum!(0.2)), FloatNum!(10.0));
406 b6.position.set(FloatNum!(6.0), FloatNum!(3.6));
407 world.Add(b6);
409 with (auto j = new Joint()) {
410 j.set(b5, b6, vec2(FloatNum!(7.0), FloatNum!(3.5)));
411 world.Add(j);
416 // a multi-pendulum
417 @DemoInfo("Multi-pendulum") void Demo9 () {
418 Body b1 = new Body();
419 b1.set(vec2(FloatNum!(100.0), FloatNum!(20.0)), Float.max);
420 b1.friction = FloatNum!(0.2);
421 b1.position.set(FloatNum!(0.0), -FloatNum!(0.5)*b1.width.y);
422 b1.rotation = FloatNum!(0.0);
423 world.Add(b1);
425 Float mass = FloatNum!(10.0);
427 // tuning
428 Float frequencyHz = FloatNum!(4.0);
429 Float dampingRatio = FloatNum!(0.7);
431 // frequency in radians
432 Float omega = FloatNum!(2.0)*PI*frequencyHz;
434 // damping coefficient
435 Float d = FloatNum!(2.0)*mass*dampingRatio*omega;
437 // spring stiffness
438 Float k = mass*omega*omega;
440 // magic formulas
441 Float softnesss = FloatNum!(1.0)/(d+timeStep*k);
442 Float biasFactorr = timeStep*k/(d+timeStep*k);
444 const Float y = FloatNum!(12.0);
446 for (int i = 0; i < 15; ++i) {
447 vec2 x = vec2(FloatNum!(0.5)+i, y);
448 auto b = new Body();
449 b.set(vec2(FloatNum!(0.75), FloatNum!(0.25)), mass);
450 b.friction = FloatNum!(0.2);
451 b.position = x;
452 b.rotation = FloatNum!(0.0);
453 world.Add(b);
455 with (auto j = new Joint()) {
456 j.set(b1, b, vec2(i, y));
457 j.softness = softnesss;
458 j.biasFactor = biasFactorr;
459 world.Add(j);
462 b1 = b;
467 // ////////////////////////////////////////////////////////////////////////// //
468 __gshared bool paused = false;
469 __gshared bool slowmo = false;
470 __gshared int slowmocount = 0;
473 void InitDemo (int index) {
474 import std.traits;
475 alias sms = getSymbolsByUDA!(mixin(__MODULE__), DemoInfo);
476 foreach (immutable idx, auto memb; sms) {
477 //pragma(msg, idx, " ", memb);
478 if (index == idx) {
479 //mixin(memb~"();");
480 //{ import std.stdio; writeln(idx); }
481 world.Clear();
482 bomb = null;
483 memb();
484 demoIndex = index;
485 return;
489 foreach (string memb; __traits(allMembers, mixin(__MODULE__))) {
490 static if (is(typeof(__traits(getMember, mixin(__MODULE__), memb)))) {
491 static if (hasUDA!(__traits(getMember, mixin(__MODULE__), memb), DemoInfo)) {
492 pragma(msg, memb);
497 //demoIndex = -1;
501 void SimulationLoop () {
502 //static uint64_t t_start = 0;
503 static double accumulator = 0;
504 //uint64_t t_cur;
505 char[128] buf;
507 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
510 snprintf(buf, sizeof(buf), "Demo %d: %s", demoIndex+1, demos[demoIndex].name);
511 DrawText(5, 15, buf);
512 DrawText(5, 45, "Keys: 1-9 Demos, Space to Launch the Bomb");
514 static char buffer[512];
515 sprintf(buffer, "(A)ccumulation %s", (World::accumulateImpulses ? "ON" : "OFF"));
516 DrawText(5, 75, buffer);
518 sprintf(buffer, "(P)osition Correction %s", (World::positionCorrection ? "ON" : "OFF"));
519 DrawText(5, 105, buffer);
521 sprintf(buffer, "(W)arm Starting %s", (World::warmStarting ? "ON" : "OFF"));
522 DrawText(5, 135, buffer);
525 glMatrixMode(GL_MODELVIEW);
526 glLoadIdentity();
527 glTranslatef(FloatNum!(0.0), -FloatNum!(7.0), -FloatNum!(25.0));
530 t_cur = k8clock_msec(NULL);
531 if (t_start == 0) t_start = t_cur;
533 //world.Step(timeStep);
534 accumulator += (t_cur-t_start)/1000.0;
535 t_start = t_cur;
536 // clamp
537 if (accumulator < 0.0) accumulator = 0.0;
538 else if (accumulator > 0.1) accumulator = 0.1;
539 // process physics
540 while (accumulator >= timeStep) {
541 if (!frameStepping) {
542 world.Step(timeStep);
543 } else {
544 if (canStep) {
545 world.Step(timeStep);
546 canStep = false;
549 accumulator -= timeStep;
552 world.Step(timeStep);
556 void drawWorld () {
557 glMatrixMode(GL_MODELVIEW);
558 glLoadIdentity();
559 glTranslatef(FloatNum!(0.0), -FloatNum!(7.0), -FloatNum!(25.0));
561 // draw world
562 foreach (Body b; world.bodies) b.DrawBody();
563 foreach (Joint j; world.joints) j.DrawJoint();
567 void main () {
568 //setOpenGLContextVersion(3, 2); // up to GLSL 150
569 //openGLContextCompatible = false;
571 auto sdwindow = new SimpleWindow(GWidth, GHeight, "Verlet Physics", OpenGlOptions.yes, Resizablity.fixedSize);
572 //sdwindow.hideCursor();
574 //sdwindow.closeQuery = delegate () { concmd("quit"); };
576 sdwindow.redrawOpenGlScene = delegate () {
577 glClear(GL_COLOR_BUFFER_BIT);
578 drawWorld();
580 world.render();
581 if (dragVertex !is null) {
582 glPointSize(FloatNum!(6.0));
583 glColor3f(FloatNum!(1.0), FloatNum!(1.0), FloatNum!(0.0));
584 glBegin(GL_POINTS);
585 glVertex2f(dragVertex.position.x, dragVertex.position.y);
586 glEnd();
589 //glFlush();
592 sdwindow.visibleForTheFirstTime = delegate () {
593 InitDemo(0);
594 sdwindow.setAsCurrentOpenGlContext(); // make this window active
595 glbindLoadFunctions();
596 // init matrices
598 glMatrixMode(GL_PROJECTION);
599 glLoadIdentity();
600 glOrtho(0, GWidth, GHeight, 0, -1, 1);
601 glMatrixMode(GL_MODELVIEW);
602 glLoadIdentity();
604 glViewport(0, 0, GWidth, GHeight);
605 glMatrixMode(GL_PROJECTION);
606 glLoadIdentity();
607 oglPerspective(45.0, cast(Float)GWidth/cast(Float)GHeight, 0.1, 100.0);
608 sdwindow.redrawOpenGlScene();
611 sdwindow.eventLoop(cast(int)(FloatNum!(1000.0)/FloatNum!(60.0)),
612 delegate () {
613 if (sdwindow.closed || world is null) return;
614 if (!paused) {
615 if (slowmo) {
616 if (--slowmocount < 0) {
617 SimulationLoop();
618 slowmocount = 10;
620 } else {
621 SimulationLoop();
622 slowmocount = 0;
625 sdwindow.redrawOpenGlSceneNow();
627 delegate (KeyEvent event) {
628 if (sdwindow.closed) return;
629 if (!event.pressed) return;
630 switch (event.key) {
631 case Key.Escape: sdwindow.close(); break;
632 default:
635 delegate (MouseEvent event) {
637 if (event.type == MouseEventType.buttonPressed) {
638 if (event.button == MouseButton.left) dragVertex = world.FindVertex(event.x, event.y);
639 if (event.button == MouseButton.right) dragVertex = null;
640 } else if (event.type == MouseEventType.buttonReleased) {
641 if (event.button == MouseButton.left) dragVertex = null;
643 if (dragVertex !is null) dragVertex.position = vec2(cast(Float)event.x, cast(Float)event.y); // sets the position of the dragVertex to the mouse position to drag it around
646 delegate (dchar ch) {
647 if (ch == 'q') { sdwindow.close(); return; }
648 if (ch == 'r') { InitDemo(demoIndex); return; }
649 if (ch == ' ') { paused = !paused; return; }
650 if (ch == 's') { slowmo = !slowmo; return; }
651 if (ch >= '1' && ch <= '9') { InitDemo(ch-'1'); return; }
652 if (ch == '0') { InitDemo(10); return; }
653 if (ch == 'a') { World.accumulateImpulses = !World.accumulateImpulses; return; }
654 if (ch == 'p') { World.positionCorrection = !World.positionCorrection; return; }
655 if (ch == 'w') { World.warmStarting = !World.warmStarting; return; }
656 if (ch == '\n' || ch == '\r') { LaunchBomb(); }