]> git.saurik.com Git - wxWidgets.git/blob - samples/opengl/cube/cube.cpp
cda5cca7616d11ea7b597a391b7c85f9e8827c9a
[wxWidgets.git] / samples / opengl / cube / cube.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: cube.cpp
3 // Purpose: wxGLCanvas demo program
4 // Author: Julian Smart
5 // Modified by: Vadim Zeitlin to use new wxGLCanvas API (2007-04-09)
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 ///////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #ifdef __BORLANDC__
24 #pragma hdrstop
25 #endif
26
27 #ifndef WX_PRECOMP
28 #include "wx/wx.h"
29 #endif
30
31 #if !wxUSE_GLCANVAS
32 #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library"
33 #endif
34
35 #include "cube.h"
36
37 #ifndef wxHAS_IMAGES_IN_RESOURCES
38 #include "../../sample.xpm"
39 #endif
40
41 // ----------------------------------------------------------------------------
42 // constants
43 // ----------------------------------------------------------------------------
44
45 // control ids
46 enum
47 {
48 SpinTimer = wxID_HIGHEST + 1
49 };
50
51 // ----------------------------------------------------------------------------
52 // helper functions
53 // ----------------------------------------------------------------------------
54
55 static void CheckGLError()
56 {
57 GLenum errLast = GL_NO_ERROR;
58
59 for ( ;; )
60 {
61 GLenum err = glGetError();
62 if ( err == GL_NO_ERROR )
63 return;
64
65 // normally the error is reset by the call to glGetError() but if
66 // glGetError() itself returns an error, we risk looping forever here
67 // so check that we get a different error than the last time
68 if ( err == errLast )
69 {
70 wxLogError(wxT("OpenGL error state couldn't be reset."));
71 return;
72 }
73
74 errLast = err;
75
76 wxLogError(wxT("OpenGL error %d"), err);
77 }
78 }
79
80 // function to draw the texture for cube faces
81 static wxImage DrawDice(int size, unsigned num)
82 {
83 wxASSERT_MSG( num >= 1 && num <= 6, wxT("invalid dice index") );
84
85 const int dot = size/16; // radius of a single dot
86 const int gap = 5*size/32; // gap between dots
87
88 wxBitmap bmp(size, size);
89 wxMemoryDC dc;
90 dc.SelectObject(bmp);
91 dc.SetBackground(*wxWHITE_BRUSH);
92 dc.Clear();
93 dc.SetBrush(*wxBLACK_BRUSH);
94
95 // the upper left and lower right points
96 if ( num != 1 )
97 {
98 dc.DrawCircle(gap + dot, gap + dot, dot);
99 dc.DrawCircle(size - gap - dot, size - gap - dot, dot);
100 }
101
102 // draw the central point for odd dices
103 if ( num % 2 )
104 {
105 dc.DrawCircle(size/2, size/2, dot);
106 }
107
108 // the upper right and lower left points
109 if ( num > 3 )
110 {
111 dc.DrawCircle(size - gap - dot, gap + dot, dot);
112 dc.DrawCircle(gap + dot, size - gap - dot, dot);
113 }
114
115 // finally those 2 are only for the last dice
116 if ( num == 6 )
117 {
118 dc.DrawCircle(gap + dot, size/2, dot);
119 dc.DrawCircle(size - gap - dot, size/2, dot);
120 }
121
122 dc.SelectObject(wxNullBitmap);
123
124 return bmp.ConvertToImage();
125 }
126
127 // ============================================================================
128 // implementation
129 // ============================================================================
130
131 // ----------------------------------------------------------------------------
132 // TestGLContext
133 // ----------------------------------------------------------------------------
134
135 TestGLContext::TestGLContext(wxGLCanvas *canvas)
136 : wxGLContext(canvas)
137 {
138 SetCurrent(*canvas);
139
140 // set up the parameters we want to use
141 glEnable(GL_CULL_FACE);
142 glEnable(GL_DEPTH_TEST);
143 glEnable(GL_LIGHTING);
144 glEnable(GL_LIGHT0);
145 glEnable(GL_TEXTURE_2D);
146
147 // add slightly more light, the default lighting is rather dark
148 GLfloat ambient[] = { 0.5, 0.5, 0.5, 0.5 };
149 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
150
151 // set viewing projection
152 glMatrixMode(GL_PROJECTION);
153 glLoadIdentity();
154 glFrustum(-0.5f, 0.5f, -0.5f, 0.5f, 1.0f, 3.0f);
155
156 // create the textures to use for cube sides: they will be reused by all
157 // canvases (which is probably not critical in the case of simple textures
158 // we use here but could be really important for a real application where
159 // each texture could take many megabytes)
160 glGenTextures(WXSIZEOF(m_textures), m_textures);
161
162 for ( unsigned i = 0; i < WXSIZEOF(m_textures); i++ )
163 {
164 glBindTexture(GL_TEXTURE_2D, m_textures[i]);
165
166 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
167 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
168 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
169 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
171
172 const wxImage img(DrawDice(256, i + 1));
173
174 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
175 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(),
176 0, GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
177 }
178
179 CheckGLError();
180 }
181
182 void TestGLContext::DrawRotatedCube(float xangle, float yangle)
183 {
184 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
185
186 glMatrixMode(GL_MODELVIEW);
187 glLoadIdentity();
188 glTranslatef(0.0f, 0.0f, -2.0f);
189 glRotatef(xangle, 1.0f, 0.0f, 0.0f);
190 glRotatef(yangle, 0.0f, 1.0f, 0.0f);
191
192 // draw six faces of a cube of size 1 centered at (0, 0, 0)
193 glBindTexture(GL_TEXTURE_2D, m_textures[0]);
194 glBegin(GL_QUADS);
195 glNormal3f( 0.0f, 0.0f, 1.0f);
196 glTexCoord2f(0, 0); glVertex3f( 0.5f, 0.5f, 0.5f);
197 glTexCoord2f(1, 0); glVertex3f(-0.5f, 0.5f, 0.5f);
198 glTexCoord2f(1, 1); glVertex3f(-0.5f,-0.5f, 0.5f);
199 glTexCoord2f(0, 1); glVertex3f( 0.5f,-0.5f, 0.5f);
200 glEnd();
201
202 glBindTexture(GL_TEXTURE_2D, m_textures[1]);
203 glBegin(GL_QUADS);
204 glNormal3f( 0.0f, 0.0f,-1.0f);
205 glTexCoord2f(0, 0); glVertex3f(-0.5f,-0.5f,-0.5f);
206 glTexCoord2f(1, 0); glVertex3f(-0.5f, 0.5f,-0.5f);
207 glTexCoord2f(1, 1); glVertex3f( 0.5f, 0.5f,-0.5f);
208 glTexCoord2f(0, 1); glVertex3f( 0.5f,-0.5f,-0.5f);
209 glEnd();
210
211 glBindTexture(GL_TEXTURE_2D, m_textures[2]);
212 glBegin(GL_QUADS);
213 glNormal3f( 0.0f, 1.0f, 0.0f);
214 glTexCoord2f(0, 0); glVertex3f( 0.5f, 0.5f, 0.5f);
215 glTexCoord2f(1, 0); glVertex3f( 0.5f, 0.5f,-0.5f);
216 glTexCoord2f(1, 1); glVertex3f(-0.5f, 0.5f,-0.5f);
217 glTexCoord2f(0, 1); glVertex3f(-0.5f, 0.5f, 0.5f);
218 glEnd();
219
220 glBindTexture(GL_TEXTURE_2D, m_textures[3]);
221 glBegin(GL_QUADS);
222 glNormal3f( 0.0f,-1.0f, 0.0f);
223 glTexCoord2f(0, 0); glVertex3f(-0.5f,-0.5f,-0.5f);
224 glTexCoord2f(1, 0); glVertex3f( 0.5f,-0.5f,-0.5f);
225 glTexCoord2f(1, 1); glVertex3f( 0.5f,-0.5f, 0.5f);
226 glTexCoord2f(0, 1); glVertex3f(-0.5f,-0.5f, 0.5f);
227 glEnd();
228
229 glBindTexture(GL_TEXTURE_2D, m_textures[4]);
230 glBegin(GL_QUADS);
231 glNormal3f( 1.0f, 0.0f, 0.0f);
232 glTexCoord2f(0, 0); glVertex3f( 0.5f, 0.5f, 0.5f);
233 glTexCoord2f(1, 0); glVertex3f( 0.5f,-0.5f, 0.5f);
234 glTexCoord2f(1, 1); glVertex3f( 0.5f,-0.5f,-0.5f);
235 glTexCoord2f(0, 1); glVertex3f( 0.5f, 0.5f,-0.5f);
236 glEnd();
237
238 glBindTexture(GL_TEXTURE_2D, m_textures[5]);
239 glBegin(GL_QUADS);
240 glNormal3f(-1.0f, 0.0f, 0.0f);
241 glTexCoord2f(0, 0); glVertex3f(-0.5f,-0.5f,-0.5f);
242 glTexCoord2f(1, 0); glVertex3f(-0.5f,-0.5f, 0.5f);
243 glTexCoord2f(1, 1); glVertex3f(-0.5f, 0.5f, 0.5f);
244 glTexCoord2f(0, 1); glVertex3f(-0.5f, 0.5f,-0.5f);
245 glEnd();
246
247 glFlush();
248
249 CheckGLError();
250 }
251
252
253 // ----------------------------------------------------------------------------
254 // MyApp: the application object
255 // ----------------------------------------------------------------------------
256
257 IMPLEMENT_APP(MyApp)
258
259 bool MyApp::OnInit()
260 {
261 if ( !wxApp::OnInit() )
262 return false;
263
264 new MyFrame();
265
266 return true;
267 }
268
269 int MyApp::OnExit()
270 {
271 delete m_glContext;
272
273 return wxApp::OnExit();
274 }
275
276 TestGLContext& MyApp::GetContext(wxGLCanvas *canvas)
277 {
278 if ( !m_glContext )
279 {
280 // Create the OpenGL context for the first window which needs it:
281 // subsequently created windows will all share the same context.
282 m_glContext = new TestGLContext(canvas);
283 }
284
285 m_glContext->SetCurrent(*canvas);
286
287 return *m_glContext;
288 }
289
290 // ----------------------------------------------------------------------------
291 // TestGLCanvas
292 // ----------------------------------------------------------------------------
293
294 BEGIN_EVENT_TABLE(TestGLCanvas, wxGLCanvas)
295 EVT_PAINT(TestGLCanvas::OnPaint)
296 EVT_KEY_DOWN(TestGLCanvas::OnKeyDown)
297 EVT_TIMER(SpinTimer, TestGLCanvas::OnSpinTimer)
298 END_EVENT_TABLE()
299
300 TestGLCanvas::TestGLCanvas(wxWindow *parent)
301 // With perspective OpenGL graphics, the wxFULL_REPAINT_ON_RESIZE style
302 // flag should always be set, because even making the canvas smaller should
303 // be followed by a paint event that updates the entire canvas with new
304 // viewport settings.
305 : wxGLCanvas(parent, wxID_ANY, NULL /* attribs */,
306 wxDefaultPosition, wxDefaultSize,
307 wxFULL_REPAINT_ON_RESIZE),
308 m_xangle(30.0),
309 m_yangle(30.0),
310 m_spinTimer(this,SpinTimer)
311 {
312 }
313
314 void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
315 {
316 // This is required even though dc is not used otherwise.
317 wxPaintDC dc(this);
318
319 // Set the OpenGL viewport according to the client size of this canvas.
320 // This is done here rather than in a wxSizeEvent handler because our
321 // OpenGL rendering context (and thus viewport setting) is used with
322 // multiple canvases: If we updated the viewport in the wxSizeEvent
323 // handler, changing the size of one canvas causes a viewport setting that
324 // is wrong when next another canvas is repainted.
325 const wxSize ClientSize = GetClientSize();
326
327 TestGLContext& canvas = wxGetApp().GetContext(this);
328 glViewport(0, 0, ClientSize.x, ClientSize.y);
329
330 // Render the graphics and swap the buffers.
331 canvas.DrawRotatedCube(m_xangle, m_yangle);
332 SwapBuffers();
333 }
334
335 void TestGLCanvas::Spin(float xSpin, float ySpin)
336 {
337 m_xangle += xSpin;
338 m_yangle += ySpin;
339
340 Refresh(false);
341 }
342
343 void TestGLCanvas::OnKeyDown(wxKeyEvent& event)
344 {
345 float angle = 5.0;
346
347 switch ( event.GetKeyCode() )
348 {
349 case WXK_RIGHT:
350 Spin( 0.0, -angle );
351 break;
352
353 case WXK_LEFT:
354 Spin( 0.0, angle );
355 break;
356
357 case WXK_DOWN:
358 Spin( -angle, 0.0 );
359 break;
360
361 case WXK_UP:
362 Spin( angle, 0.0 );
363 break;
364
365 case WXK_SPACE:
366 if ( m_spinTimer.IsRunning() )
367 m_spinTimer.Stop();
368 else
369 m_spinTimer.Start( 25 );
370 break;
371
372 default:
373 event.Skip();
374 return;
375 }
376 }
377
378 void TestGLCanvas::OnSpinTimer(wxTimerEvent& WXUNUSED(event))
379 {
380 Spin(0.0, 4.0);
381 }
382
383
384 // ----------------------------------------------------------------------------
385 // MyFrame: main application window
386 // ----------------------------------------------------------------------------
387
388 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
389 EVT_MENU(wxID_NEW, MyFrame::OnNewWindow)
390 EVT_MENU(wxID_CLOSE, MyFrame::OnClose)
391 END_EVENT_TABLE()
392
393 MyFrame::MyFrame()
394 : wxFrame(NULL, wxID_ANY, wxT("wxWidgets OpenGL Cube Sample"))
395 {
396 new TestGLCanvas(this);
397
398 SetIcon(wxICON(sample));
399
400 // Make a menubar
401 wxMenu *menu = new wxMenu;
402 menu->Append(wxID_NEW);
403 menu->AppendSeparator();
404 menu->Append(wxID_CLOSE);
405 wxMenuBar *menuBar = new wxMenuBar;
406 menuBar->Append(menu, wxT("&Cube"));
407
408 SetMenuBar(menuBar);
409
410 CreateStatusBar();
411
412 SetClientSize(400, 400);
413 Show();
414
415 // test IsDisplaySupported() function:
416 static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
417 wxLogStatus("Double-buffered display %s supported",
418 wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not");
419 }
420
421 void MyFrame::OnClose(wxCommandEvent& WXUNUSED(event))
422 {
423 // true is to force the frame to close
424 Close(true);
425 }
426
427 void MyFrame::OnNewWindow( wxCommandEvent& WXUNUSED(event) )
428 {
429 new MyFrame();
430 }
431