1 module xmain
is aliced
;
10 // Replaces gluPerspective. Sets the frustum to perspective mode.
11 // fovY - Field of vision in degrees in the y direction
12 // aspect - Aspect ratio of the viewport
13 // zNear - The near clipping distance
14 // zFar - The far clipping distance
15 void perspectiveGL (GLdouble fovY
, GLdouble aspect
, GLdouble zNear
, GLdouble zFar
) {
16 import std
.math
: tan
;
17 //Very long (& in theory accurate!) version of Pi. Hopefully an optimizing compiler will replace references to this with the value!
18 enum GLdouble pi
= 3.1415926535897932384626433832795;
19 GLdouble fW
, fH
; // Half of the size of the x and y clipping planes.
20 // Calculate the distance from 0 of the y clipping plane. Basically trig to calculate position of clipper at zNear.
21 // Note: tan( double ) uses radians but OpenGL works in degrees so we convert degrees to radians by dividing by 360 then multiplying by pi.
22 // Formula below corrected by Carsten Jurenz:
23 //fH = tan( (fovY / 2) / 180 * pi ) * zNear;
24 // Which can be reduced to:
25 fH
= tan( fovY
/ 360 * pi
) * zNear
;
26 // Calculate the distance from 0 of the x clipping plane based on the aspect ratio.
28 // Finally call glFrustum, this is all gluPerspective does anyway! This is why we calculate half the distance between the clipping planes
29 // glFrustum takes an offset from zero for each clipping planes distance. (Saves 2 divides)
30 glFrustum( -fW
, fW
, -fH
, fH
, zNear
, zFar
);
43 float timeStep
= 1.0f/60.0f;
45 Vec2 gravity
= Vec2(0.0f, -10.0f);
50 static this () { world
= new World(gravity
, iterations
); }
53 static void LaunchBomb () {
56 bomb
.Set(Vec2(1.0f, 1.0f), 50.0f);
60 bomb
.position
.Set(Random(-15.0f, 15.0f), 15.0f);
61 bomb
.rotation
= Random(-1.5f, 1.5f);
62 bomb
.velocity
= -1.5f*bomb
.position
;
63 bomb
.angularVelocity
= Random(-20.0f, 20.0f);
67 static void DrawBody (Body
body) {
68 auto R
= Mat22(body.rotation
);
69 Vec2 x
= body.position
;
70 Vec2 h
= 0.5f*body.width
;
72 Vec2 v1
= x
+R
*Vec2(-h
.x
, -h
.y
);
73 Vec2 v2
= x
+R
*Vec2( h
.x
, -h
.y
);
74 Vec2 v3
= x
+R
*Vec2( h
.x
, h
.y
);
75 Vec2 v4
= x
+R
*Vec2(-h
.x
, h
.y
);
78 glColor3f(0.4f, 0.9f, 0.4f);
80 glColor3f(0.8f, 0.8f, 0.9f);
83 glBegin(GL_LINE_LOOP
);
84 glVertex2f(v1
.x
, v1
.y
);
85 glVertex2f(v2
.x
, v2
.y
);
86 glVertex2f(v3
.x
, v3
.y
);
87 glVertex2f(v4
.x
, v4
.y
);
92 static void DrawJoint (Joint joint
) {
93 Body b1
= joint
.body1
;
94 Body b2
= joint
.body2
;
96 auto R1
= Mat22(b1
.rotation
);
97 auto R2
= Mat22(b2
.rotation
);
99 Vec2 x1
= b1
.position
;
100 Vec2 p1
= x1
+R1
*joint
.localAnchor1
;
102 Vec2 x2
= b2
.position
;
103 Vec2 p2
= x2
+R2
*joint
.localAnchor2
;
105 glColor3f(0.5f, 0.5f, 0.8f);
107 glVertex2f(x1
.x
, x1
.y
);
108 glVertex2f(p1
.x
, p1
.y
);
109 glVertex2f(x2
.x
, x2
.y
);
110 glVertex2f(p2
.x
, p2
.y
);
115 // ////////////////////////////////////////////////////////////////////////// //
117 struct DemoInfo
{ string descr
; }
119 {"A Single Box", Demo1},
120 {"Simple Pendulum", Demo2},
121 {"Varying Friction Coefficients", Demo3},
122 {"Randomized Stacking", Demo4},
123 {"Pyramid Stacking", Demo5},
125 {"A Suspension Bridge", Demo7},
127 {"Multi-pendulum", Demo9},
131 @DemoInfo("A Single Box") void Demo1 () {
132 with (auto b
= new Body()) {
133 b
.Set(Vec2(100.0f, 20.0f), float.max
);
134 b
.position
.Set(0.0f, -0.5f*b
.width
.y
);
137 with (auto b
= new Body()) {
138 b
.Set(Vec2(1.0f, 1.0f), 200.0f);
139 b
.position
.Set(0.0f, 4.0f);
146 @DemoInfo("Simple Pendulum") void Demo2 () {
147 auto b1
= new Body();
148 b1
.Set(Vec2(100.0f, 20.0f), float.max
);
150 b1
.position
.Set(0.0f, -0.5f*b1
.width
.y
);
154 auto b2
= new Body();
155 b2
.Set(Vec2(1.0f, 1.0f), 100.0f);
157 b2
.position
.Set(9.0f, 11.0f);
161 with (auto j
= new Joint()) {
162 j
.Set(b1
, b2
, Vec2(0.0f, 11.0f));
168 // varying friction coefficients
169 @DemoInfo("Varying Friction Coefficients") void Demo3 () {
170 with (auto b
= new Body()) {
171 b
.Set(Vec2(100.0f, 20.0f), float.max
);
172 b
.position
.Set(0.0f, -0.5f*b
.width
.y
);
176 with (auto b
= new Body()) {
177 b
.Set(Vec2(13.0f, 0.25f), float.max
);
178 b
.position
.Set(-2.0f, 11.0f);
183 with (auto b
= new Body()) {
184 b
.Set(Vec2(0.25f, 1.0f), float.max
);
185 b
.position
.Set(5.25f, 9.5f);
189 with (auto b
= new Body()) {
190 b
.Set(Vec2(13.0f, 0.25f), float.max
);
191 b
.position
.Set(2.0f, 7.0f);
196 with (auto b
= new Body()) {
197 b
.Set(Vec2(0.25f, 1.0f), float.max
);
198 b
.position
.Set(-5.25f, 5.5f);
202 with (auto b
= new Body()) {
203 b
.Set(Vec2(13.0f, 0.25f), float.max
);
204 b
.position
.Set(-2.0f, 3.0f);
209 static immutable float[5] frictions
= [0.75f, 0.5f, 0.35f, 0.1f, 0.0f];
210 for (int i
= 0; i
< 5; ++i
) {
211 with (auto b
= new Body()) {
212 b
.Set(Vec2(0.5f, 0.5f), 25.0f);
213 b
.friction
= frictions
[i
];
214 b
.position
.Set(-7.5f+2.0f*i
, 14.0f);
222 @DemoInfo("Randomized Stacking") void Demo4 () {
223 with (auto b
= new Body()) {
224 b
.Set(Vec2(100.0f, 20.0f), float.max
);
226 b
.position
.Set(0.0f, -0.5f*b
.width
.y
);
231 for (int i
= 0; i
< 10; ++i
) {
232 with (auto b
= new Body()) {
233 b
.Set(Vec2(1.0f, 1.0f), 1.0f);
235 float x
= Random(-0.1f, 0.1f);
236 b
.position
.Set(x
, 0.51f+1.05f*i
);
244 @DemoInfo("Pyramid Stacking") void Demo5 () {
245 with (auto b
= new Body()) {
246 b
.Set(Vec2(100.0f, 20.0f), float.max
);
248 b
.position
.Set(0.0f, -0.5f*b
.width
.y
);
253 Vec2 x
= Vec2(-6.0f, 0.75f);
256 for (int i
= 0; i
< 12; ++i
) {
258 for (int j
= i
; j
< 12; ++j
) {
259 with (auto b
= new Body()) {
260 b
.Set(Vec2(1.0f, 1.0f), 10.0f);
265 y
+= Vec2(1.125f, 0.0f);
267 //x += Vec2(0.5625f, 1.125f);
268 x
+= Vec2(0.5625f, 2.0f);
274 @DemoInfo("A Teeter") void Demo6 () {
275 Body b1
= new Body();
276 b1
.Set(Vec2(100.0f, 20.0f), float.max
);
277 b1
.position
.Set(0.0f, -0.5f*b1
.width
.y
);
280 Body b2
= new Body();
281 b2
.Set(Vec2(12.0f, 0.25f), 100.0f);
282 b2
.position
.Set(0.0f, 1.0f);
285 Body b3
= new Body();
286 b3
.Set(Vec2(0.5f, 0.5f), 25.0f);
287 b3
.position
.Set(-5.0f, 2.0f);
290 Body b4
= new Body();
291 b4
.Set(Vec2(0.5f, 0.5f), 25.0f);
292 b4
.position
.Set(-5.5f, 2.0f);
295 Body b5
= new Body();
296 b5
.Set(Vec2(1.0f, 1.0f), 100.0f);
297 b5
.position
.Set(5.5f, 15.0f);
300 with (auto j
= new Joint()) {
301 j
.Set(b1
, b2
, Vec2(0.0f, 1.0f));
307 // a suspension bridge
308 @DemoInfo("A Suspension Bridge") void Demo7 () {
309 with (auto b
= new Body()) {
310 b
.Set(Vec2(100.0f, 20.0f), float.max
);
312 b
.position
.Set(0.0f, -0.5f*b
.width
.y
);
320 for (int i
= 0; i
< numPlanks
; ++i
) {
322 b
.Set(Vec2(1.0f, 0.25f), mass
);
324 b
.position
.Set(-8.5f+1.25f*i
, 5.0f);
329 float frequencyHz
= 2.0f;
330 float dampingRatio
= 0.7f;
332 // frequency in radians
333 float omega
= 2.0f*k_pi
*frequencyHz
;
335 // damping coefficient
336 float d
= 2.0f*mass
*dampingRatio
*omega
;
339 float k
= mass
*omega
*omega
;
342 float softnesss
= 1.0f/(d
+timeStep
*k
);
343 float biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
345 for (int i
= 0; i
< numPlanks
; ++i
) {
346 auto j
= new Joint();
347 j
.Set(world
.bodies
[i
], world
.bodies
[i
+1], Vec2(-9.125f+1.25f*i
, 5.0f));
348 j
.softness
= softnesss
;
349 j
.biasFactor
= biasFactorr
;
353 with (auto j
= new Joint()) {
354 j
.Set(world
.bodies
[numPlanks
], world
.bodies
[0], Vec2(-9.125f+1.25f*numPlanks
, 5.0f));
355 j
.softness
= softnesss
;
356 j
.biasFactor
= biasFactorr
;
363 @DemoInfo("Dominos") void Demo8 () {
364 Body b1
= new Body();
365 b1
.Set(Vec2(100.0f, 20.0f), float.max
);
366 b1
.position
.Set(0.0f, -0.5f*b1
.width
.y
);
369 with (auto b
= new Body()) {
370 b
.Set(Vec2(12.0f, 0.5f), float.max
);
371 b
.position
.Set(-1.5f, 10.0f);
375 for (int i
= 0; i
< 10; ++i
) {
376 with (auto b
= new Body()) {
377 b
.Set(Vec2(0.2f, 2.0f), 10.0f);
378 b
.position
.Set(-6.0f+1.0f*i
, 11.125f);
384 with (auto b
= new Body()) {
385 b
.Set(Vec2(14.0f, 0.5f), float.max
);
386 b
.position
.Set(1.0f, 6.0f);
391 Body b2
= new Body();
392 b2
.Set(Vec2(0.5f, 3.0f), float.max
);
393 b2
.position
.Set(-7.0f, 4.0f);
396 Body b3
= new Body();
397 b3
.Set(Vec2(12.0f, 0.25f), 20.0f);
398 b3
.position
.Set(-0.9f, 1.0f);
401 with (auto j
= new Joint()) {
402 j
.Set(b1
, b3
, Vec2(-2.0f, 1.0f));
406 Body b4
= new Body();
407 b4
.Set(Vec2(0.5f, 0.5f), 10.0f);
408 b4
.position
.Set(-10.0f, 15.0f);
411 with (auto j
= new Joint()) {
412 j
.Set(b2
, b4
, Vec2(-7.0f, 15.0f));
416 Body b5
= new Body();
417 b5
.Set(Vec2(2.0f, 2.0f), 20.0f);
418 b5
.position
.Set(6.0f, 2.5f);
422 with (auto j
= new Joint()) {
423 j
.Set(b1
, b5
, Vec2(6.0f, 2.6f));
427 Body b6
= new Body();
428 b6
.Set(Vec2(2.0f, 0.2f), 10.0f);
429 b6
.position
.Set(6.0f, 3.6f);
432 with (auto j
= new Joint()) {
433 j
.Set(b5
, b6
, Vec2(7.0f, 3.5f));
440 @DemoInfo("Multi-pendulum") void Demo9 () {
441 Body b1
= new Body();
442 b1
.Set(Vec2(100.0f, 20.0f), float.max
);
444 b1
.position
.Set(0.0f, -0.5f*b1
.width
.y
);
451 float frequencyHz
= 4.0f;
452 float dampingRatio
= 0.7f;
454 // frequency in radians
455 float omega
= 2.0f*k_pi
*frequencyHz
;
457 // damping coefficient
458 float d
= 2.0f*mass
*dampingRatio
*omega
;
461 float k
= mass
*omega
*omega
;
464 float softnesss
= 1.0f/(d
+timeStep
*k
);
465 float biasFactorr
= timeStep
*k
/(d
+timeStep
*k
);
467 const float y
= 12.0f;
469 for (int i
= 0; i
< 15; ++i
) {
470 Vec2 x
= Vec2(0.5f+i
, y
);
472 b
.Set(Vec2(0.75f, 0.25f), mass
);
478 with (auto j
= new Joint()) {
479 j
.Set(b1
, b
, Vec2(i
, y
));
480 j
.softness
= softnesss
;
481 j
.biasFactor
= biasFactorr
;
490 // ////////////////////////////////////////////////////////////////////////// //
491 __gshared
bool paused
= false;
492 __gshared
bool slowmo
= false;
493 __gshared
int slowmocount
= 0;
496 void InitDemo (int index
) {
498 alias sms
= getSymbolsByUDA
!(mixin(__MODULE__
), DemoInfo
);
499 foreach (immutable idx
, auto memb
; sms
) {
500 //pragma(msg, idx, " ", memb);
503 //{ import std.stdio; writeln(idx); }
512 foreach (string memb; __traits(allMembers, mixin(__MODULE__))) {
513 static if (is(typeof(__traits(getMember, mixin(__MODULE__), memb)))) {
514 static if (hasUDA!(__traits(getMember, mixin(__MODULE__), memb), DemoInfo)) {
524 void SimulationLoop () {
525 //static uint64_t t_start = 0;
526 static double accumulator
= 0;
530 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT
);
533 snprintf(buf, sizeof(buf), "Demo %d: %s", demoIndex+1, demos[demoIndex].name);
534 DrawText(5, 15, buf);
535 DrawText(5, 45, "Keys: 1-9 Demos, Space to Launch the Bomb");
537 static char buffer[512];
538 sprintf(buffer, "(A)ccumulation %s", (World::accumulateImpulses ? "ON" : "OFF"));
539 DrawText(5, 75, buffer);
541 sprintf(buffer, "(P)osition Correction %s", (World::positionCorrection ? "ON" : "OFF"));
542 DrawText(5, 105, buffer);
544 sprintf(buffer, "(W)arm Starting %s", (World::warmStarting ? "ON" : "OFF"));
545 DrawText(5, 135, buffer);
548 glMatrixMode(GL_MODELVIEW
);
550 glTranslatef(0.0f, -7.0f, -25.0f);
553 t_cur = k8clock_msec(NULL);
554 if (t_start == 0) t_start = t_cur;
556 //world.Step(timeStep);
557 accumulator += (t_cur-t_start)/1000.0;
560 if (accumulator < 0.0) accumulator = 0.0;
561 else if (accumulator > 0.1) accumulator = 0.1;
563 while (accumulator >= timeStep) {
564 if (!frameStepping) {
565 world.Step(timeStep);
568 world.Step(timeStep);
572 accumulator -= timeStep;
575 world
.Step(timeStep
);
580 glMatrixMode(GL_MODELVIEW
);
582 glTranslatef(0.0f, -7.0f, -25.0f);
585 foreach (Body b
; world
.bodies
) b
.DrawBody();
586 foreach (Joint j
; world
.joints
) j
.DrawJoint();
591 //setOpenGLContextVersion(3, 2); // up to GLSL 150
592 //openGLContextCompatible = false;
594 auto sdwindow
= new SimpleWindow(GWidth
, GHeight
, "Verlet Physics", OpenGlOptions
.yes
, Resizablity
.fixedSize
);
595 //sdwindow.hideCursor();
597 //sdwindow.closeQuery = delegate () { concmd("quit"); };
599 sdwindow
.redrawOpenGlScene
= delegate () {
600 glClear(GL_COLOR_BUFFER_BIT
);
604 if (dragVertex !is null) {
606 glColor3f(1.0f, 1.0f, 0.0f);
608 glVertex2f(dragVertex.position.x, dragVertex.position.y);
615 sdwindow
.visibleForTheFirstTime
= delegate () {
617 sdwindow
.setAsCurrentOpenGlContext(); // make this window active
618 glbindLoadFunctions();
621 glMatrixMode(GL_PROJECTION);
623 glOrtho(0, GWidth, GHeight, 0, -1, 1);
624 glMatrixMode(GL_MODELVIEW);
627 glViewport(0, 0, GWidth
, GHeight
);
628 glMatrixMode(GL_PROJECTION
);
630 perspectiveGL(45.0, cast(float)GWidth
/cast(float)GHeight
, 0.1, 100.0);
631 sdwindow
.redrawOpenGlScene();
634 sdwindow
.eventLoop(cast(int)(1000.0f/60.0f),
636 if (sdwindow
.closed || world
is null) return;
639 if (--slowmocount
< 0) {
648 sdwindow
.redrawOpenGlSceneNow();
650 delegate (KeyEvent event
) {
651 if (sdwindow
.closed
) return;
652 if (!event
.pressed
) return;
654 case Key
.Escape
: sdwindow
.close(); break;
658 delegate (MouseEvent event
) {
660 if (event.type == MouseEventType.buttonPressed) {
661 if (event.button == MouseButton.left) dragVertex = world.FindVertex(event.x, event.y);
662 if (event.button == MouseButton.right) dragVertex = null;
663 } else if (event.type == MouseEventType.buttonReleased) {
664 if (event.button == MouseButton.left) dragVertex = null;
666 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
669 delegate (dchar ch
) {
670 if (ch
== 'q') { sdwindow
.close(); return; }
671 if (ch
== 'r') { InitDemo(demoIndex
); return; }
672 if (ch
== ' ') { paused
= !paused
; return; }
673 if (ch
== 's') { slowmo
= !slowmo
; return; }
674 if (ch
>= '1' && ch
<= '9') { InitDemo(ch
-'1'); return; }
675 if (ch
== '0') { InitDemo(10); return; }
676 if (ch
== 'a') { World
.accumulateImpulses
= !World
.accumulateImpulses
; return; }
677 if (ch
== 'p') { World
.positionCorrection
= !World
.positionCorrection
; return; }
678 if (ch
== 'w') { World
.warmStarting
= !World
.warmStarting
; return; }
679 if (ch
== '\n' || ch
== '\r') { LaunchBomb(); }