1 /* Copyright (c) 2007 Scott Lembcke
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to deal
5  * in the Software without restriction, including without limitation the rights
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7  * copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19  * SOFTWARE.
20  */
21 
22 /*
23 	IMPORTANT - READ ME!
24 
25 	This file sets up a simple interface that the individual demos can use to get
26 	a Chipmunk space running and draw what's in it. In order to keep the Chipmunk
27 	examples clean and simple, they contain no graphics code. All drawing is done
28 	by accessing the Chipmunk structures at a very low level. It is NOT
29 	recommended to write a game or application this way as it does not scale
30 	beyond simple shape drawing and is very dependent on implementation details
31 	about Chipmunk which may change with little to no warning.
32 */
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <limits.h>
37 #include <stdarg.h>
38 
39 #include "GL/glew.h"
40 #include "GL/glfw.h"
41 
42 #include "chipmunk/chipmunk_private.h"
43 #include "ChipmunkDemo.h"
44 #include "ChipmunkDemoTextSupport.h"
45 
46 static ChipmunkDemo *demos;
47 static int demo_count = 0;
48 static int demo_index = 'a' - 'a';
49 
50 static cpBool paused = cpFalse;
51 static cpBool step = cpFalse;
52 
53 static cpSpace *space;
54 
55 static double Accumulator = 0.0;
56 static double LastTime = 0.0;
57 int ChipmunkDemoTicks = 0;
58 double ChipmunkDemoTime;
59 
60 cpVect ChipmunkDemoMouse;
61 cpBool ChipmunkDemoRightClick = cpFalse;
62 cpBool ChipmunkDemoRightDown = cpFalse;
63 cpVect ChipmunkDemoKeyboard = {};
64 
65 static cpBody *mouse_body = NULL;
66 static cpConstraint *mouse_joint = NULL;
67 
68 char const *ChipmunkDemoMessageString = NULL;
69 
70 #define GRABBABLE_MASK_BIT (1<<31)
71 cpShapeFilter GRAB_FILTER = {CP_NO_GROUP, GRABBABLE_MASK_BIT, GRABBABLE_MASK_BIT};
72 cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT};
73 
74 cpVect translate = {0, 0};
75 cpFloat scale = 1.0;
76 
ShapeFreeWrap(cpSpace * space,cpShape * shape,void * unused)77 static void ShapeFreeWrap(cpSpace *space, cpShape *shape, void *unused){
78 	cpSpaceRemoveShape(space, shape);
79 	cpShapeFree(shape);
80 }
81 
PostShapeFree(cpShape * shape,cpSpace * space)82 static void PostShapeFree(cpShape *shape, cpSpace *space){
83 	cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ShapeFreeWrap, shape, NULL);
84 }
85 
ConstraintFreeWrap(cpSpace * space,cpConstraint * constraint,void * unused)86 static void ConstraintFreeWrap(cpSpace *space, cpConstraint *constraint, void *unused){
87 	cpSpaceRemoveConstraint(space, constraint);
88 	cpConstraintFree(constraint);
89 }
90 
PostConstraintFree(cpConstraint * constraint,cpSpace * space)91 static void PostConstraintFree(cpConstraint *constraint, cpSpace *space){
92 	cpSpaceAddPostStepCallback(space, (cpPostStepFunc)ConstraintFreeWrap, constraint, NULL);
93 }
94 
BodyFreeWrap(cpSpace * space,cpBody * body,void * unused)95 static void BodyFreeWrap(cpSpace *space, cpBody *body, void *unused){
96 	cpSpaceRemoveBody(space, body);
97 	cpBodyFree(body);
98 }
99 
PostBodyFree(cpBody * body,cpSpace * space)100 static void PostBodyFree(cpBody *body, cpSpace *space){
101 	cpSpaceAddPostStepCallback(space, (cpPostStepFunc)BodyFreeWrap, body, NULL);
102 }
103 
104 // Safe and future proof way to remove and free all objects that have been added to the space.
105 void
ChipmunkDemoFreeSpaceChildren(cpSpace * space)106 ChipmunkDemoFreeSpaceChildren(cpSpace *space)
107 {
108 	// Must remove these BEFORE freeing the body or you will access dangling pointers.
109 	cpSpaceEachShape(space, (cpSpaceShapeIteratorFunc)PostShapeFree, space);
110 	cpSpaceEachConstraint(space, (cpSpaceConstraintIteratorFunc)PostConstraintFree, space);
111 
112 	cpSpaceEachBody(space, (cpSpaceBodyIteratorFunc)PostBodyFree, space);
113 }
114 
115 static void
DrawCircle(cpVect p,cpFloat a,cpFloat r,cpSpaceDebugColor outline,cpSpaceDebugColor fill,cpDataPointer data)116 DrawCircle(cpVect p, cpFloat a, cpFloat r, cpSpaceDebugColor outline, cpSpaceDebugColor fill, cpDataPointer data)
117 {ChipmunkDebugDrawCircle(p, a, r, outline, fill);}
118 
119 static void
DrawSegment(cpVect a,cpVect b,cpSpaceDebugColor color,cpDataPointer data)120 DrawSegment(cpVect a, cpVect b, cpSpaceDebugColor color, cpDataPointer data)
121 {ChipmunkDebugDrawSegment(a, b, color);}
122 
123 static void
DrawFatSegment(cpVect a,cpVect b,cpFloat r,cpSpaceDebugColor outline,cpSpaceDebugColor fill,cpDataPointer data)124 DrawFatSegment(cpVect a, cpVect b, cpFloat r, cpSpaceDebugColor outline, cpSpaceDebugColor fill, cpDataPointer data)
125 {ChipmunkDebugDrawFatSegment(a, b, r, outline, fill);}
126 
127 static void
DrawPolygon(int count,const cpVect * verts,cpFloat r,cpSpaceDebugColor outline,cpSpaceDebugColor fill,cpDataPointer data)128 DrawPolygon(int count, const cpVect *verts, cpFloat r, cpSpaceDebugColor outline, cpSpaceDebugColor fill, cpDataPointer data)
129 {ChipmunkDebugDrawPolygon(count, verts, r, outline, fill);}
130 
131 static void
DrawDot(cpFloat size,cpVect pos,cpSpaceDebugColor color,cpDataPointer data)132 DrawDot(cpFloat size, cpVect pos, cpSpaceDebugColor color, cpDataPointer data)
133 {ChipmunkDebugDrawDot(size, pos, color);}
134 
135 static cpSpaceDebugColor
ColorForShape(cpShape * shape,cpDataPointer data)136 ColorForShape(cpShape *shape, cpDataPointer data)
137 {
138 	if(cpShapeGetSensor(shape)){
139 		return LAColor(1.0f, 0.1f);
140 	} else {
141 		cpBody *body = cpShapeGetBody(shape);
142 
143 		if(cpBodyIsSleeping(body)){
144 			return LAColor(0.2f, 1.0f);
145 		} else if(body->sleeping.idleTime > shape->space->sleepTimeThreshold) {
146 			return LAColor(0.66f, 1.0f);
147 		} else {
148 			uint32_t val = (uint32_t)shape->hashid;
149 
150 			// scramble the bits up using Robert Jenkins' 32 bit integer hash function
151 			val = (val+0x7ed55d16) + (val<<12);
152 			val = (val^0xc761c23c) ^ (val>>19);
153 			val = (val+0x165667b1) + (val<<5);
154 			val = (val+0xd3a2646c) ^ (val<<9);
155 			val = (val+0xfd7046c5) + (val<<3);
156 			val = (val^0xb55a4f09) ^ (val>>16);
157 
158 			GLfloat r = (GLfloat)((val>>0) & 0xFF);
159 			GLfloat g = (GLfloat)((val>>8) & 0xFF);
160 			GLfloat b = (GLfloat)((val>>16) & 0xFF);
161 
162 			GLfloat max = (GLfloat)cpfmax(cpfmax(r, g), b);
163 			GLfloat min = (GLfloat)cpfmin(cpfmin(r, g), b);
164 			GLfloat intensity = (cpBodyGetType(body) == CP_BODY_TYPE_STATIC ? 0.15f : 0.75f);
165 
166 			// Saturate and scale the color
167 			if(min == max){
168 				return RGBAColor(intensity, 0.0f, 0.0f, 1.0f);
169 			} else {
170 				GLfloat coef = (GLfloat)intensity/(max - min);
171 				return RGBAColor(
172 					(r - min)*coef,
173 					(g - min)*coef,
174 					(b - min)*coef,
175 					1.0f
176 				);
177 			}
178 		}
179 	}
180 }
181 
182 
183 void
ChipmunkDemoDefaultDrawImpl(cpSpace * space)184 ChipmunkDemoDefaultDrawImpl(cpSpace *space)
185 {
186 	cpSpaceDebugDrawOptions drawOptions = {
187 		DrawCircle,
188 		DrawSegment,
189 		DrawFatSegment,
190 		DrawPolygon,
191 		DrawDot,
192 
193 		(cpSpaceDebugDrawFlags)(CP_SPACE_DEBUG_DRAW_SHAPES | CP_SPACE_DEBUG_DRAW_CONSTRAINTS | CP_SPACE_DEBUG_DRAW_COLLISION_POINTS),
194 
195 		{200.0f/255.0f, 210.0f/255.0f, 230.0f/255.0f, 1.0f},
196 		ColorForShape,
197 		{0.0f, 0.75f, 0.0f, 1.0f},
198 		{1.0f, 0.0f, 0.0f, 1.0f},
199 		NULL,
200 	};
201 
202 	cpSpaceDebugDraw(space, &drawOptions);
203 }
204 
205 static void
DrawInstructions()206 DrawInstructions()
207 {
208 	ChipmunkDemoTextDrawString(cpv(-300, 220),
209 		"Controls:\n"
210 		"A - * Switch demos. (return restarts)\n"
211 		"Use the mouse to grab objects.\n"
212 	);
213 }
214 
215 static int max_arbiters = 0;
216 static int max_points = 0;
217 static int max_constraints = 0;
218 
219 static void
DrawInfo()220 DrawInfo()
221 {
222 	int arbiters = space->arbiters->num;
223 	int points = 0;
224 
225 	for(int i=0; i<arbiters; i++)
226 		points += ((cpArbiter *)(space->arbiters->arr[i]))->count;
227 
228 	int constraints = (space->constraints->num + points)*space->iterations;
229 
230 	max_arbiters = arbiters > max_arbiters ? arbiters : max_arbiters;
231 	max_points = points > max_points ? points : max_points;
232 	max_constraints = constraints > max_constraints ? constraints : max_constraints;
233 
234 	char buffer[1024];
235 	const char *format =
236 		"Arbiters: %d (%d) - "
237 		"Contact Points: %d (%d)\n"
238 		"Other Constraints: %d, Iterations: %d\n"
239 		"Constraints x Iterations: %d (%d)\n"
240 		"Time:% 5.2fs, KE:% 5.2e";
241 
242 	cpArray *bodies = space->dynamicBodies;
243 	cpFloat ke = 0.0f;
244 	for(int i=0; i<bodies->num; i++){
245 		cpBody *body = (cpBody *)bodies->arr[i];
246 		if(body->m == INFINITY || body->i == INFINITY) continue;
247 
248 		ke += body->m*cpvdot(body->v, body->v) + body->i*body->w*body->w;
249 	}
250 
251 	sprintf(buffer, format,
252 		arbiters, max_arbiters,
253 		points, max_points,
254 		space->constraints->num, space->iterations,
255 		constraints, max_constraints,
256 		ChipmunkDemoTime, (ke < 1e-10f ? 0.0f : ke)
257 	);
258 
259 	ChipmunkDemoTextDrawString(cpv(0, 220), buffer);
260 }
261 
262 static char PrintStringBuffer[1024*8];
263 static char *PrintStringCursor;
264 
265 void
ChipmunkDemoPrintString(char const * fmt,...)266 ChipmunkDemoPrintString(char const *fmt, ...)
267 {
268 	ChipmunkDemoMessageString = PrintStringBuffer;
269 
270 	va_list args;
271 	va_start(args, fmt);
272 	// TODO: should use vsnprintf here
273 	PrintStringCursor += vsprintf(PrintStringCursor, fmt, args);
274 	va_end(args);
275 }
276 
277 static void
Tick(double dt)278 Tick(double dt)
279 {
280 	if(!paused || step){
281 		PrintStringBuffer[0] = 0;
282 		PrintStringCursor = PrintStringBuffer;
283 
284 		// Completely reset the renderer only at the beginning of a tick.
285 		// That way it can always display at least the last ticks' debug drawing.
286 		ChipmunkDebugDrawClearRenderer();
287 		ChipmunkDemoTextClearRenderer();
288 
289 		cpVect new_point = cpvlerp(mouse_body->p, ChipmunkDemoMouse, 0.25f);
290 		mouse_body->v = cpvmult(cpvsub(new_point, mouse_body->p), 60.0f);
291 		mouse_body->p = new_point;
292 
293 		demos[demo_index].updateFunc(space, dt);
294 
295 		ChipmunkDemoTicks++;
296 		ChipmunkDemoTime += dt;
297 
298 		step = cpFalse;
299 		ChipmunkDemoRightDown = cpFalse;
300 
301 		ChipmunkDemoTextDrawString(cpv(-300, -200), ChipmunkDemoMessageString);
302 	}
303 }
304 
305 static void
Update(void)306 Update(void)
307 {
308 	double time = glfwGetTime();
309 	double dt = time - LastTime;
310 	if(dt > 0.2) dt = 0.2;
311 
312 	double fixed_dt = demos[demo_index].timestep;
313 
314 	for(Accumulator += dt; Accumulator > fixed_dt; Accumulator -= fixed_dt){
315 		Tick(fixed_dt);
316 	}
317 
318 	LastTime = time;
319 }
320 
321 static void
Display(void)322 Display(void)
323 {
324 	glMatrixMode(GL_MODELVIEW);
325 	glLoadIdentity();
326 	glTranslatef((GLfloat)translate.x, (GLfloat)translate.y, 0.0f);
327 	glScalef((GLfloat)scale, (GLfloat)scale, 1.0f);
328 
329 	Update();
330 
331 	ChipmunkDebugDrawPushRenderer();
332 	demos[demo_index].drawFunc(space);
333 
334 //	// Highlight the shape under the mouse because it looks neat.
335 //	cpShape *nearest = cpSpacePointQueryNearest(space, ChipmunkDemoMouse, 0.0f, CP_ALL_LAYERS, CP_NO_GROUP, NULL);
336 //	if(nearest) ChipmunkDebugDrawShape(nearest, RGBAColor(1.0f, 0.0f, 0.0f, 1.0f), LAColor(0.0f, 0.0f));
337 
338 	// Draw the renderer contents and reset it back to the last tick's state.
339 	ChipmunkDebugDrawFlushRenderer();
340 	ChipmunkDebugDrawPopRenderer();
341 
342 	ChipmunkDemoTextPushRenderer();
343 	// Now render all the UI text.
344 	DrawInstructions();
345 	DrawInfo();
346 
347 	glMatrixMode(GL_MODELVIEW);
348 	glPushMatrix(); {
349 		// Draw the text at fixed positions,
350 		// but save the drawing matrix for the mouse picking
351 		glLoadIdentity();
352 
353 		ChipmunkDemoTextFlushRenderer();
354 		ChipmunkDemoTextPopRenderer();
355 	} glPopMatrix();
356 
357 	glfwSwapBuffers();
358 	glClear(GL_COLOR_BUFFER_BIT);
359 }
360 
361 static void
Reshape(int width,int height)362 Reshape(int width, int height)
363 {
364 	glViewport(0, 0, width, height);
365 
366 	float scale = (float)cpfmin(width/640.0, height/480.0);
367 	float hw = width*(0.5f/scale);
368 	float hh = height*(0.5f/scale);
369 
370 	ChipmunkDebugDrawPointLineScale = scale;
371 	glLineWidth((GLfloat)scale);
372 
373 	glMatrixMode(GL_PROJECTION);
374 	glLoadIdentity();
375 	gluOrtho2D(-hw, hw, -hh, hh);
376 }
377 
378 static char *
DemoTitle(int index)379 DemoTitle(int index)
380 {
381 	static char title[1024];
382 	sprintf(title, "Demo(%c): %s", 'a' + index, demos[demo_index].name);
383 
384 	return title;
385 }
386 
387 static void
RunDemo(int index)388 RunDemo(int index)
389 {
390 	srand(45073);
391 
392 	demo_index = index;
393 
394 	ChipmunkDemoTicks = 0;
395 	ChipmunkDemoTime = 0.0;
396 	Accumulator = 0.0;
397 	LastTime = glfwGetTime();
398 
399 	mouse_joint = NULL;
400 	ChipmunkDemoMessageString = "";
401 	max_arbiters = 0;
402 	max_points = 0;
403 	max_constraints = 0;
404 	space = demos[demo_index].initFunc();
405 
406 	glfwSetWindowTitle(DemoTitle(index));
407 }
408 
409 static void
Keyboard(int key,int state)410 Keyboard(int key, int state)
411 {
412 	if(state == GLFW_RELEASE) return;
413 
414 	int index = key - 'a';
415 
416 	if(0 <= index && index < demo_count){
417 		demos[demo_index].destroyFunc(space);
418 		RunDemo(index);
419 	} else if(key == ' '){
420 		demos[demo_index].destroyFunc(space);
421 		RunDemo(demo_index);
422   } else if(key == '`'){
423 		paused = !paused;
424   } else if(key == '1'){
425 		step = cpTrue;
426 	} else if(key == '\\'){
427 		glDisable(GL_LINE_SMOOTH);
428 		glDisable(GL_POINT_SMOOTH);
429 	}
430 
431 	GLfloat translate_increment = 50.0f/(GLfloat)scale;
432 	GLfloat scale_increment = 1.2f;
433 	if(key == '5'){
434 		translate.x = 0.0f;
435 		translate.y = 0.0f;
436 		scale = 1.0f;
437 	}else if(key == '4'){
438 		translate.x += translate_increment;
439 	}else if(key == '6'){
440 		translate.x -= translate_increment;
441 	}else if(key == '2'){
442 		translate.y += translate_increment;
443 	}else if(key == '8'){
444 		translate.y -= translate_increment;
445 	}else if(key == '7'){
446 		scale /= scale_increment;
447 	}else if(key == '9'){
448 		scale *= scale_increment;
449 	}
450 }
451 
452 static cpVect
MouseToSpace(int x,int y)453 MouseToSpace(int x, int y)
454 {
455 	GLdouble model[16];
456 	glGetDoublev(GL_MODELVIEW_MATRIX, model);
457 
458 	GLdouble proj[16];
459 	glGetDoublev(GL_PROJECTION_MATRIX, proj);
460 
461 	GLint view[4];
462 	glGetIntegerv(GL_VIEWPORT, view);
463 
464 	int ww, wh;
465 	glfwGetWindowSize(&ww, &wh);
466 
467 	GLdouble mx, my, mz;
468 	gluUnProject(x, wh - y, 0.0f, model, proj, view, &mx, &my, &mz);
469 
470 	return cpv(mx, my);
471 }
472 
473 static void
Mouse(int x,int y)474 Mouse(int x, int y)
475 {
476 	ChipmunkDemoMouse = MouseToSpace(x, y);
477 }
478 
479 static void
Click(int button,int state)480 Click(int button, int state)
481 {
482 	if(button == GLFW_MOUSE_BUTTON_1){
483 		if(state == GLFW_PRESS){
484 			// give the mouse click a little radius to make it easier to click small shapes.
485 			cpFloat radius = 5.0;
486 
487 			cpPointQueryInfo info = {};
488 			cpShape *shape = cpSpacePointQueryNearest(space, ChipmunkDemoMouse, radius, GRAB_FILTER, &info);
489 
490 			if(shape && cpBodyGetMass(cpShapeGetBody(shape)) < INFINITY){
491 				// Use the closest point on the surface if the click is outside of the shape.
492 				cpVect nearest = (info.distance > 0.0f ? info.point : ChipmunkDemoMouse);
493 
494 				cpBody *body = cpShapeGetBody(shape);
495 				mouse_joint = cpPivotJointNew2(mouse_body, body, cpvzero, cpBodyWorldToLocal(body, nearest));
496 				mouse_joint->maxForce = 50000.0f;
497 				mouse_joint->errorBias = cpfpow(1.0f - 0.15f, 60.0f);
498 				cpSpaceAddConstraint(space, mouse_joint);
499 			}
500 		} else if(mouse_joint){
501 			cpSpaceRemoveConstraint(space, mouse_joint);
502 			cpConstraintFree(mouse_joint);
503 			mouse_joint = NULL;
504 		}
505 	} else if(button == GLFW_MOUSE_BUTTON_2){
506 		ChipmunkDemoRightDown = ChipmunkDemoRightClick = (state == GLFW_PRESS);
507 	}
508 }
509 
510 static void
SpecialKeyboard(int key,int state)511 SpecialKeyboard(int key, int state)
512 {
513 	switch(key){
514 		case GLFW_KEY_UP   : ChipmunkDemoKeyboard.y += (state == GLFW_PRESS ?  1.0 : -1.0); break;
515 		case GLFW_KEY_DOWN : ChipmunkDemoKeyboard.y += (state == GLFW_PRESS ? -1.0 :  1.0); break;
516 		case GLFW_KEY_RIGHT: ChipmunkDemoKeyboard.x += (state == GLFW_PRESS ?  1.0 : -1.0); break;
517 		case GLFW_KEY_LEFT : ChipmunkDemoKeyboard.x += (state == GLFW_PRESS ? -1.0 :  1.0); break;
518 		default: break;
519 	}
520 }
521 
522 static int
WindowClose()523 WindowClose()
524 {
525 	glfwTerminate();
526 	exit(EXIT_SUCCESS);
527 
528 	return GL_TRUE;
529 }
530 
531 static void
SetupGL(void)532 SetupGL(void)
533 {
534 	glewExperimental = GL_TRUE;
535 	cpAssertHard(glewInit() == GLEW_NO_ERROR, "There was an error initializing GLEW.");
536 	cpAssertHard(GLEW_ARB_vertex_array_object, "Requires VAO support.");
537 
538 	ChipmunkDebugDrawInit();
539 	ChipmunkDemoTextInit();
540 
541 	glClearColor(52.0f/255.0f, 62.0f/255.0f, 72.0f/255.0f, 1.0f);
542 	glClear(GL_COLOR_BUFFER_BIT);
543 
544 	glEnable(GL_LINE_SMOOTH);
545 	glEnable(GL_POINT_SMOOTH);
546 
547 	glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
548 	glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
549 
550 	glEnable(GL_BLEND);
551 	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
552 }
553 
554 static void
SetupGLFW()555 SetupGLFW()
556 {
557 	cpAssertHard(glfwInit(), "Error initializing GLFW.");
558 
559 	cpAssertHard(glfwOpenWindow(640, 480, 8, 8, 8, 8, 0, 0, GLFW_WINDOW), "Error opening GLFW window.");
560 	glfwSetWindowTitle(DemoTitle(demo_index));
561 	glfwSwapInterval(1);
562 
563 	SetupGL();
564 
565 	glfwSetWindowSizeCallback(Reshape);
566 	glfwSetWindowCloseCallback(WindowClose);
567 
568 	glfwSetCharCallback(Keyboard);
569 	glfwSetKeyCallback(SpecialKeyboard);
570 
571 	glfwSetMousePosCallback(Mouse);
572 	glfwSetMouseButtonCallback(Click);
573 }
574 
575 static void
TimeTrial(int index,int count)576 TimeTrial(int index, int count)
577 {
578 	space = demos[index].initFunc();
579 
580 	double start_time = glfwGetTime();
581 	double dt = demos[index].timestep;
582 
583 	for(int i=0; i<count; i++)
584 		demos[index].updateFunc(space, dt);
585 
586 	double end_time = glfwGetTime();
587 
588 	demos[index].destroyFunc(space);
589 
590 	printf("Time(%c) = %8.2f ms (%s)\n", index + 'a', (end_time - start_time)*1e3f, demos[index].name);
591 	fflush(stdout);
592 }
593 
594 extern ChipmunkDemo LogoSmash;
595 extern ChipmunkDemo PyramidStack;
596 extern ChipmunkDemo Plink;
597 extern ChipmunkDemo BouncyHexagons;
598 extern ChipmunkDemo Tumble;
599 extern ChipmunkDemo PyramidTopple;
600 extern ChipmunkDemo Planet;
601 extern ChipmunkDemo Springies;
602 extern ChipmunkDemo Pump;
603 extern ChipmunkDemo TheoJansen;
604 extern ChipmunkDemo Query;
605 extern ChipmunkDemo OneWay;
606 extern ChipmunkDemo Player;
607 extern ChipmunkDemo Joints;
608 extern ChipmunkDemo Tank;
609 extern ChipmunkDemo Chains;
610 extern ChipmunkDemo Crane;
611 extern ChipmunkDemo Buoyancy;
612 extern ChipmunkDemo ContactGraph;
613 extern ChipmunkDemo Slice;
614 extern ChipmunkDemo Convex;
615 extern ChipmunkDemo Unicycle;
616 extern ChipmunkDemo Sticky;
617 extern ChipmunkDemo Shatter;
618 extern ChipmunkDemo GJK;
619 
620 extern ChipmunkDemo bench_list[];
621 extern int bench_count;
622 
623 int
main(int argc,const char ** argv)624 main(int argc, const char **argv)
625 {
626 	ChipmunkDemo demo_list[] = {
627 		LogoSmash,//A
628 		PyramidStack,//B
629 		Plink,//C
630 		BouncyHexagons,//D
631 		Tumble,//E
632 		PyramidTopple,//F
633 		Planet,//G
634 		Springies,//H
635 		Pump,//I
636 		TheoJansen,//J
637 		Query,//K
638 		OneWay,//L
639 		Joints,//M
640 		Tank,//N
641 		Chains,//O
642 		Crane,//P
643 		ContactGraph,//Q
644 		Buoyancy,//R
645 		Player,//S
646 		Slice,//T
647 		Convex,//U
648 		Unicycle,//V
649 		Sticky,//W
650 		Shatter,//X
651 	};
652 
653 	demos = demo_list;
654 	demo_count = sizeof(demo_list)/sizeof(ChipmunkDemo);
655 	int trial = 0;
656 
657 	for(int i=0; i<argc; i++){
658 		if(strcmp(argv[i], "-bench") == 0){
659 			demos = bench_list;
660 			demo_count = bench_count;
661 		} else if(strcmp(argv[i], "-trial") == 0){
662 			trial = 1;
663 		}
664 	}
665 
666 	if(trial){
667 		cpAssertHard(glfwInit(), "Error initializing GLFW.");
668 //		sleep(1);
669 		for(int i=0; i<demo_count; i++) TimeTrial(i, 1000);
670 //		time_trial('d' - 'a', 10000);
671 		exit(0);
672 	} else {
673 		mouse_body = cpBodyNewKinematic();
674 
675 		RunDemo(demo_index);
676 		SetupGLFW();
677 
678 		while(1){
679 			Display();
680 		}
681 	}
682 
683 	return 0;
684 }
685