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