use slightly more light
[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 #if !defined(__WXMSW__) && !defined(__WXPM__)
38 #include "../../sample.xpm"
39 #endif
40
41 // ----------------------------------------------------------------------------
42 // helper functions
43 // ----------------------------------------------------------------------------
44
45 static 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
71 static 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
117 // ============================================================================
118 // implementation
119 // ============================================================================
120
121 // ----------------------------------------------------------------------------
122 // MyApp: the application object
123 // ----------------------------------------------------------------------------
124
125 IMPLEMENT_APP(MyApp)
126
127 bool MyApp::OnInit()
128 {
129 if ( !wxApp::OnInit() )
130 return false;
131
132 // Create the main window
133 new MyFrame();
134
135 return true;
136 }
137
138 int MyApp::OnExit()
139 {
140 delete m_glContext;
141
142 return wxApp::OnExit();
143 }
144
145 TestGLContext& MyApp::GetContext(wxGLCanvas *canvas)
146 {
147 if ( !m_glContext )
148 m_glContext = new TestGLContext(canvas);
149
150 return *m_glContext;
151 }
152
153 // ----------------------------------------------------------------------------
154 // TestGLContext
155 // ----------------------------------------------------------------------------
156
157 TestGLContext::TestGLContext(wxGLCanvas *canvas)
158 : wxGLContext(canvas)
159 {
160 SetCurrent(*canvas);
161
162 // set up the parameters we want to use
163 glEnable(GL_DEPTH_TEST);
164 glEnable(GL_LIGHTING);
165 glEnable(GL_LIGHT0);
166 glEnable(GL_TEXTURE_2D);
167
168 // add slightly more light, the default lightning is rather dark
169 GLfloat ambient[] = { 0.5, 0.5, 0.5, 0.5 };
170 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
171
172 // set viewing projection
173 glMatrixMode(GL_PROJECTION);
174 glLoadIdentity();
175 glFrustum(-0.5f, 0.5f, -0.5f, 0.5f, 1.0f, 3.0f);
176
177 // create the textures to use for cube sides: they will be reused by all
178 // canvases (which is probably not critical in the case of simple textures
179 // we use here but could be really important for a real application where
180 // each texture could take many megabytes)
181 glGenTextures(WXSIZEOF(m_textures), m_textures);
182
183 for ( unsigned i = 0; i < WXSIZEOF(m_textures); i++ )
184 {
185 glBindTexture(GL_TEXTURE_2D, m_textures[i]);
186
187 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
188 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
189 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
190 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
191 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
192
193 const wxImage img(DrawDice(256, i + 1));
194
195 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
196 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.GetWidth(), img.GetHeight(),
197 0, GL_RGB, GL_UNSIGNED_BYTE, img.GetData());
198 }
199
200 CheckGLError();
201 }
202
203 void TestGLContext::DrawRotatedCube(float xangle, float yangle)
204 {
205 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
206
207 glMatrixMode(GL_MODELVIEW);
208 glLoadIdentity();
209 glTranslatef(0.0f, 0.0f, -2.0f);
210 glRotatef(xangle, 1.0f, 0.0f, 0.0f);
211 glRotatef(yangle, 0.0f, 1.0f, 0.0f);
212
213 // draw six faces of a cube of size 1 centered at (0, 0, 0)
214 glBindTexture(GL_TEXTURE_2D, m_textures[0]);
215 glBegin(GL_QUADS);
216 glNormal3f( 0.0f, 0.0f, 1.0f);
217 glTexCoord2f(0, 0); glVertex3f( 0.5f, 0.5f, 0.5f);
218 glTexCoord2f(1, 0); glVertex3f(-0.5f, 0.5f, 0.5f);
219 glTexCoord2f(1, 1); glVertex3f(-0.5f,-0.5f, 0.5f);
220 glTexCoord2f(0, 1); glVertex3f( 0.5f,-0.5f, 0.5f);
221 glEnd();
222
223 glBindTexture(GL_TEXTURE_2D, m_textures[1]);
224 glBegin(GL_QUADS);
225 glNormal3f( 0.0f, 0.0f,-1.0f);
226 glTexCoord2f(0, 0); glVertex3f(-0.5f,-0.5f,-0.5f);
227 glTexCoord2f(1, 0); glVertex3f(-0.5f, 0.5f,-0.5f);
228 glTexCoord2f(1, 1); glVertex3f( 0.5f, 0.5f,-0.5f);
229 glTexCoord2f(0, 1); glVertex3f( 0.5f,-0.5f,-0.5f);
230 glEnd();
231
232 glBindTexture(GL_TEXTURE_2D, m_textures[2]);
233 glBegin(GL_QUADS);
234 glNormal3f( 0.0f, 1.0f, 0.0f);
235 glTexCoord2f(0, 0); glVertex3f( 0.5f, 0.5f, 0.5f);
236 glTexCoord2f(1, 0); glVertex3f( 0.5f, 0.5f,-0.5f);
237 glTexCoord2f(1, 1); glVertex3f(-0.5f, 0.5f,-0.5f);
238 glTexCoord2f(0, 1); glVertex3f(-0.5f, 0.5f, 0.5f);
239 glEnd();
240
241 glBindTexture(GL_TEXTURE_2D, m_textures[3]);
242 glBegin(GL_QUADS);
243 glNormal3f( 0.0f,-1.0f, 0.0f);
244 glTexCoord2f(0, 0); glVertex3f(-0.5f,-0.5f,-0.5f);
245 glTexCoord2f(1, 0); glVertex3f( 0.5f,-0.5f,-0.5f);
246 glTexCoord2f(1, 1); glVertex3f( 0.5f,-0.5f, 0.5f);
247 glTexCoord2f(0, 1); glVertex3f(-0.5f,-0.5f, 0.5f);
248 glEnd();
249
250 glBindTexture(GL_TEXTURE_2D, m_textures[4]);
251 glBegin(GL_QUADS);
252 glNormal3f( 1.0f, 0.0f, 0.0f);
253 glTexCoord2f(0, 0); glVertex3f( 0.5f, 0.5f, 0.5f);
254 glTexCoord2f(1, 0); glVertex3f( 0.5f,-0.5f, 0.5f);
255 glTexCoord2f(1, 1); glVertex3f( 0.5f,-0.5f,-0.5f);
256 glTexCoord2f(0, 1); glVertex3f( 0.5f, 0.5f,-0.5f);
257 glEnd();
258
259 glBindTexture(GL_TEXTURE_2D, m_textures[5]);
260 glBegin(GL_QUADS);
261 glNormal3f(-1.0f, 0.0f, 0.0f);
262 glTexCoord2f(0, 0); glVertex3f(-0.5f,-0.5f,-0.5f);
263 glTexCoord2f(1, 0); glVertex3f(-0.5f,-0.5f, 0.5f);
264 glTexCoord2f(1, 1); glVertex3f(-0.5f, 0.5f, 0.5f);
265 glTexCoord2f(0, 1); glVertex3f(-0.5f, 0.5f,-0.5f);
266 glEnd();
267
268 glFlush();
269
270 CheckGLError();
271 }
272
273 // ----------------------------------------------------------------------------
274 // TestGLCanvas
275 // ----------------------------------------------------------------------------
276
277 BEGIN_EVENT_TABLE(TestGLCanvas, wxGLCanvas)
278 EVT_SIZE(TestGLCanvas::OnSize)
279 EVT_PAINT(TestGLCanvas::OnPaint)
280
281 EVT_KEY_DOWN(TestGLCanvas::OnKeyDown)
282 END_EVENT_TABLE()
283
284 TestGLCanvas::TestGLCanvas(wxWindow *parent)
285 : wxGLCanvas(parent, wxID_ANY, NULL /* attribs */)
286 {
287 m_xangle =
288 m_yangle = 30;
289 }
290
291 void TestGLCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
292 {
293 wxPaintDC dc(this);
294
295 wxGetApp().GetContext(this).DrawRotatedCube(m_xangle, m_yangle);
296
297 SwapBuffers();
298 }
299
300 void TestGLCanvas::OnSize(wxSizeEvent& event)
301 {
302 // don't prevent default processing from taking place
303 event.Skip();
304
305 if ( !IsShown() )
306 return;
307
308 // set GL viewport (not called by wxGLCanvas::OnSize on all platforms...)
309 int w, h;
310 GetClientSize(&w, &h);
311
312 wxGetApp().GetContext(this);
313 glViewport(0, 0, w, h);
314 }
315
316 void TestGLCanvas::OnKeyDown( wxKeyEvent& event )
317 {
318 float *p = NULL;
319
320 bool inverse = false;
321
322 switch ( event.GetKeyCode() )
323 {
324 case WXK_RIGHT:
325 inverse = true;
326 // fall through
327
328 case WXK_LEFT:
329 // rotate around Y axis
330 p = &m_yangle;
331 break;
332
333 case WXK_DOWN:
334 inverse = true;
335 // fall through
336
337 case WXK_UP:
338 // rotate around X axis
339 p = &m_xangle;
340 break;
341
342 default:
343 event.Skip();
344 return;
345 }
346
347 float angle = 5;
348 if ( inverse )
349 angle = -angle;
350
351 *p += angle;
352
353 Refresh(false);
354 }
355
356 // ----------------------------------------------------------------------------
357 // MyFrame: main application window
358 // ----------------------------------------------------------------------------
359
360 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
361 EVT_MENU(wxID_NEW, MyFrame::OnNewWindow)
362 EVT_MENU(wxID_CLOSE, MyFrame::OnClose)
363 END_EVENT_TABLE()
364
365 MyFrame::MyFrame()
366 : wxFrame(NULL, wxID_ANY, _T("wxWidgets OpenGL Cube Sample"))
367 {
368 new TestGLCanvas(this);
369
370 SetIcon(wxICON(sample));
371
372 // Make a menubar
373 wxMenu *menu = new wxMenu;
374 menu->Append(wxID_NEW);
375 menu->AppendSeparator();
376 menu->Append(wxID_CLOSE);
377 wxMenuBar *menuBar = new wxMenuBar;
378 menuBar->Append(menu, _T("&Cube"));
379
380 SetMenuBar(menuBar);
381
382 CreateStatusBar();
383
384 SetClientSize(400, 400);
385 Show();
386 }
387
388 void MyFrame::OnClose(wxCommandEvent& WXUNUSED(event))
389 {
390 // true is to force the frame to close
391 Close(true);
392 }
393
394 void MyFrame::OnNewWindow( wxCommandEvent& WXUNUSED(event) )
395 {
396 (void) new MyFrame();
397 }
398