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