Mercurial > hg > forks > gldragon
comparison gldragon.cpp @ 23:f080349584b8
Rename from glxdragon to gldragon.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 22 Nov 2019 05:29:34 +0200 |
parents | glxdragon.cpp@03b86b9c2f29 |
children | c1897cfc8463 |
comparison
equal
deleted
inserted
replaced
22:03b86b9c2f29 | 23:f080349584b8 |
---|---|
1 // | |
2 // GLDragon - OpenGL PLY model viewer / simple benchmark | |
3 // Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org> | |
4 // (C) Copyright 2019 Tecnic Software productions (TNSP) | |
5 // | |
6 // See file "COPYING" for license information. | |
7 // | |
8 // Originally based on 'glxdragon' Copyright (c) 2009, Thomas Trummer | |
9 // | |
10 #include <SDL.h> | |
11 #include <SDL_opengl.h> | |
12 #include <GL/glu.h> | |
13 #include <GL/glext.h> | |
14 | |
15 #include "dmutil.h" | |
16 #include "dmmodel.h" | |
17 | |
18 | |
19 /* Default settings etc. constants | |
20 */ | |
21 #define SET_DEF_WIDTH 1280 | |
22 #define SET_DEF_HEIGHT 960 | |
23 #define SET_FRAMES (180) | |
24 #define SET_MAX_SHADER_SIZE (128 * 1024) | |
25 | |
26 | |
27 /* Options | |
28 */ | |
29 bool optUseShaders = false; | |
30 int optWidth = SET_DEF_WIDTH, | |
31 optHeight = SET_DEF_HEIGHT, | |
32 optVSyncMode = 1; | |
33 | |
34 | |
35 | |
36 /* Globals | |
37 */ | |
38 SDL_Window *dmWindow = NULL; | |
39 SDL_GLContext dmGLContext = NULL; | |
40 | |
41 | |
42 /* Helpers | |
43 */ | |
44 bool dmInitSDLGL(const int width, const int height, const char *title) | |
45 { | |
46 int ret; | |
47 std::string msg; | |
48 | |
49 // Set GL attributes | |
50 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); | |
51 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); | |
52 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); | |
53 | |
54 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); | |
55 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); | |
56 | |
57 // Attempt to initialize libSDL | |
58 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_EVENTS) != 0) | |
59 { | |
60 printf("ERROR: Unable to initialize SDL: %s\n", | |
61 SDL_GetError()); | |
62 return false; | |
63 } | |
64 | |
65 // Attempt to create a window | |
66 if ((dmWindow = SDL_CreateWindow(title, | |
67 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, | |
68 width, height, | |
69 SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE)) == NULL) | |
70 { | |
71 printf("ERROR: Could not create SDL window: %s", | |
72 SDL_GetError()); | |
73 return false; | |
74 } | |
75 | |
76 if ((dmGLContext = SDL_GL_CreateContext(dmWindow)) == NULL) | |
77 { | |
78 printf("ERROR: Unable to create SDL OpenGL context: %s\n", | |
79 SDL_GetError()); | |
80 return false; | |
81 } | |
82 | |
83 // Check if we want to attempt to use vsync | |
84 switch (optVSyncMode) | |
85 { | |
86 case 3: | |
87 ret = SDL_GL_SetSwapInterval(-1); | |
88 msg = "adaptive vsync"; | |
89 break; | |
90 | |
91 case 2: | |
92 ret = SDL_GL_SetSwapInterval(1); | |
93 msg = "synchronized (vsync)"; | |
94 break; | |
95 | |
96 case 1: | |
97 ret = SDL_GL_SetSwapInterval(0); | |
98 msg = "immediate (no vsync)"; | |
99 break; | |
100 | |
101 default: | |
102 ret = -1; | |
103 msg = "INVALID VSYNC MODE"; | |
104 break; | |
105 } | |
106 | |
107 if (ret != 0) | |
108 { | |
109 printf("ERROR: Could not set vsync mode to %s.\n", | |
110 msg.c_str()); | |
111 return false; | |
112 } | |
113 | |
114 // Dump some information | |
115 printf( | |
116 "GL_VENDOR : %s\n" | |
117 "GL_RENDERER : %s\n" | |
118 "GL_VERSION : %s\n" | |
119 "VSync mode : %s\n", | |
120 glGetString(GL_VENDOR), | |
121 glGetString(GL_RENDERER), | |
122 glGetString(GL_VERSION), | |
123 msg.c_str()); | |
124 | |
125 // Setup the window and view port | |
126 glViewport(0, 0, width, height); | |
127 | |
128 glMatrixMode(GL_PROJECTION); | |
129 glLoadIdentity(); | |
130 | |
131 gluPerspective(45.0f, GLfloat(width) / GLfloat(height), 0.1f, 1000.0f); | |
132 | |
133 glMatrixMode(GL_MODELVIEW); | |
134 glLoadIdentity(); | |
135 | |
136 // Enable back face culling | |
137 glEnable(GL_CULL_FACE); | |
138 | |
139 // Enable smooth shading | |
140 glShadeModel(GL_SMOOTH); | |
141 | |
142 // Enable the depth buffer | |
143 glEnable(GL_DEPTH_TEST); | |
144 | |
145 // Enable normal rescaling | |
146 glEnable(GL_RESCALE_NORMAL); | |
147 | |
148 // Setup depth buffer | |
149 glClearDepth(1.0f); | |
150 | |
151 // Set the depth buffer function | |
152 glDepthFunc(GL_LEQUAL); | |
153 | |
154 // Enable vertex and and normal arrays | |
155 glEnableClientState(GL_VERTEX_ARRAY); | |
156 glEnableClientState(GL_NORMAL_ARRAY); | |
157 | |
158 // Set correct perspective correction | |
159 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); | |
160 | |
161 return true; | |
162 } | |
163 | |
164 | |
165 void dmDrawModel(const DMModel &model) | |
166 { | |
167 int maxIndices; | |
168 | |
169 if (optUseShaders) | |
170 { | |
171 // Enable shader program | |
172 glUseProgram(model.id_prog); | |
173 } | |
174 else | |
175 { | |
176 // Set the color of the model | |
177 glEnable(GL_LIGHTING); | |
178 glColor3ub(0x90, 0x80, 0x90); | |
179 } | |
180 | |
181 // Render the model | |
182 glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); | |
183 | |
184 glPushMatrix(); | |
185 | |
186 // Add transforms | |
187 glScalef(model.scale.x, model.scale.y, model.scale.z); | |
188 glTranslatef(model.translate.x, model.translate.y, model.translate. z); | |
189 glRotatef(model.rotate.x, 1.0f, 0.0f, 0.0f); | |
190 glRotatef(model.rotate.y, 0.0f, 1.0f, 0.0f); | |
191 glRotatef(model.rotate.z, 0.0f, 0.0f, 1.0f); | |
192 | |
193 glVertexPointer(3, GL_FLOAT, 0, &model.vertices[0]); | |
194 glNormalPointer( GL_FLOAT, 0, &model.normals[0]); | |
195 | |
196 for (int n = 0; n < model.nfaces; n += maxIndices) | |
197 { | |
198 const int count = std::min(maxIndices, model.nfaces - n); | |
199 glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_INT, &model.faces[n * 3]); | |
200 } | |
201 | |
202 glPopMatrix(); | |
203 | |
204 // Restore | |
205 if (optUseShaders) | |
206 { | |
207 glUseProgram(0); | |
208 } | |
209 } | |
210 | |
211 | |
212 void dmDrawScene(const DMSimpleScene &scene) | |
213 { | |
214 glClear(GL_DEPTH_BUFFER_BIT); | |
215 | |
216 glMatrixMode(GL_PROJECTION); | |
217 glPushMatrix(); | |
218 glLoadIdentity(); | |
219 | |
220 glOrtho(0.0, 1.0, 0.0, 1.0, -1, 1); | |
221 | |
222 glMatrixMode(GL_MODELVIEW); | |
223 glPushMatrix(); | |
224 glLoadIdentity(); | |
225 | |
226 // Draw the background gradient | |
227 glDisable(GL_DEPTH_TEST); | |
228 glDisable(GL_LIGHTING); | |
229 glBegin(GL_QUADS); | |
230 { | |
231 glColor3ub(0x3B, 0x3B, 0x75); | |
232 glVertex2f(0.0f, 0.0f); | |
233 glVertex2f(1.0f, 0.0f); | |
234 | |
235 glColor3ub(0x00, 0x00, 0x00); | |
236 glVertex2f(1.0f, 1.0f); | |
237 glVertex2f(0.0f, 1.0f); | |
238 } | |
239 glEnd(); | |
240 | |
241 | |
242 // Restore the 3D projection | |
243 glMatrixMode(GL_PROJECTION); | |
244 glPopMatrix(); | |
245 | |
246 glMatrixMode(GL_MODELVIEW); | |
247 glPopMatrix(); | |
248 | |
249 glEnable(GL_DEPTH_TEST); | |
250 | |
251 // Draw models | |
252 for (const DMModel &model : scene.models) | |
253 dmDrawModel(model); | |
254 } | |
255 | |
256 | |
257 GLuint dmCompileShader(const GLenum stype, const std::string &src) | |
258 { | |
259 GLuint shader = glCreateShader(stype); | |
260 const char *tmp = src.c_str(); | |
261 | |
262 glShaderSource(shader, 1, &tmp, 0); | |
263 glCompileShader(shader); | |
264 | |
265 return shader; | |
266 } | |
267 | |
268 | |
269 void dmLinkModelShaders(DMModel &model) | |
270 { | |
271 model.id_prog = glCreateProgram(); | |
272 glAttachShader(model.id_prog, model.id_fs); | |
273 glAttachShader(model.id_prog, model.id_vs); | |
274 glLinkProgram(model.id_prog); | |
275 } | |
276 | |
277 | |
278 int main(int argc, char *argv[]) | |
279 { | |
280 int startTime, cycleStart, cycleFrames = 0, totalFrames = 0; | |
281 double totalTime; | |
282 bool | |
283 exitFlag = false, | |
284 optShowHelp = false, | |
285 optSetInputFilename = false; | |
286 std::string optInputFilename = "dragon.scene", basePath; | |
287 DMSimpleScene scene; | |
288 | |
289 // Check commandline argument for enabling shaders | |
290 for (int narg = 1; narg < argc; narg++) | |
291 { | |
292 char *arg = argv[narg]; | |
293 if (arg[0] == '-') | |
294 { | |
295 char *opt = arg + 1; | |
296 | |
297 if ((opt[0] == '-' && opt[1] == 'h' && opt[2] == 'e') || | |
298 opt[0] == '?' || (opt[0] == '-' && opt[1] == '?')) | |
299 { | |
300 optShowHelp = true; | |
301 break; | |
302 } | |
303 else | |
304 if (opt[0] == '-') | |
305 opt++; | |
306 | |
307 if (opt[0] == 'g') | |
308 optUseShaders = true; | |
309 else | |
310 switch (opt[0]) | |
311 { | |
312 case 'w': | |
313 case 'h': | |
314 case 'm': | |
315 case 'v': | |
316 if (opt[1] == 0) | |
317 { | |
318 printf("Option '%s' requires an argument.\n", opt); | |
319 goto exit; | |
320 } | |
321 | |
322 switch (opt[0]) | |
323 { | |
324 case 'w': optWidth = atoi(opt + 1); break; | |
325 case 'h': optHeight = atoi(opt + 1); break; | |
326 case 'v': optVSyncMode = atoi(opt + 1); break; | |
327 } | |
328 break; | |
329 | |
330 default: | |
331 printf("Unknown option '%s'.\n", opt); | |
332 goto exit; | |
333 } | |
334 } | |
335 else | |
336 { | |
337 if (optSetInputFilename) | |
338 { | |
339 printf("ERROR: Please specify only one scene file.\n"); | |
340 goto exit; | |
341 } | |
342 | |
343 optSetInputFilename = true; | |
344 optInputFilename = std::string(arg); | |
345 if (optInputFilename.empty()) | |
346 { | |
347 printf("ERROR: Invalid input filename.\n"); | |
348 goto exit; | |
349 } | |
350 | |
351 } | |
352 } | |
353 | |
354 if (optShowHelp) | |
355 { | |
356 printf( | |
357 "Usage: %s [options] [<scenefile.scene>]\n" | |
358 "-? Show this help\n" | |
359 "-g Use GLSL shader instead of basic OpenGL lighting\n" | |
360 "-w<width> Window width (default %d)\n" | |
361 "-h<height> Window height (default %d)\n" | |
362 "-v<1-3> Set vsync mode: 1 = no vsync, 2 = vsync, 3 = adaptive\n" | |
363 " Default is no vsync. Using vsync will result in FPS being\n" | |
364 " approx whatever your monitor refresh rate is.\n" | |
365 "\n", | |
366 argv[0], | |
367 SET_DEF_WIDTH, SET_DEF_HEIGHT | |
368 ); | |
369 | |
370 goto exit; | |
371 } | |
372 | |
373 if (optWidth < 100 || optWidth > 8192 || optHeight < 100 || optHeight > 8192) | |
374 { | |
375 printf("ERROR: Invalid window width or height (%d x %d).\n", | |
376 optWidth, optHeight); | |
377 goto exit; | |
378 } | |
379 | |
380 // Load the scene | |
381 if (!scene.loadInfo(optInputFilename)) | |
382 goto exit; | |
383 | |
384 if (scene.models.size() == 0) | |
385 { | |
386 printf("ERROR: Scenefile '%s' contains no models.\n", | |
387 optInputFilename.c_str()); | |
388 goto exit; | |
389 } | |
390 | |
391 printf("INFO: Loading %ld model(s) ..\n", | |
392 scene.models.size()); | |
393 | |
394 basePath = dmGetPath(optInputFilename); | |
395 printf("INFO: Model base path '%s'\n", basePath.c_str()); | |
396 | |
397 for (DMModel &model : scene.models) | |
398 { | |
399 if (!model.loadFromPLY(basePath + model.modelFile)) | |
400 goto exit; | |
401 | |
402 if (optUseShaders) | |
403 { | |
404 std::string | |
405 fragFile = model.fragShaderFile.empty() ? "shader.frag" : basePath + model.fragShaderFile, | |
406 vertFile = model.vertShaderFile.empty() ? "shader.vert" : basePath + model.vertShaderFile; | |
407 | |
408 if (!dmReadText(fragFile, model.fragShaderStr, SET_MAX_SHADER_SIZE) || | |
409 !dmReadText(vertFile, model.vertShaderStr, SET_MAX_SHADER_SIZE)) | |
410 goto exit; | |
411 } | |
412 } | |
413 | |
414 // Initialize SDL + OpenGL | |
415 if (!dmInitSDLGL(optWidth, optHeight, "GLDragon")) | |
416 goto exit; | |
417 | |
418 // According to our mode .. | |
419 if (optUseShaders) | |
420 { | |
421 for (DMModel &model : scene.models) | |
422 { | |
423 model.id_fs = dmCompileShader(GL_FRAGMENT_SHADER, model.fragShaderStr); | |
424 model.id_vs = dmCompileShader(GL_VERTEX_SHADER, model.vertShaderStr); | |
425 dmLinkModelShaders(model); | |
426 } | |
427 } | |
428 else | |
429 { | |
430 float specReflection[] = { 0.8f, 0.8f, 0.8f, 1.0f }; | |
431 | |
432 glEnable(GL_COLOR_MATERIAL); | |
433 | |
434 glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); | |
435 glMateriali(GL_FRONT, GL_SHININESS, 96); | |
436 glMaterialfv(GL_FRONT, GL_SPECULAR, specReflection); | |
437 | |
438 glEnable(GL_LIGHT0); | |
439 | |
440 // Define the light components and position | |
441 GLfloat ambient[] = { 0.2f, 0.2f, 0.2f, 1.0f }; | |
442 GLfloat diffuse[] = { 0.8f, 0.8f, 0.8f, 1.0f }; | |
443 GLfloat specular[] = { 0.5f, 0.5f, 0.5f, 1.0f }; | |
444 GLfloat position[] = { 10.0f, 10.0f, 0.0f, 0.0f }; | |
445 | |
446 // Define the light components and position | |
447 glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); | |
448 glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); | |
449 glLightfv(GL_LIGHT0, GL_SPECULAR, specular); | |
450 glLightfv(GL_LIGHT0, GL_POSITION, position); | |
451 } | |
452 | |
453 // Define the camera | |
454 gluLookAt(0, 0.12, 0.24, 0, 0.12, 0, 0, 1, 0); | |
455 | |
456 | |
457 // Main loop starts | |
458 startTime = cycleStart = SDL_GetTicks(); | |
459 | |
460 while (!exitFlag) | |
461 { | |
462 SDL_Event event; | |
463 | |
464 // Check for quit events | |
465 while (SDL_PollEvent(&event)) | |
466 switch (event.type) | |
467 { | |
468 case SDL_QUIT: | |
469 exitFlag = true; | |
470 break; | |
471 | |
472 case SDL_KEYDOWN: | |
473 switch (event.key.keysym.sym) | |
474 { | |
475 case SDLK_ESCAPE: | |
476 case SDLK_q: | |
477 exitFlag = true; | |
478 break; | |
479 } | |
480 } | |
481 | |
482 // Render the next frame | |
483 dmDrawScene(scene); | |
484 | |
485 // Draw the current frame | |
486 SDL_GL_SwapWindow(dmWindow); | |
487 | |
488 // Rotate for 2 degrees | |
489 glRotatef(2.0f, 0, 1, 0); | |
490 | |
491 // Return true if a full rotation was done | |
492 totalFrames++; | |
493 if (cycleFrames++ == SET_FRAMES) | |
494 { | |
495 // Reset cycleFrames | |
496 cycleFrames = 0; | |
497 | |
498 // Get the time it took to render a full turn | |
499 int cycleEnd = SDL_GetTicks(); | |
500 double cycleTime = cycleEnd - cycleStart; | |
501 | |
502 // Restart the timer | |
503 cycleStart = SDL_GetTicks(); | |
504 | |
505 // Print the current frames per second | |
506 printf("%.1lf ms for %d frames = %.1lf FPS\n", | |
507 cycleTime, SET_FRAMES, (SET_FRAMES * 1000.0f) / cycleTime); | |
508 } | |
509 } | |
510 | |
511 // Show totals | |
512 totalTime = SDL_GetTicks() - startTime; | |
513 printf("%.1lf ms total for %d total frames = %.2lf FPS average\n", | |
514 totalTime, totalFrames, (totalFrames * 1000.0f) / totalTime); | |
515 | |
516 exit: | |
517 if (dmGLContext != NULL) | |
518 SDL_GL_DeleteContext(dmGLContext); | |
519 | |
520 if (dmWindow != NULL) | |
521 SDL_DestroyWindow(dmWindow); | |
522 | |
523 SDL_Quit(); | |
524 | |
525 return 0; | |
526 } |