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