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