]>
Commit | Line | Data |
---|---|---|
8b089c5e JS |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: isosurf.cpp | |
3 | // Purpose: wxGLCanvas demo program | |
4 | // Author: Brian Paul (original gltk version), Wolfram Gloger | |
0c1c1c71 | 5 | // Modified by: Julian Smart, Francesco Montorsi |
8b089c5e | 6 | // Created: 04/01/98 |
8b089c5e | 7 | // Copyright: (c) Julian Smart |
2f6c54eb | 8 | // Licence: wxWindows licence |
8b089c5e JS |
9 | ///////////////////////////////////////////////////////////////////////////// |
10 | ||
8b089c5e JS |
11 | // For compilers that support precompilation, includes "wx.h". |
12 | #include "wx/wxprec.h" | |
13 | ||
14 | #ifdef __BORLANDC__ | |
15 | #pragma hdrstop | |
16 | #endif | |
17 | ||
18 | #ifndef WX_PRECOMP | |
19 | #include "wx/wx.h" | |
20 | #endif | |
21 | ||
806e2f15 VZ |
22 | #if !wxUSE_GLCANVAS |
23 | #error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" | |
24 | #endif | |
25 | ||
8b089c5e JS |
26 | #include "wx/timer.h" |
27 | #include "wx/glcanvas.h" | |
b713f891 | 28 | #include "wx/math.h" |
6c7f0949 FM |
29 | #include "wx/log.h" |
30 | #include "wx/cmdline.h" | |
6c7f0949 FM |
31 | #include "wx/wfstream.h" |
32 | #include "wx/zstream.h" | |
0c1c1c71 | 33 | #include "wx/txtstrm.h" |
8b089c5e | 34 | |
451c13c8 | 35 | #include "isosurf.h" |
3a992940 JS |
36 | #include "../../sample.xpm" |
37 | ||
8b089c5e | 38 | |
6c7f0949 | 39 | // global options which can be set through command-line options |
6c7f0949 FM |
40 | GLboolean g_use_vertex_arrays = GL_FALSE; |
41 | GLboolean g_doubleBuffer = GL_TRUE; | |
42 | GLboolean g_smooth = GL_TRUE; | |
43 | GLboolean g_lighting = GL_TRUE; | |
8b089c5e | 44 | |
8b089c5e JS |
45 | |
46 | ||
6c7f0949 FM |
47 | //--------------------------------------------------------------------------- |
48 | // MyApp | |
49 | //--------------------------------------------------------------------------- | |
8b089c5e | 50 | |
6c7f0949 | 51 | IMPLEMENT_APP(MyApp) |
8b089c5e | 52 | |
6c7f0949 | 53 | bool MyApp::OnInit() |
8b089c5e | 54 | { |
6c7f0949 FM |
55 | if ( !wxApp::OnInit() ) |
56 | return false; | |
5cf036d0 | 57 | |
6c7f0949 | 58 | // Create the main frame window |
e2250d64 | 59 | new MyFrame(NULL, wxT("wxWidgets OpenGL Isosurf Sample")); |
5cf036d0 | 60 | |
6c7f0949 | 61 | return true; |
8b089c5e JS |
62 | } |
63 | ||
6c7f0949 | 64 | void MyApp::OnInitCmdLine(wxCmdLineParser& parser) |
8b089c5e | 65 | { |
7881f83b FM |
66 | parser.AddSwitch("", "sb", "Do not use double buffering"); |
67 | parser.AddSwitch("", "db", "Use double buffering"); | |
68 | parser.AddSwitch("", "va", "Use vertex arrays"); | |
8b089c5e | 69 | |
6c7f0949 | 70 | wxApp::OnInitCmdLine(parser); |
8b089c5e JS |
71 | } |
72 | ||
6c7f0949 | 73 | bool MyApp::OnCmdLineParsed(wxCmdLineParser& parser) |
8b089c5e | 74 | { |
7881f83b | 75 | if (parser.Found("sb")) |
6c7f0949 | 76 | g_doubleBuffer = GL_FALSE; |
7881f83b | 77 | else if (parser.Found("db")) |
6c7f0949 | 78 | g_doubleBuffer = GL_TRUE; |
8b089c5e | 79 | |
7881f83b | 80 | if (parser.Found("va")) |
6c7f0949 | 81 | g_use_vertex_arrays = GL_TRUE; |
5cf036d0 | 82 | |
6c7f0949 | 83 | return wxApp::OnCmdLineParsed(parser); |
8b089c5e JS |
84 | } |
85 | ||
6c7f0949 FM |
86 | //--------------------------------------------------------------------------- |
87 | // MyFrame | |
88 | //--------------------------------------------------------------------------- | |
451c13c8 VZ |
89 | |
90 | BEGIN_EVENT_TABLE(MyFrame, wxFrame) | |
91 | EVT_MENU(wxID_EXIT, MyFrame::OnExit) | |
92 | END_EVENT_TABLE() | |
93 | ||
451c13c8 | 94 | MyFrame::MyFrame(wxFrame *frame, const wxString& title, const wxPoint& pos, |
df344ba9 | 95 | const wxSize& size, long style) |
451c13c8 VZ |
96 | : wxFrame(frame, wxID_ANY, title, pos, size, style), |
97 | m_canvas(NULL) | |
98 | { | |
99 | SetIcon(wxICON(sample)); | |
100 | ||
101 | ||
5cf036d0 DS |
102 | // Make a menubar |
103 | wxMenu *fileMenu = new wxMenu; | |
8b089c5e | 104 | |
9a83f860 | 105 | fileMenu->Append(wxID_EXIT, wxT("E&xit")); |
5cf036d0 | 106 | wxMenuBar *menuBar = new wxMenuBar; |
9a83f860 | 107 | menuBar->Append(fileMenu, wxT("&File")); |
451c13c8 VZ |
108 | SetMenuBar(menuBar); |
109 | ||
8b089c5e JS |
110 | |
111 | // Make a TestGLCanvas | |
112 | ||
113 | // JACS | |
114 | #ifdef __WXMSW__ | |
5cf036d0 | 115 | int *gl_attrib = NULL; |
8b089c5e | 116 | #else |
6c7f0949 FM |
117 | int gl_attrib[20] = |
118 | { WX_GL_RGBA, WX_GL_MIN_RED, 1, WX_GL_MIN_GREEN, 1, | |
5cf036d0 DS |
119 | WX_GL_MIN_BLUE, 1, WX_GL_DEPTH_SIZE, 1, |
120 | WX_GL_DOUBLEBUFFER, | |
6716fce4 | 121 | # if defined(__WXMAC__) || defined(__WXCOCOA__) |
5cf036d0 | 122 | GL_NONE }; |
cb712074 | 123 | # else |
5cf036d0 | 124 | None }; |
cb712074 | 125 | # endif |
8b089c5e JS |
126 | #endif |
127 | ||
6c7f0949 | 128 | if (!g_doubleBuffer) |
5cf036d0 | 129 | { |
0c1c1c71 | 130 | wxLogWarning("Disabling double buffering"); |
6c7f0949 | 131 | |
8b089c5e | 132 | #ifdef __WXGTK__ |
5cf036d0 | 133 | gl_attrib[9] = None; |
8b089c5e | 134 | #endif |
6c7f0949 | 135 | g_doubleBuffer = GL_FALSE; |
5cf036d0 DS |
136 | } |
137 | ||
97f85100 VZ |
138 | m_canvas = new TestGLCanvas(this, wxID_ANY, gl_attrib); |
139 | ||
451c13c8 VZ |
140 | // Show the frame |
141 | Show(true); | |
97f85100 | 142 | Raise(); |
8b089c5e | 143 | |
97f85100 | 144 | m_canvas->InitGL(); |
8b089c5e JS |
145 | } |
146 | ||
5cf036d0 DS |
147 | MyFrame::~MyFrame() |
148 | { | |
806e2f15 | 149 | delete m_canvas; |
5cf036d0 DS |
150 | } |
151 | ||
8b089c5e | 152 | // Intercept menu commands |
5cf036d0 | 153 | void MyFrame::OnExit( wxCommandEvent& WXUNUSED(event) ) |
8b089c5e | 154 | { |
5cf036d0 DS |
155 | // true is to force the frame to close |
156 | Close(true); | |
8b089c5e JS |
157 | } |
158 | ||
6c7f0949 FM |
159 | |
160 | //--------------------------------------------------------------------------- | |
161 | // TestGLCanvas | |
162 | //--------------------------------------------------------------------------- | |
8b089c5e JS |
163 | |
164 | BEGIN_EVENT_TABLE(TestGLCanvas, wxGLCanvas) | |
165 | EVT_SIZE(TestGLCanvas::OnSize) | |
166 | EVT_PAINT(TestGLCanvas::OnPaint) | |
167 | EVT_CHAR(TestGLCanvas::OnChar) | |
168 | EVT_MOUSE_EVENTS(TestGLCanvas::OnMouseEvent) | |
8b089c5e JS |
169 | END_EVENT_TABLE() |
170 | ||
451c13c8 VZ |
171 | TestGLCanvas::TestGLCanvas(wxWindow *parent, |
172 | wxWindowID id, | |
451c13c8 | 173 | int* gl_attrib) |
0c1c1c71 | 174 | : wxGLCanvas(parent, id, gl_attrib) |
8b089c5e | 175 | { |
0c1c1c71 FM |
176 | m_xrot = 0; |
177 | m_yrot = 0; | |
178 | m_numverts = 0; | |
179 | ||
451c13c8 VZ |
180 | // Explicitly create a new rendering context instance for this canvas. |
181 | m_glRC = new wxGLContext(this); | |
8b089c5e JS |
182 | } |
183 | ||
451c13c8 VZ |
184 | TestGLCanvas::~TestGLCanvas() |
185 | { | |
186 | delete m_glRC; | |
187 | } | |
8b089c5e | 188 | |
6c7f0949 FM |
189 | void TestGLCanvas::LoadSurface(const wxString& filename) |
190 | { | |
0c1c1c71 FM |
191 | // FIXME |
192 | // we need to set english locale to force wxTextInputStream's calls to | |
193 | // wxStrtod to use the point and not the comma as decimal separator... | |
194 | // (the isosurf.dat contains points and not commas)... | |
195 | wxLocale l(wxLANGUAGE_ENGLISH); | |
196 | ||
6c7f0949 FM |
197 | wxZlibInputStream* stream = |
198 | new wxZlibInputStream(new wxFFileInputStream(filename)); | |
199 | if (!stream || !stream->IsOk()) | |
200 | { | |
201 | wxLogError("Cannot load '%s' type of files!", filename.c_str()); | |
202 | delete stream; | |
203 | return; | |
204 | } | |
205 | ||
6c7f0949 | 206 | { |
0c1c1c71 | 207 | // we suppose to have in input a text file containing floating numbers |
4c51a665 | 208 | // space/newline-separated... first 3 numbers are the coordinates of a |
0c1c1c71 FM |
209 | // vertex and the following 3 are the relative vertex normal and so on... |
210 | ||
211 | wxTextInputStream inFile(*stream); | |
212 | m_numverts = 0; | |
213 | ||
214 | while (!stream->Eof() && m_numverts < MAXVERTS)// && m_numverts<MAXVERTS) | |
215 | { | |
216 | inFile >> m_verts[m_numverts][0] >> m_verts[m_numverts][1] >> m_verts[m_numverts][2]; | |
217 | inFile >> m_norms[m_numverts][0] >> m_norms[m_numverts][1] >> m_norms[m_numverts][2]; | |
218 | ||
219 | m_numverts++; | |
220 | } | |
221 | ||
222 | // discard last vertex; it is a zero caused by the EOF | |
223 | m_numverts--; | |
6c7f0949 FM |
224 | } |
225 | ||
226 | delete stream; | |
227 | ||
9a83f860 | 228 | wxLogMessage(wxT("Loaded %d vertices, %d triangles from '%s'"), |
6c7f0949 | 229 | m_numverts, m_numverts-2, filename.c_str()); |
ce00f59b | 230 | |
df344ba9 FM |
231 | // NOTE: for some reason under wxGTK the following is required to avoid that |
232 | // the surface gets rendered in a small rectangle in the top-left corner of the frame | |
233 | PostSizeEventToParent(); | |
6c7f0949 FM |
234 | } |
235 | ||
2db98bf5 | 236 | void TestGLCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) ) |
8b089c5e JS |
237 | { |
238 | // This is a dummy, to avoid an endless succession of paint messages. | |
239 | // OnPaint handlers must always create a wxPaintDC. | |
240 | wxPaintDC dc(this); | |
241 | ||
451c13c8 VZ |
242 | // This is normally only necessary if there is more than one wxGLCanvas |
243 | // or more than one wxGLContext in the application. | |
244 | SetCurrent(*m_glRC); | |
919ae91a | 245 | |
6c7f0949 FM |
246 | glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); |
247 | glPushMatrix(); | |
248 | glRotatef( m_yrot, 0.0f, 1.0f, 0.0f ); | |
249 | glRotatef( m_xrot, 1.0f, 0.0f, 0.0f ); | |
250 | ||
251 | // draw the surface | |
0c1c1c71 | 252 | if (g_use_vertex_arrays) |
6c7f0949 FM |
253 | { |
254 | glDrawArrays( GL_TRIANGLE_STRIP, 0, m_numverts ); | |
255 | } | |
0c1c1c71 | 256 | else |
6c7f0949 FM |
257 | { |
258 | glBegin( GL_TRIANGLE_STRIP ); | |
259 | ||
260 | for (int i=0;i<m_numverts;i++) | |
261 | { | |
262 | glNormal3fv( m_norms[i] ); | |
263 | glVertex3fv( m_verts[i] ); | |
264 | } | |
265 | ||
266 | glEnd(); | |
267 | } | |
268 | ||
269 | glPopMatrix(); | |
270 | glFlush(); // Not really necessary: buffer swapping below implies glFlush() | |
271 | ||
8b089c5e JS |
272 | SwapBuffers(); |
273 | } | |
274 | ||
275 | void TestGLCanvas::OnSize(wxSizeEvent& event) | |
276 | { | |
97f85100 VZ |
277 | if ( !IsShownOnScreen() ) |
278 | return; | |
451c13c8 VZ |
279 | // This is normally only necessary if there is more than one wxGLCanvas |
280 | // or more than one wxGLContext in the application. | |
281 | SetCurrent(*m_glRC); | |
282 | ||
283 | // It's up to the application code to update the OpenGL viewport settings. | |
284 | // This is OK here only because there is only one canvas that uses the | |
285 | // context. See the cube sample for that case that multiple canvases are | |
286 | // made current with one context. | |
287 | glViewport(0, 0, event.GetSize().x, event.GetSize().y); | |
8b089c5e JS |
288 | } |
289 | ||
290 | void TestGLCanvas::OnChar(wxKeyEvent& event) | |
291 | { | |
5cf036d0 DS |
292 | switch( event.GetKeyCode() ) |
293 | { | |
8b089c5e | 294 | case WXK_ESCAPE: |
5cf036d0 DS |
295 | wxTheApp->ExitMainLoop(); |
296 | return; | |
297 | ||
8b089c5e | 298 | case WXK_LEFT: |
6c7f0949 | 299 | m_yrot -= 15.0; |
5cf036d0 DS |
300 | break; |
301 | ||
8b089c5e | 302 | case WXK_RIGHT: |
6c7f0949 | 303 | m_yrot += 15.0; |
5cf036d0 DS |
304 | break; |
305 | ||
8b089c5e | 306 | case WXK_UP: |
6c7f0949 | 307 | m_xrot += 15.0; |
5cf036d0 DS |
308 | break; |
309 | ||
8b089c5e | 310 | case WXK_DOWN: |
6c7f0949 | 311 | m_xrot -= 15.0; |
5cf036d0 DS |
312 | break; |
313 | ||
8b089c5e | 314 | case 's': case 'S': |
6c7f0949 FM |
315 | g_smooth = !g_smooth; |
316 | if (g_smooth) | |
5cf036d0 | 317 | glShadeModel(GL_SMOOTH); |
5cf036d0 | 318 | else |
5cf036d0 | 319 | glShadeModel(GL_FLAT); |
5cf036d0 DS |
320 | break; |
321 | ||
8b089c5e | 322 | case 'l': case 'L': |
6c7f0949 FM |
323 | g_lighting = !g_lighting; |
324 | if (g_lighting) | |
5cf036d0 | 325 | glEnable(GL_LIGHTING); |
5cf036d0 | 326 | else |
5cf036d0 | 327 | glDisable(GL_LIGHTING); |
5cf036d0 DS |
328 | break; |
329 | ||
330 | default: | |
8b089c5e | 331 | event.Skip(); |
5cf036d0 | 332 | return; |
8b089c5e JS |
333 | } |
334 | ||
5cf036d0 | 335 | Refresh(false); |
8b089c5e JS |
336 | } |
337 | ||
338 | void TestGLCanvas::OnMouseEvent(wxMouseEvent& event) | |
339 | { | |
340 | static int dragging = 0; | |
341 | static float last_x, last_y; | |
342 | ||
451c13c8 VZ |
343 | // Allow default processing to happen, or else the canvas cannot gain focus |
344 | // (for key events). | |
345 | event.Skip(); | |
346 | ||
df344ba9 | 347 | if (event.LeftIsDown()) |
5cf036d0 | 348 | { |
df344ba9 | 349 | if (!dragging) |
5cf036d0 DS |
350 | { |
351 | dragging = 1; | |
352 | } | |
353 | else | |
354 | { | |
6c7f0949 FM |
355 | m_yrot += (event.GetX() - last_x)*1.0; |
356 | m_xrot += (event.GetY() - last_y)*1.0; | |
5cf036d0 DS |
357 | Refresh(false); |
358 | } | |
359 | last_x = event.GetX(); | |
360 | last_y = event.GetY(); | |
2f6c54eb | 361 | } |
5cf036d0 | 362 | else |
451c13c8 | 363 | { |
5cf036d0 | 364 | dragging = 0; |
451c13c8 | 365 | } |
8b089c5e JS |
366 | } |
367 | ||
6c7f0949 FM |
368 | void TestGLCanvas::InitMaterials() |
369 | { | |
370 | static const GLfloat ambient[4] = {0.1f, 0.1f, 0.1f, 1.0f}; | |
371 | static const GLfloat diffuse[4] = {0.5f, 1.0f, 1.0f, 1.0f}; | |
372 | static const GLfloat position0[4] = {0.0f, 0.0f, 20.0f, 0.0f}; | |
373 | static const GLfloat position1[4] = {0.0f, 0.0f, -20.0f, 0.0f}; | |
374 | static const GLfloat front_mat_shininess[1] = {60.0f}; | |
375 | static const GLfloat front_mat_specular[4] = {0.2f, 0.2f, 0.2f, 1.0f}; | |
376 | static const GLfloat front_mat_diffuse[4] = {0.5f, 0.28f, 0.38f, 1.0f}; | |
377 | /* | |
378 | static const GLfloat back_mat_shininess[1] = {60.0f}; | |
379 | static const GLfloat back_mat_specular[4] = {0.5f, 0.5f, 0.2f, 1.0f}; | |
380 | static const GLfloat back_mat_diffuse[4] = {1.0f, 1.0f, 0.2f, 1.0f}; | |
381 | */ | |
382 | static const GLfloat lmodel_ambient[4] = {1.0f, 1.0f, 1.0f, 1.0f}; | |
383 | static const GLfloat lmodel_twoside[1] = {GL_FALSE}; | |
384 | ||
385 | glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); | |
386 | glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); | |
387 | glLightfv(GL_LIGHT0, GL_POSITION, position0); | |
388 | glEnable(GL_LIGHT0); | |
389 | ||
390 | glLightfv(GL_LIGHT1, GL_AMBIENT, ambient); | |
391 | glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse); | |
392 | glLightfv(GL_LIGHT1, GL_POSITION, position1); | |
393 | glEnable(GL_LIGHT1); | |
394 | ||
395 | glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); | |
396 | glLightModelfv(GL_LIGHT_MODEL_TWO_SIDE, lmodel_twoside); | |
397 | glEnable(GL_LIGHTING); | |
398 | ||
399 | glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, front_mat_shininess); | |
400 | glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, front_mat_specular); | |
401 | glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, front_mat_diffuse); | |
402 | } | |
403 | ||
404 | void TestGLCanvas::InitGL() | |
405 | { | |
97f85100 VZ |
406 | // Make the new context current (activate it for use) with this canvas. |
407 | SetCurrent(*m_glRC); | |
408 | ||
6c7f0949 FM |
409 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
410 | ||
411 | glShadeModel(GL_SMOOTH); | |
412 | glEnable(GL_DEPTH_TEST); | |
413 | ||
414 | InitMaterials(); | |
415 | ||
416 | glMatrixMode(GL_PROJECTION); | |
417 | glLoadIdentity(); | |
418 | glFrustum( -1.0, 1.0, -1.0, 1.0, 5.0, 25.0 ); | |
419 | ||
420 | glMatrixMode(GL_MODELVIEW); | |
421 | glLoadIdentity(); | |
422 | glTranslatef( 0.0, 0.0, -6.0 ); | |
423 | ||
424 | if (g_use_vertex_arrays) | |
425 | { | |
426 | glVertexPointer( 3, GL_FLOAT, 0, m_verts ); | |
427 | glNormalPointer( GL_FLOAT, 0, m_norms ); | |
0c1c1c71 FM |
428 | glEnable( GL_VERTEX_ARRAY ); |
429 | glEnable( GL_NORMAL_ARRAY ); | |
6c7f0949 | 430 | } |
97f85100 VZ |
431 | |
432 | InitMaterials(); | |
433 | LoadSurface("isosurf.dat.gz"); | |
6c7f0949 FM |
434 | } |
435 |