1 /*************************************************************************
2 * *
3 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
5 * *
6 * This library is free software; you can redistribute it and/or *
7 * modify it under the terms of EITHER: *
8 * (1) The GNU Lesser General Public License as published by the Free *
9 * Software Foundation; either version 2.1 of the License, or (at *
10 * your option) any later version. The text of the GNU Lesser *
11 * General Public License is included with this library in the *
12 * file LICENSE.TXT. *
13 * (2) The BSD-style license that is included with this library in *
14 * the file LICENSE-BSD.TXT. *
15 * *
16 * This library is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
19 * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
20 * *
21 *************************************************************************/
22
23 #if defined(WIN32) || defined(__CYGWIN__)// this prevents warnings when dependencies built
24 #include <windows.h>
25 #endif
26 #include <process.h>
27 #include <ode/odeconfig.h>
28 #include <GL/gl.h>
29
30 #include "config.h"
31 #include "resource.h"
32 #include "internal.h"
33
34 //***************************************************************************
35 // application globals
36
37 static HINSTANCE ghInstance = 0;
38 static int gnCmdShow = 0;
39 static HACCEL accelerators = 0;
40 static HWND main_window = 0;
41
42 //***************************************************************************
43 // error and message handling
44
errorBox(const char * title,const char * msg,va_list ap)45 static void errorBox (const char *title, const char *msg, va_list ap)
46 {
47 char s[1000];
48 vsprintf (s,msg,ap);
49 MessageBox (0,s,title,MB_OK | MB_APPLMODAL | MB_ICONEXCLAMATION);
50 }
51
52
dsWarning(const char * msg,...)53 static void dsWarning (const char *msg, ...)
54 {
55 va_list ap;
56 va_start (ap,msg);
57 errorBox ("Warning",msg,ap);
58 va_end (ap);
59 }
60
61
dsError(const char * msg,...)62 extern "C" void dsError (const char *msg, ...)
63 {
64 va_list ap;
65 va_start (ap,msg);
66 errorBox ("Error",msg,ap);
67 va_end (ap);
68 exit (1);
69 }
70
71
dsDebug(const char * msg,...)72 extern "C" void dsDebug (const char *msg, ...)
73 {
74 va_list ap;
75 va_start (ap,msg);
76 errorBox ("INTERNAL ERROR",msg,ap);
77 va_end (ap);
78 // *((char *)0) = 0; ... commit SEGVicide ?
79 abort();
80 exit (1); // should never get here, but just in case...
81 }
82
83
dsPrint(const char * msg,...)84 extern "C" void dsPrint (const char *msg, ...)
85 {
86 va_list ap;
87 va_start (ap,msg);
88 vprintf (msg,ap);
89 va_end (ap);
90 }
91
92 //***************************************************************************
93 // rendering thread
94
95 // globals used to communicate with rendering thread
96
97 static volatile int renderer_run = 1;
98 static volatile int renderer_pause = 0; // 0=run, 1=pause
99 static volatile int renderer_ss = 0; // single step command
100 static volatile int renderer_width = 1;
101 static volatile int renderer_height = 1;
102 static dsFunctions *renderer_fn = 0;
103 static volatile HDC renderer_dc = 0;
104 static volatile int keybuffer[16]; // fifo ring buffer for keypresses
105 static volatile int keybuffer_head = 0; // index of next key to put in (modified by GUI)
106 static volatile int keybuffer_tail = 0; // index of next key to take out (modified by renderer)
107
108
setupRendererGlobals()109 static void setupRendererGlobals()
110 {
111 renderer_run = 1;
112 renderer_pause = 0;
113 renderer_ss = 0;
114 renderer_width = 1;
115 renderer_height = 1;
116 renderer_fn = 0;
117 renderer_dc = 0;
118 keybuffer[16];
119 keybuffer_head = 0;
120 keybuffer_tail = 0;
121 }
122
123
renderingThread(LPVOID lpParam)124 static unsigned CALLBACK renderingThread (LPVOID lpParam)
125 {
126 // create openGL context and make it current
127 HGLRC glc = wglCreateContext (renderer_dc);
128 if (glc==NULL) dsError ("could not create OpenGL context");
129 if (wglMakeCurrent (renderer_dc,glc) != TRUE)
130 dsError ("could not make OpenGL context current");
131
132 // test openGL capabilities
133 int maxtsize=0;
134 glGetIntegerv (GL_MAX_TEXTURE_SIZE,&maxtsize);
135 if (maxtsize < 128) dsWarning ("max texture size too small (%dx%d)",
136 maxtsize,maxtsize);
137
138 dsStartGraphics (renderer_width,renderer_height,renderer_fn);
139 if (renderer_fn->start) renderer_fn->start();
140
141 while (renderer_run) {
142 // need to make local copy of renderer_ss to help prevent races
143 int ss = renderer_ss;
144 dsDrawFrame (renderer_width,renderer_height,renderer_fn,
145 renderer_pause && !ss);
146 if (ss) renderer_ss = 0;
147
148 // read keys out of ring buffer and feed them to the command function
149 while (keybuffer_head != keybuffer_tail) {
150 if (renderer_fn->command) renderer_fn->command (keybuffer[keybuffer_tail]);
151 keybuffer_tail = (keybuffer_tail+1) & 15;
152 }
153
154 // swap buffers
155 SwapBuffers (renderer_dc);
156 }
157
158 if (renderer_fn->stop) renderer_fn->stop();
159 dsStopGraphics();
160
161 // delete openGL context
162 wglMakeCurrent (NULL,NULL);
163 wglDeleteContext (glc);
164
165 return 123; // magic value used to test for thread termination
166 }
167
168 //***************************************************************************
169 // window handling
170
171 // callback function for "about" dialog box
172
AboutDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)173 static LRESULT CALLBACK AboutDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam,
174 LPARAM lParam)
175 {
176 switch (uMsg) {
177 case WM_INITDIALOG:
178 return TRUE;
179 case WM_COMMAND:
180 switch (wParam) {
181 case IDOK:
182 EndDialog (hDlg, TRUE);
183 return TRUE;
184 }
185 break;
186 }
187 return FALSE;
188 }
189
190
191 // callback function for the main window
192
mainWndProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)193 static LRESULT CALLBACK mainWndProc (HWND hWnd, UINT msg, WPARAM wParam,
194 LPARAM lParam)
195 {
196 static int button=0,lastx=0,lasty=0;
197 int ctrl = int(wParam & MK_CONTROL);
198
199 switch (msg) {
200 case WM_LBUTTONDOWN:
201 case WM_MBUTTONDOWN:
202 case WM_RBUTTONDOWN:
203 if (msg==WM_LBUTTONDOWN) button |= 1;
204 else if (msg==WM_MBUTTONDOWN) button |= 2;
205 else button |= 4;
206 lastx = SHORT(LOWORD(lParam));
207 lasty = SHORT(HIWORD(lParam));
208 SetCapture (hWnd);
209 break;
210
211 case WM_LBUTTONUP:
212 case WM_MBUTTONUP:
213 case WM_RBUTTONUP:
214 if (msg==WM_LBUTTONUP) button &= ~1;
215 else if (msg==WM_MBUTTONUP) button &= ~2;
216 else button &= ~4;
217 if (button==0) ReleaseCapture();
218 break;
219
220 case WM_MOUSEMOVE: {
221 int x = SHORT(LOWORD(lParam));
222 int y = SHORT(HIWORD(lParam));
223 if (button) dsMotion (button,x-lastx,y-lasty);
224 lastx = x;
225 lasty = y;
226 break;
227 }
228
229 case WM_CHAR: {
230 if (wParam >= ' ' && wParam <= 126) {
231 int nexth = (keybuffer_head+1) & 15;
232 if (nexth != keybuffer_tail) {
233 keybuffer[keybuffer_head] = int(wParam);
234 keybuffer_head = nexth;
235 }
236 }
237 break;
238 }
239
240 case WM_SIZE:
241 // lParam will contain the size of the *client* area!
242 renderer_width = LOWORD(lParam);
243 renderer_height = HIWORD(lParam);
244 break;
245
246 case WM_COMMAND:
247 switch (wParam & 0xffff) {
248 case IDM_ABOUT:
249 DialogBox (ghInstance,MAKEINTRESOURCE(IDD_ABOUT),hWnd,
250 (DLGPROC) AboutDlgProc);
251 break;
252 case IDM_PAUSE: {
253 renderer_pause ^= 1;
254 CheckMenuItem (GetMenu(hWnd),IDM_PAUSE,
255 renderer_pause ? MF_CHECKED : MF_UNCHECKED);
256 if (renderer_pause) renderer_ss = 0;
257 break;
258 }
259 case IDM_SINGLE_STEP: {
260 if (renderer_pause)
261 renderer_ss = 1;
262 else
263 SendMessage( hWnd, WM_COMMAND, IDM_PAUSE, 0 );
264 break;
265 }
266 case IDM_PERF_MONITOR: {
267 dsWarning ("Performance monitor not yet implemented.");
268 break;
269 }
270 case IDM_TEXTURES: {
271 static int tex = 1;
272 tex ^= 1;
273 CheckMenuItem (GetMenu(hWnd),IDM_TEXTURES,
274 tex ? MF_CHECKED : MF_UNCHECKED);
275 dsSetTextures (tex);
276 break;
277 }
278 case IDM_SHADOWS: {
279 static int shadows = 1;
280 shadows ^= 1;
281 CheckMenuItem (GetMenu(hWnd),IDM_SHADOWS,
282 shadows ? MF_CHECKED : MF_UNCHECKED);
283 dsSetShadows (shadows);
284 break;
285 }
286 case IDM_SAVE_SETTINGS: {
287 dsWarning ("\"Save Settings\" not yet implemented.");
288 break;
289 }
290 case IDM_EXIT:
291 PostQuitMessage (0);
292 break;
293 }
294 break;
295
296 case WM_CLOSE:
297 PostQuitMessage (0);
298 break;
299
300 default:
301 return (DefWindowProc (hWnd, msg, wParam, lParam));
302 }
303
304 return 0;
305 }
306
307
308 // this comes from an MSDN example. believe it or not, this is the recommended
309 // way to get the console window handle.
310
GetConsoleHwnd()311 static HWND GetConsoleHwnd()
312 {
313 // the console window title to a "unique" value, then find the window
314 // that has this title.
315 char title[1024];
316 wsprintf (title,"DrawStuff:%d/%d",GetTickCount(),GetCurrentProcessId());
317 SetConsoleTitle (title);
318 Sleep(40); // ensure window title has been updated
319 return FindWindow (NULL,title);
320 }
321
322
drawStuffStartup()323 static void drawStuffStartup()
324 {
325 static int startup_called = 0;
326 if (startup_called) return;
327 startup_called = 1;
328 if (!ghInstance)
329 ghInstance = GetModuleHandleA (NULL);
330 gnCmdShow = SW_SHOWNORMAL; // @@@ fix this later
331
332 // redirect standard I/O to a new console (except on cygwin and mingw)
333 #if !defined(__CYGWIN__) && !defined(__MINGW32__)
334 FreeConsole();
335 if (AllocConsole()==0) dsError ("AllocConsole() failed");
336 if (freopen ("CONIN$","rt",stdin)==0) dsError ("could not open stdin");
337 if (freopen ("CONOUT$","wt",stdout)==0) dsError ("could not open stdout");
338 if (freopen ("CONOUT$","wt",stderr)==0) dsError ("could not open stderr");
339 BringWindowToTop (GetConsoleHwnd());
340 SetConsoleTitle ("DrawStuff Messages");
341 #endif
342
343 // register the window class
344 WNDCLASS wc;
345 wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
346 wc.lpfnWndProc = mainWndProc;
347 wc.cbClsExtra = 0;
348 wc.cbWndExtra = 0;
349 wc.hInstance = ghInstance;
350 wc.hIcon = LoadIcon (NULL,IDI_APPLICATION);
351 wc.hCursor = LoadCursor (NULL,IDC_ARROW);
352 wc.hbrBackground = (HBRUSH) (COLOR_WINDOW+1);
353 wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
354 wc.lpszClassName = "SimAppClass";
355 if (RegisterClass (&wc)==0) dsError ("could not register window class");
356
357 // load accelerators
358 accelerators = LoadAccelerators (ghInstance,
359 MAKEINTRESOURCE(IDR_ACCELERATOR1));
360 if (accelerators==NULL) dsError ("could not load accelerators");
361 }
362
363
dsPlatformSimLoop(int window_width,int window_height,dsFunctions * fn,int initial_pause)364 void dsPlatformSimLoop (int window_width, int window_height,
365 dsFunctions *fn, int initial_pause)
366 {
367 drawStuffStartup();
368 setupRendererGlobals();
369 renderer_pause = initial_pause;
370
371 // create window - but first get window size for desired size of client area.
372 // if this adjustment isn't made then the openGL area will be shifted into
373 // the nonclient area and determining the frame buffer coordinate from the
374 // client area coordinate will be hard.
375 RECT winrect;
376 winrect.left = 50;
377 winrect.top = 80;
378 winrect.right = winrect.left + window_width;
379 winrect.bottom = winrect.top + window_height;
380 DWORD style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
381 AdjustWindowRect (&winrect,style,1);
382 char title[100];
383 sprintf (title,"Simulation test environment v%d.%02d",
384 DS_VERSION >> 8,DS_VERSION & 0xff);
385 main_window = CreateWindow ("SimAppClass",title,style,
386 winrect.left,winrect.top,winrect.right-winrect.left,winrect.bottom-winrect.top,
387 NULL,NULL,ghInstance,NULL);
388 if (main_window==NULL) dsError ("could not create main window");
389 ShowWindow (main_window, gnCmdShow);
390
391 HDC dc = GetDC (main_window); // get DC for this window
392 if (dc==NULL) dsError ("could not get window DC");
393
394 // set pixel format for DC
395
396 PIXELFORMATDESCRIPTOR pfd = {
397 sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
398 1, // version number
399 PFD_DRAW_TO_WINDOW | // support window
400 PFD_SUPPORT_OPENGL | // support OpenGL
401 PFD_DOUBLEBUFFER, // double buffered
402 PFD_TYPE_RGBA, // RGBA type
403 24, // 24-bit color depth
404 0, 0, 0, 0, 0, 0, // color bits ignored
405 0, // no alpha buffer
406 0, // shift bit ignored
407 0, // no accumulation buffer
408 0, 0, 0, 0, // accum bits ignored
409 32, // 32-bit z-buffer
410 0, // no stencil buffer
411 0, // no auxiliary buffer
412 PFD_MAIN_PLANE, // main layer
413 0, // reserved
414 0, 0, 0 // layer masks ignored
415 };
416 // get the best available match of pixel format for the device context
417 int iPixelFormat = ChoosePixelFormat (dc,&pfd);
418 if (iPixelFormat==0)
419 dsError ("could not find a good OpenGL pixel format");
420 // set the pixel format of the device context
421 if (SetPixelFormat (dc,iPixelFormat,&pfd)==FALSE)
422 dsError ("could not set DC pixel format for OpenGL");
423
424 // **********
425 // start the rendering thread
426
427 // set renderer globals
428 renderer_dc = dc;
429 renderer_width = window_width;
430 renderer_height = window_height;
431 renderer_fn = fn;
432
433 unsigned threadId;
434 HANDLE hThread;
435
436 hThread = (HANDLE)_beginthreadex(
437 NULL, // no security attributes
438 0, // use default stack size
439 &renderingThread, // thread function
440 NULL, // argument to thread function
441 0, // use default creation flags
442 &threadId); // returns the thread identifier
443
444 if (hThread==NULL) dsError ("Could not create rendering thread");
445
446 // **********
447 // start GUI message processing
448
449 MSG msg;
450 while (GetMessage (&msg,main_window,0,0)) {
451 if (!TranslateAccelerator (main_window,accelerators,&msg)) {
452 TranslateMessage (&msg);
453 DispatchMessage (&msg);
454 }
455 }
456
457 // terminate rendering thread
458 renderer_run = 0;
459 DWORD ret = WaitForSingleObject (hThread,2000);
460 if (ret==WAIT_TIMEOUT) dsWarning ("Could not kill rendering thread (1)");
461 DWORD exitcode=0;
462 if (!(GetExitCodeThread (hThread,&exitcode) && exitcode == 123))
463 dsWarning ("Could not kill rendering thread (2)");
464 CloseHandle (hThread); // dont need thread handle anymore
465
466 // destroy window
467 DestroyWindow (main_window);
468 }
469
470
dsStop()471 extern "C" void dsStop()
472 {
473 // just calling PostQuitMessage() here wont work, as this function is
474 // typically called from the rendering thread, not the GUI thread.
475 // instead we must post the message to the GUI window explicitly.
476
477 if (main_window) PostMessage (main_window,WM_QUIT,0,0);
478 }
479
480
dsElapsedTime()481 extern "C" double dsElapsedTime()
482 {
483 static double prev=0.0;
484 double curr = timeGetTime()/1000.0;
485 if (!prev)
486 prev=curr;
487 double retval = curr-prev;
488 prev=curr;
489 if (retval>1.0) retval=1.0;
490 if (retval<dEpsilon) retval=dEpsilon;
491 return retval;
492 }
493
494
495 // JPerkins: if running as a DLL, grab my module handle at load time so
496 // I can find the accelerators table later
497
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)498 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
499 {
500 switch (fdwReason)
501 {
502 case DLL_PROCESS_ATTACH:
503 ghInstance = hinstDLL;
504 break;
505 }
506 return TRUE;
507 }
508
509
510 // JPerkins: the new build system can set the entry point of the tests to
511 // main(); this code is no longer necessary
512 /*
513
514 //***************************************************************************
515 // windows entry point
516 //
517 // NOTE: WinMain is not guaranteed to be called with MinGW, because MinGW
518 // always calls main if it is defined and most users of this library will
519 // define their own main. So the startup functionality is kept in
520 // zDriverStartup(), which is also called when dsSimulationLoop() is called.
521
522 extern "C" int main (int argc, char **argv);
523
524
525 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
526 LPSTR lpCmdLine, int nCmdShow)
527 {
528 drawStuffStartup();
529 return main (0,0); // @@@ should really pass cmd line arguments
530 }
531
532 */
533
534
535
536