Split this out from other changes to keep things sane..
[wxWidgets.git] / samples / opengl / cube / cube.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: cube.cpp
3 // Purpose: wxGLCanvas demo program
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #if defined(__GNUG__) && !defined(__APPLE__)
13 #pragma implementation
14 #pragma interface
15 #endif
16
17 // For compilers that support precompilation, includes "wx.h".
18 #include "wx/wxprec.h"
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef WX_PRECOMP
25 #include "wx/wx.h"
26 #endif
27
28 #include "wx/log.h"
29
30 #if !wxUSE_GLCANVAS
31 #error Please set wxUSE_GLCANVAS to 1 in setup.h.
32 #endif
33
34 #include "cube.h"
35
36 #ifndef __WXMSW__ // for wxStopWatch, see remark below
37 #if defined(__WXMAC__) && !defined(__DARWIN__)
38 #include <utime.h>
39 #include <unistd.h>
40 #else
41 #include <sys/time.h>
42 #include <sys/unistd.h>
43 #endif
44 #else
45 #include <sys/timeb.h>
46 #endif
47
48 #define ID_NEW_WINDOW 10000
49 #define ID_DEF_ROTATE_LEFT_KEY 10001
50 #define ID_DEF_ROTATE_RIGHT_KEY 10002
51
52 /*----------------------------------------------------------
53 Control to get a keycode
54 ----------------------------------------------------------*/
55 class ScanCodeCtrl : public wxTextCtrl
56 {
57 public:
58 ScanCodeCtrl( wxWindow* parent, wxWindowID id, int code,
59 const wxPoint& pos, const wxSize& size );
60 void OnChar( wxKeyEvent& event ) { } /* do nothing */
61 void OnKeyDown(wxKeyEvent& event);
62 private:
63 // any class wishing to process wxWindows events must use this macro
64 DECLARE_EVENT_TABLE()
65 };
66 BEGIN_EVENT_TABLE( ScanCodeCtrl, wxTextCtrl )
67 EVT_CHAR( ScanCodeCtrl::OnChar )
68 EVT_KEY_DOWN( ScanCodeCtrl::OnKeyDown )
69 END_EVENT_TABLE()
70
71 ScanCodeCtrl::ScanCodeCtrl( wxWindow* parent, wxWindowID id, int code,
72 const wxPoint& pos, const wxSize& size )
73 : wxTextCtrl( parent, id, "", pos, size )
74 { wxString buf;
75 buf.Printf( "0x%04x", code );
76 SetValue( buf );
77 }
78
79 void ScanCodeCtrl::OnKeyDown( wxKeyEvent& event )
80 { wxString buf;
81 buf.Printf( "0x%04x", event.GetKeyCode() );
82 SetValue( buf );
83 }
84
85 /*------------------------------------------------------------------
86 Dialog for defining a keypress
87 -------------------------------------------------------------------*/
88
89 class ScanCodeDialog : public wxDialog
90 {
91 public:
92 ScanCodeDialog( wxWindow* parent, wxWindowID id, const int code,
93 const wxString &descr, const wxString& title );
94 int GetValue();
95 private:
96 ScanCodeCtrl *m_ScanCode;
97 wxTextCtrl *m_Description;
98 };
99
100 ScanCodeDialog::ScanCodeDialog( wxWindow* parent, wxWindowID id,
101 const int code, const wxString &descr, const wxString& title )
102 : wxDialog( parent, id, title, wxPoint(-1, -1), wxSize(96*2,76*2) )
103 {
104 new wxStaticText( this, -1, "Scancode", wxPoint(4*2,3*2),
105 wxSize(31*2,12*2) );
106 m_ScanCode = new ScanCodeCtrl( this, -1, code, wxPoint(37*2,6*2),
107 wxSize(53*2,14*2) );
108
109 new wxStaticText( this, -1, "Description", wxPoint(4*2,24*2),
110 wxSize(32*2,12*2) );
111 m_Description = new wxTextCtrl( this, -1, descr, wxPoint(37*2,27*2),
112 wxSize(53*2,14*2) );
113
114 new wxButton( this, wxID_OK, "Ok", wxPoint(20*2,50*2), wxSize(20*2,13*2) );
115 new wxButton( this, wxID_CANCEL, "Cancel", wxPoint(44*2,50*2),
116 wxSize(25*2,13*2) );
117 }
118
119 int ScanCodeDialog::GetValue()
120 {
121 int code;
122 wxString buf = m_ScanCode->GetValue();
123 sscanf( buf.c_str(), "%i", &code );
124 return( code );
125 }
126
127 /*----------------------------------------------------------------------
128 Utility function to get the elapsed time (in msec) since a given point
129 in time (in sec) (because current version of wxGetElapsedTime doesn´t
130 works right with glibc-2.1 and linux, at least for me)
131 -----------------------------------------------------------------------*/
132 unsigned long wxStopWatch( unsigned long *sec_base )
133 {
134 unsigned long secs,msec;
135
136 #if defined(__WXMSW__)
137 struct timeb tb;
138 ftime( &tb );
139 secs = tb.time;
140 msec = tb.millitm;
141 #elif defined(__WXMAC__) && !defined(__DARWIN__)
142 wxLongLong tl = wxGetLocalTimeMillis();
143 secs = (unsigned long) (tl.GetValue() / 1000);
144 msec = (unsigned long) (tl.GetValue() - secs*1000);
145 #else
146 // think every unice has gettimeofday
147 struct timeval tv;
148 gettimeofday( &tv, (struct timezone *)NULL );
149 secs = tv.tv_sec;
150 msec = tv.tv_usec/1000;
151 #endif
152
153 if( *sec_base == 0 )
154 *sec_base = secs;
155
156 return( (secs-*sec_base)*1000 + msec );
157 }
158
159 /*----------------------------------------------------------------
160 Implementation of Test-GLCanvas
161 -----------------------------------------------------------------*/
162
163 BEGIN_EVENT_TABLE(TestGLCanvas, wxGLCanvas)
164 EVT_SIZE(TestGLCanvas::OnSize)
165 EVT_PAINT(TestGLCanvas::OnPaint)
166 EVT_ERASE_BACKGROUND(TestGLCanvas::OnEraseBackground)
167 EVT_KEY_DOWN( TestGLCanvas::OnKeyDown )
168 EVT_KEY_UP( TestGLCanvas::OnKeyUp )
169 EVT_ENTER_WINDOW( TestGLCanvas::OnEnterWindow )
170 END_EVENT_TABLE()
171
172 unsigned long TestGLCanvas::m_secbase = 0;
173 int TestGLCanvas::m_TimeInitialized = 0;
174 unsigned long TestGLCanvas::m_xsynct;
175 unsigned long TestGLCanvas::m_gsynct;
176
177 TestGLCanvas::TestGLCanvas(wxWindow *parent, wxWindowID id,
178 const wxPoint& pos, const wxSize& size, long style, const wxString& name):
179 wxGLCanvas(parent, (wxGLCanvas*) NULL, id, pos, size, style, name )
180 {
181 m_init = FALSE;
182 m_gllist = 0;
183 m_rleft = WXK_LEFT;
184 m_rright = WXK_RIGHT;
185 }
186
187 TestGLCanvas::TestGLCanvas(wxWindow *parent, const TestGLCanvas &other,
188 wxWindowID id, const wxPoint& pos, const wxSize& size, long style,
189 const wxString& name ) :
190 wxGLCanvas(parent, other.GetContext(), id, pos, size, style, name )
191 {
192 m_init = FALSE;
193 m_gllist = other.m_gllist; /* share display list */
194 m_rleft = WXK_LEFT;
195 m_rright = WXK_RIGHT;
196 }
197
198 TestGLCanvas::~TestGLCanvas()
199 {
200 }
201
202 void TestGLCanvas::Render()
203 {
204 wxPaintDC dc(this);
205
206 #ifndef __WXMOTIF__
207 if (!GetContext()) return;
208 #endif
209
210 SetCurrent();
211 /* init OpenGL once, but after SetCurrent */
212 if (!m_init)
213 {
214 InitGL();
215 m_init = TRUE;
216 }
217
218 glMatrixMode(GL_PROJECTION);
219 glLoadIdentity();
220 glFrustum(-0.5F, 0.5F, -0.5F, 0.5F, 1.0F, 3.0F);
221 glMatrixMode(GL_MODELVIEW);
222
223 /* clear color and depth buffers */
224 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
225
226 if( m_gllist == 0 )
227 {
228 m_gllist = glGenLists( 1 );
229 glNewList( m_gllist, GL_COMPILE_AND_EXECUTE );
230 /* draw six faces of a cube */
231 glBegin(GL_QUADS);
232 glNormal3f( 0.0F, 0.0F, 1.0F);
233 glVertex3f( 0.5F, 0.5F, 0.5F); glVertex3f(-0.5F, 0.5F, 0.5F);
234 glVertex3f(-0.5F,-0.5F, 0.5F); glVertex3f( 0.5F,-0.5F, 0.5F);
235
236 glNormal3f( 0.0F, 0.0F,-1.0F);
237 glVertex3f(-0.5F,-0.5F,-0.5F); glVertex3f(-0.5F, 0.5F,-0.5F);
238 glVertex3f( 0.5F, 0.5F,-0.5F); glVertex3f( 0.5F,-0.5F,-0.5F);
239
240 glNormal3f( 0.0F, 1.0F, 0.0F);
241 glVertex3f( 0.5F, 0.5F, 0.5F); glVertex3f( 0.5F, 0.5F,-0.5F);
242 glVertex3f(-0.5F, 0.5F,-0.5F); glVertex3f(-0.5F, 0.5F, 0.5F);
243
244 glNormal3f( 0.0F,-1.0F, 0.0F);
245 glVertex3f(-0.5F,-0.5F,-0.5F); glVertex3f( 0.5F,-0.5F,-0.5F);
246 glVertex3f( 0.5F,-0.5F, 0.5F); glVertex3f(-0.5F,-0.5F, 0.5F);
247
248 glNormal3f( 1.0F, 0.0F, 0.0F);
249 glVertex3f( 0.5F, 0.5F, 0.5F); glVertex3f( 0.5F,-0.5F, 0.5F);
250 glVertex3f( 0.5F,-0.5F,-0.5F); glVertex3f( 0.5F, 0.5F,-0.5F);
251
252 glNormal3f(-1.0F, 0.0F, 0.0F);
253 glVertex3f(-0.5F,-0.5F,-0.5F); glVertex3f(-0.5F,-0.5F, 0.5F);
254 glVertex3f(-0.5F, 0.5F, 0.5F); glVertex3f(-0.5F, 0.5F,-0.5F);
255 glEnd();
256
257 glEndList();
258 }
259 else
260 glCallList( m_gllist );
261
262 glFlush();
263 SwapBuffers();
264 }
265
266 void TestGLCanvas::OnEnterWindow( wxMouseEvent& event )
267 {
268 SetFocus();
269 }
270
271 void TestGLCanvas::OnPaint( wxPaintEvent& event )
272 {
273 Render();
274 }
275
276 void TestGLCanvas::OnSize(wxSizeEvent& event)
277 {
278 // this is also necessary to update the context on some platforms
279 wxGLCanvas::OnSize(event);
280
281 // set GL viewport (not called by wxGLCanvas::OnSize on all platforms...)
282 int w, h;
283 GetClientSize(&w, &h);
284 #ifndef __WXMOTIF__
285 if (GetContext())
286 #endif
287 {
288 SetCurrent();
289 glViewport(0, 0, (GLint) w, (GLint) h);
290 }
291 }
292
293 void TestGLCanvas::OnEraseBackground(wxEraseEvent& event)
294 {
295 // Do nothing, to avoid flashing.
296 }
297
298 void TestGLCanvas::InitGL()
299 {
300 SetCurrent();
301
302 /* set viewing projection */
303 glMatrixMode(GL_PROJECTION);
304 glFrustum(-0.5F, 0.5F, -0.5F, 0.5F, 1.0F, 3.0F);
305
306 /* position viewer */
307 glMatrixMode(GL_MODELVIEW);
308 glTranslatef(0.0F, 0.0F, -2.0F);
309
310 /* position object */
311 glRotatef(30.0F, 1.0F, 0.0F, 0.0F);
312 glRotatef(30.0F, 0.0F, 1.0F, 0.0F);
313
314 glEnable(GL_DEPTH_TEST);
315 glEnable(GL_LIGHTING);
316 glEnable(GL_LIGHT0);
317 }
318
319 GLfloat TestGLCanvas::CalcRotateSpeed( unsigned long acceltime )
320 {
321 GLfloat t,v;
322
323 t = ((GLfloat)acceltime) / 1000.0f;
324
325 if( t < 0.5f )
326 v = t;
327 else if( t < 1.0f )
328 v = t * (2.0f - t);
329 else
330 v = 0.75f;
331
332 return(v);
333 }
334
335 GLfloat TestGLCanvas::CalcRotateAngle( unsigned long lasttime,
336 unsigned long acceltime )
337 {
338 GLfloat t,s1,s2;
339
340 t = ((GLfloat)(acceltime - lasttime)) / 1000.0f;
341 s1 = CalcRotateSpeed( lasttime );
342 s2 = CalcRotateSpeed( acceltime );
343
344 return( t * (s1 + s2) * 135.0f );
345 }
346
347 void TestGLCanvas::Action( long code, unsigned long lasttime,
348 unsigned long acceltime )
349 {
350 GLfloat angle = CalcRotateAngle( lasttime, acceltime );
351
352 if (code == m_rleft)
353 Rotate( angle );
354 else if (code == m_rright)
355 Rotate( -angle );
356 }
357
358 void TestGLCanvas::OnKeyDown( wxKeyEvent& event )
359 {
360 long evkey = event.GetKeyCode();
361 if (evkey == 0) return;
362
363 if (!m_TimeInitialized)
364 {
365 m_TimeInitialized = 1;
366 m_xsynct = event.m_timeStamp;
367 m_gsynct = wxStopWatch(&m_secbase);
368
369 m_Key = evkey;
370 m_StartTime = 0;
371 m_LastTime = 0;
372 m_LastRedraw = 0;
373 }
374
375 unsigned long currTime = event.m_timeStamp - m_xsynct;
376
377 if (evkey != m_Key)
378 {
379 m_Key = evkey;
380 m_LastRedraw = m_StartTime = m_LastTime = currTime;
381 }
382
383 if (currTime >= m_LastRedraw) // Redraw:
384 {
385 Action( m_Key, m_LastTime-m_StartTime, currTime-m_StartTime );
386
387 #if defined(__WXMAC__) && !defined(__DARWIN__)
388 m_LastRedraw = currTime; // wxStopWatch() doesn't work on Mac...
389 #else
390 m_LastRedraw = wxStopWatch(&m_secbase) - m_gsynct;
391 #endif
392 m_LastTime = currTime;
393 }
394
395 event.Skip();
396 }
397
398 void TestGLCanvas::OnKeyUp( wxKeyEvent& event )
399 {
400 m_Key = 0;
401 m_StartTime = 0;
402 m_LastTime = 0;
403 m_LastRedraw = 0;
404
405 event.Skip();
406 }
407
408 void TestGLCanvas::Rotate( GLfloat deg )
409 {
410 SetCurrent();
411
412 glMatrixMode(GL_MODELVIEW);
413 glRotatef((GLfloat)deg, 0.0F, 0.0F, 1.0F);
414 Refresh(FALSE);
415 }
416
417
418 /* -----------------------------------------------------------------------
419 Main Window
420 -------------------------------------------------------------------------*/
421
422 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
423 EVT_MENU(wxID_EXIT, MyFrame::OnExit)
424 EVT_MENU( ID_NEW_WINDOW, MyFrame::OnNewWindow)
425 EVT_MENU( ID_DEF_ROTATE_LEFT_KEY, MyFrame::OnDefRotateLeftKey)
426 EVT_MENU( ID_DEF_ROTATE_RIGHT_KEY, MyFrame::OnDefRotateRightKey)
427 END_EVENT_TABLE()
428
429 // My frame constructor
430 MyFrame::MyFrame(wxFrame *frame, const wxString& title, const wxPoint& pos,
431 const wxSize& size, long style)
432 : wxFrame(frame, -1, title, pos, size, style)
433 {
434 m_canvas = NULL;
435 }
436
437 // Intercept menu commands
438 void MyFrame::OnExit(wxCommandEvent& event)
439 {
440 Destroy();
441 }
442
443 void MyFrame::OnNewWindow(wxCommandEvent& event)
444 {
445 MyFrame *frame = new MyFrame(NULL, "Cube OpenGL Demo Clone",
446 wxPoint(50, 50), wxSize(400, 300));
447 // Give it an icon
448 #ifdef __WXMSW__
449 frame->SetIcon(wxIcon("mondrian"));
450 #endif
451
452 // Make a menubar
453 wxMenu *winMenu = new wxMenu;
454
455 winMenu->Append(wxID_EXIT, "&Close");
456 winMenu->Append(ID_NEW_WINDOW, "&New" );
457 wxMenuBar *menuBar = new wxMenuBar;
458 menuBar->Append(winMenu, "&Window");
459
460 winMenu = new wxMenu;
461 winMenu->Append(ID_DEF_ROTATE_LEFT_KEY, "Rotate &left");
462 winMenu->Append(ID_DEF_ROTATE_RIGHT_KEY, "Rotate &right");
463 menuBar->Append(winMenu, "&Key");
464
465 frame->SetMenuBar(menuBar);
466
467 frame->m_canvas = new TestGLCanvas( frame, *m_canvas, -1,
468 wxDefaultPosition, wxDefaultSize );
469
470 // Show the frame
471 frame->Show(TRUE);
472 }
473
474 void MyFrame::OnDefRotateLeftKey(wxCommandEvent& event)
475 {
476 ScanCodeDialog dial( this, -1, m_canvas->m_rleft,
477 wxString("Left"), "Define key" );
478 int result = dial.ShowModal();
479 if( result == wxID_OK )
480 m_canvas->m_rleft = dial.GetValue();
481 }
482 void MyFrame::OnDefRotateRightKey(wxCommandEvent& event)
483 {
484 ScanCodeDialog dial( this, -1, m_canvas->m_rright,
485 wxString("Right"), "Define key" );
486 int result = dial.ShowModal();
487 if( result == wxID_OK )
488 m_canvas->m_rright = dial.GetValue();
489 }
490
491 /*------------------------------------------------------------------
492 Application object ( equivalent to main() )
493 ------------------------------------------------------------------ */
494
495 IMPLEMENT_APP(MyApp)
496
497 bool MyApp::OnInit(void)
498 {
499 wxLog::SetTraceMask(wxTraceMessages);
500
501 // Create the main frame window
502 MyFrame *frame = new MyFrame(NULL, "Cube OpenGL Demo", wxPoint(50, 50),
503 wxSize(400, 300));
504 // Give it an icon
505 #ifdef wx_msw
506 frame->SetIcon(wxIcon("mondrian"));
507 #endif
508
509 // Make a menubar
510 wxMenu *winMenu = new wxMenu;
511
512 winMenu->Append(wxID_EXIT, "&Close");
513 winMenu->Append(ID_NEW_WINDOW, "&New" );
514 wxMenuBar *menuBar = new wxMenuBar;
515 menuBar->Append(winMenu, "&Window");
516
517 winMenu = new wxMenu;
518 winMenu->Append(ID_DEF_ROTATE_LEFT_KEY, "Rotate &left");
519 winMenu->Append(ID_DEF_ROTATE_RIGHT_KEY, "Rotate &right");
520 menuBar->Append(winMenu, "&Key");
521
522 frame->SetMenuBar(menuBar);
523
524 frame->m_canvas = new TestGLCanvas(frame, -1, wxDefaultPosition, wxDefaultSize);
525
526 // Show the frame
527 frame->Show(TRUE);
528
529 return TRUE;
530 }