Mercurial > hg > forks > gldragon
comparison glxdragon.cpp @ 21:1404dfcee7b8
More work on scenefile and model loading support.
Can now load PLY models and simple scene definition files.
Converted dragon mesh to binary PLY format.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 22 Nov 2019 03:03:52 +0200 |
parents | 294c4c7943b5 |
children | 03b86b9c2f29 |
comparison
equal
deleted
inserted
replaced
20:294c4c7943b5 | 21:1404dfcee7b8 |
---|---|
31 #include <SDL.h> | 31 #include <SDL.h> |
32 #include <SDL_opengl.h> | 32 #include <SDL_opengl.h> |
33 #include <GL/glu.h> | 33 #include <GL/glu.h> |
34 #include <GL/glext.h> | 34 #include <GL/glext.h> |
35 | 35 |
36 #include <iostream> | 36 #include "dmutil.h" |
37 #include <fstream> | 37 #include "dmmodel.h" |
38 #include <string> | |
39 #include <vector> | |
40 #include <cstdio> | |
41 | 38 |
42 | 39 |
43 /* Default settings etc. constants | 40 /* Default settings etc. constants |
44 */ | 41 */ |
45 #define SET_DEF_WIDTH 1280 | 42 #define SET_DEF_WIDTH 1280 |
46 #define SET_DEF_HEIGHT 960 | 43 #define SET_DEF_HEIGHT 960 |
47 #define SET_FRAMES (180) | 44 #define SET_FRAMES (180) |
48 #define SET_MAX_SHADER_SIZE (128 * 1024) | 45 #define SET_MAX_SHADER_SIZE (128 * 1024) |
49 | 46 |
50 | 47 |
51 /* Structures | |
52 */ | |
53 struct Mesh | |
54 { | |
55 int nvertices, nfaces; | |
56 | |
57 std::vector<float> vertices; | |
58 std::vector<unsigned> faces; | |
59 | |
60 GLuint id_prog, id_fs, id_vs; | |
61 }; | |
62 | |
63 | |
64 /* Options | 48 /* Options |
65 */ | 49 */ |
66 bool optUseShaders = false; | 50 bool optUseShaders = false; |
67 int optWidth = SET_DEF_WIDTH, | 51 int optWidth = SET_DEF_WIDTH, |
68 optHeight = SET_DEF_HEIGHT, | 52 optHeight = SET_DEF_HEIGHT, |
69 optVSyncMode = 1; | 53 optVSyncMode = 1; |
70 | 54 |
71 std::string optModelPrefix = "dragon"; | |
72 | 55 |
73 | 56 |
74 /* Globals | 57 /* Globals |
75 */ | 58 */ |
76 SDL_Window *dmWindow = NULL; | 59 SDL_Window *dmWindow = NULL; |
77 SDL_GLContext dmGLContext = NULL; | 60 SDL_GLContext dmGLContext = NULL; |
78 | 61 |
79 | 62 |
63 /* Helpers | |
64 */ | |
80 bool dmInitSDLGL(const int width, const int height, const char *title) | 65 bool dmInitSDLGL(const int width, const int height, const char *title) |
81 { | 66 { |
82 int ret; | 67 int ret; |
83 std::string msg; | 68 std::string msg; |
84 | 69 |
167 gluPerspective(45.0f, GLfloat(width) / GLfloat(height), 0.1f, 1000.0f); | 152 gluPerspective(45.0f, GLfloat(width) / GLfloat(height), 0.1f, 1000.0f); |
168 | 153 |
169 glMatrixMode(GL_MODELVIEW); | 154 glMatrixMode(GL_MODELVIEW); |
170 glLoadIdentity(); | 155 glLoadIdentity(); |
171 | 156 |
172 | |
173 // Enable back face culling | 157 // Enable back face culling |
174 glEnable(GL_CULL_FACE); | 158 glEnable(GL_CULL_FACE); |
175 | 159 |
176 // Enable smooth shading | 160 // Enable smooth shading |
177 glShadeModel(GL_SMOOTH); | 161 glShadeModel(GL_SMOOTH); |
178 | 162 |
179 // Enable the depth buffer | 163 // Enable the depth buffer |
180 glEnable(GL_DEPTH_TEST); | 164 glEnable(GL_DEPTH_TEST); |
165 | |
166 // Enable normal rescaling | |
167 glEnable(GL_RESCALE_NORMAL); | |
181 | 168 |
182 // Setup depth buffer | 169 // Setup depth buffer |
183 glClearDepth(1.0f); | 170 glClearDepth(1.0f); |
184 | 171 |
185 // Set the depth buffer function | 172 // Set the depth buffer function |
194 | 181 |
195 return true; | 182 return true; |
196 } | 183 } |
197 | 184 |
198 | 185 |
199 void dmDrawModelVA(const Mesh &mesh) | 186 void dmDrawModel(const DMModel &model) |
200 { | 187 { |
201 int maxIndices; | 188 int maxIndices; |
202 | 189 |
203 if (optUseShaders) | 190 if (optUseShaders) |
204 { | 191 { |
205 // Enable shader program | 192 // Enable shader program |
206 glUseProgram(mesh.id_prog); | 193 glUseProgram(model.id_prog); |
207 } | 194 } |
208 else | 195 else |
209 { | 196 { |
210 // Set the color of the model | 197 // Set the color of the model |
211 glEnable(GL_LIGHTING); | 198 glEnable(GL_LIGHTING); |
213 } | 200 } |
214 | 201 |
215 // Render the model | 202 // Render the model |
216 glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); | 203 glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices); |
217 | 204 |
218 glVertexPointer(3, GL_FLOAT, 3 * 4 * 2, &mesh.vertices[0]); | 205 glPushMatrix(); |
219 glNormalPointer( GL_FLOAT, 3 * 4 * 2, &mesh.vertices[3]); | 206 |
220 | 207 // Add transforms |
221 for (int n = 0; n < mesh.nfaces; n += maxIndices) | 208 glScalef(model.scale.x, model.scale.y, model.scale.z); |
222 { | 209 glTranslatef(model.translate.x, model.translate.y, model.translate. z); |
223 const int count = std::min(maxIndices, mesh.nfaces - n); | 210 glRotatef(model.rotate.x, 1.0f, 0.0f, 0.0f); |
224 glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_INT, &mesh.faces[n * 3]); | 211 glRotatef(model.rotate.y, 0.0f, 1.0f, 0.0f); |
225 } | 212 glRotatef(model.rotate.z, 0.0f, 0.0f, 1.0f); |
213 | |
214 glVertexPointer(3, GL_FLOAT, 0, &model.vertices[0]); | |
215 glNormalPointer( GL_FLOAT, 0, &model.normals[0]); | |
216 | |
217 for (int n = 0; n < model.nfaces; n += maxIndices) | |
218 { | |
219 const int count = std::min(maxIndices, model.nfaces - n); | |
220 glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_INT, &model.faces[n * 3]); | |
221 } | |
222 | |
223 glPopMatrix(); | |
226 | 224 |
227 // Restore | 225 // Restore |
228 if (optUseShaders) | 226 if (optUseShaders) |
229 { | 227 { |
230 glUseProgram(0); | 228 glUseProgram(0); |
231 } | 229 } |
232 } | 230 } |
233 | 231 |
234 | 232 |
235 void dmDrawBackground() | 233 void dmDrawScene(const DMSimpleScene &scene) |
236 { | 234 { |
237 glClear(GL_DEPTH_BUFFER_BIT); | 235 glClear(GL_DEPTH_BUFFER_BIT); |
238 | 236 |
239 glMatrixMode(GL_PROJECTION); | 237 glMatrixMode(GL_PROJECTION); |
240 glPushMatrix(); | 238 glPushMatrix(); |
268 | 266 |
269 glMatrixMode(GL_MODELVIEW); | 267 glMatrixMode(GL_MODELVIEW); |
270 glPopMatrix(); | 268 glPopMatrix(); |
271 | 269 |
272 glEnable(GL_DEPTH_TEST); | 270 glEnable(GL_DEPTH_TEST); |
273 } | 271 |
274 | 272 // Draw models |
275 | 273 for (const DMModel &model : scene.models) |
276 bool dmReadText(const std::string &filename, std::string &buf, const int maxSize) | 274 dmDrawModel(model); |
277 { | |
278 std::ifstream in(filename.c_str()); | |
279 | |
280 if (!in.is_open()) | |
281 { | |
282 printf("ERROR: Unable to open file '%s'.\n", | |
283 filename.c_str()); | |
284 return false; | |
285 } | |
286 | |
287 in.seekg(0, std::ios::end); | |
288 | |
289 if (in.tellg() > maxSize) | |
290 { | |
291 printf("ERROR: File '%s' is too large.\n", | |
292 filename.c_str()); | |
293 return false; | |
294 } | |
295 | |
296 buf.reserve(in.tellg()); | |
297 in.seekg(0, std::ios::beg); | |
298 | |
299 buf.assign((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>()); | |
300 | |
301 return true; | |
302 } | |
303 | |
304 | |
305 bool dmLoadMesh(const std::string &filename, const std::string &infofilename, Mesh &mesh) | |
306 { | |
307 std::ifstream info(infofilename.c_str()); | |
308 int required = 0, nline = 0; | |
309 | |
310 printf("INFO: Trying to read mesh meta from '%s'.\n", | |
311 infofilename.c_str()); | |
312 | |
313 if (!info.is_open()) | |
314 { | |
315 printf("ERROR: Unable to open file '%s'.\n", | |
316 infofilename.c_str()); | |
317 return false; | |
318 } | |
319 | |
320 while (required != 0x03) | |
321 { | |
322 std::string tmp; | |
323 int value; | |
324 char key; | |
325 | |
326 // Read one line | |
327 if (!std::getline(info, tmp)) | |
328 { | |
329 printf("ERROR: Could not read from file '%s'.\n", | |
330 infofilename.c_str()); | |
331 return false; | |
332 } | |
333 | |
334 nline++; | |
335 | |
336 // Skip empty lines and comments | |
337 if (tmp.empty() || tmp.substr(0, 1) == "#") | |
338 continue; | |
339 | |
340 if (sscanf(tmp.c_str(), "%c:%d", &key, &value) != 2) | |
341 { | |
342 printf("ERROR: Syntax error in '%s' line #%d\n'%s'\n", | |
343 infofilename.c_str(), | |
344 nline, | |
345 tmp.c_str()); | |
346 return false; | |
347 } | |
348 | |
349 switch (key) | |
350 { | |
351 case 'v': | |
352 mesh.nvertices = value; | |
353 required |= 0x01; | |
354 break; | |
355 | |
356 case 'f': | |
357 mesh.nfaces = value; | |
358 required |= 0x02; | |
359 break; | |
360 | |
361 default: | |
362 printf("ERROR: Syntax error in '%s' line #%d\nUnknown key value '%c'.\n", | |
363 infofilename.c_str(), | |
364 nline, | |
365 key); | |
366 break; | |
367 } | |
368 } | |
369 | |
370 if (mesh.nvertices < 3 || mesh.nfaces < 1) | |
371 { | |
372 printf("ERROR: Invalid nvertices (%d) and/or nfaces (%d) in '%s'.\n", | |
373 mesh.nvertices, mesh.nfaces, | |
374 infofilename.c_str()); | |
375 return false; | |
376 } | |
377 | |
378 printf("INFO: %d vertices, %d faces\n", mesh.nvertices, mesh.nfaces); | |
379 printf("INFO: Trying to read mesh data from '%s'.\n", | |
380 filename.c_str()); | |
381 | |
382 std::ifstream in(filename.c_str(), std::ios::binary); | |
383 | |
384 if (!in.is_open()) | |
385 { | |
386 printf("ERROR: Unable to open file '%s'.\n", | |
387 filename.c_str()); | |
388 return false; | |
389 } | |
390 | |
391 mesh.vertices.resize(mesh.nvertices * 6); | |
392 in.read(reinterpret_cast<char*>(&mesh.vertices[0]), mesh.nvertices * 6 * 4); | |
393 | |
394 mesh.faces.resize(mesh.nfaces * 3); | |
395 | |
396 for (int i = 0; i < mesh.nfaces; i++) | |
397 { | |
398 in.seekg(1, std::ios::cur); | |
399 in.read(reinterpret_cast<char*>(&mesh.faces[i * 3]), 3 * 4); | |
400 } | |
401 | |
402 return true; | |
403 } | 275 } |
404 | 276 |
405 | 277 |
406 GLuint dmCompileShader(const GLenum stype, const std::string &src) | 278 GLuint dmCompileShader(const GLenum stype, const std::string &src) |
407 { | 279 { |
413 | 285 |
414 return shader; | 286 return shader; |
415 } | 287 } |
416 | 288 |
417 | 289 |
418 void dmLinkMeshShaders(Mesh &mesh) | 290 void dmLinkModelShaders(DMModel &model) |
419 { | 291 { |
420 mesh.id_prog = glCreateProgram(); | 292 model.id_prog = glCreateProgram(); |
421 glAttachShader(mesh.id_prog, mesh.id_fs); | 293 glAttachShader(model.id_prog, model.id_fs); |
422 glAttachShader(mesh.id_prog, mesh.id_vs); | 294 glAttachShader(model.id_prog, model.id_vs); |
423 glLinkProgram(mesh.id_prog); | 295 glLinkProgram(model.id_prog); |
424 } | 296 } |
425 | 297 |
426 | 298 |
427 int main(int argc, char *argv[]) | 299 int main(int argc, char *argv[]) |
428 { | 300 { |
429 std::string modelVertStr, modelFragStr; | |
430 Mesh modelMesh; | |
431 bool exitFlag = false, optShowHelp = false; | |
432 int startTime, cycleStart, cycleFrames = 0, totalFrames = 0; | 301 int startTime, cycleStart, cycleFrames = 0, totalFrames = 0; |
433 double totalTime; | 302 double totalTime; |
303 bool | |
304 exitFlag = false, | |
305 optShowHelp = false, | |
306 optSetInputFilename = false; | |
307 std::string optInputFilename = "dragon.scene", basePath; | |
308 DMSimpleScene scene; | |
434 | 309 |
435 // Check commandline argument for enabling shaders | 310 // Check commandline argument for enabling shaders |
436 for (int narg = 1; narg < argc; narg++) | 311 for (int narg = 1; narg < argc; narg++) |
437 { | 312 { |
438 char *arg = argv[narg]; | 313 char *arg = argv[narg]; |
467 | 342 |
468 switch (opt[0]) | 343 switch (opt[0]) |
469 { | 344 { |
470 case 'w': optWidth = atoi(opt + 1); break; | 345 case 'w': optWidth = atoi(opt + 1); break; |
471 case 'h': optHeight = atoi(opt + 1); break; | 346 case 'h': optHeight = atoi(opt + 1); break; |
472 case 'm': optModelPrefix = std::string(opt + 1); break; | |
473 case 'v': optVSyncMode = atoi(opt + 1); break; | 347 case 'v': optVSyncMode = atoi(opt + 1); break; |
474 } | 348 } |
475 break; | 349 break; |
476 | 350 |
477 default: | 351 default: |
478 printf("Unknown option '%s'.\n", opt); | 352 printf("Unknown option '%s'.\n", opt); |
479 goto exit; | 353 goto exit; |
480 } | 354 } |
481 } | 355 } |
356 else | |
357 { | |
358 if (optSetInputFilename) | |
359 { | |
360 printf("ERROR: Please specify only one scene file.\n"); | |
361 goto exit; | |
362 } | |
363 | |
364 optSetInputFilename = true; | |
365 optInputFilename = std::string(arg); | |
366 if (optInputFilename.empty()) | |
367 { | |
368 printf("ERROR: Invalid input filename.\n"); | |
369 goto exit; | |
370 } | |
371 | |
372 } | |
482 } | 373 } |
483 | 374 |
484 if (optShowHelp) | 375 if (optShowHelp) |
485 { | 376 { |
486 printf( | 377 printf( |
487 "Usage: %s [options]\n" | 378 "Usage: %s [options] [<scenefile.scene>]\n" |
488 "-? Show this help\n" | 379 "-? Show this help\n" |
489 "-g Use GLSL shader instead of basic OpenGL lighting\n" | 380 "-g Use GLSL shader instead of basic OpenGL lighting\n" |
490 "-w<width> Window width (default %d)\n" | 381 "-w<width> Window width (default %d)\n" |
491 "-h<height> Window height (default %d)\n" | 382 "-h<height> Window height (default %d)\n" |
492 "-m<modelfile> Set model filenames prefix. Using \"-mfoo\" will\n" | |
493 " specify \"foo.mesh\", \"foo.frag\", \"foo.vert\".\n" | |
494 "-v<1-3> Set vsync mode: 1 = no vsync, 2 = vsync, 3 = adaptive\n" | 383 "-v<1-3> Set vsync mode: 1 = no vsync, 2 = vsync, 3 = adaptive\n" |
495 " Default is no vsync.\n" | 384 " Default is no vsync. Using vsync will result in FPS being\n" |
385 " approx whatever your monitor refresh rate is.\n" | |
496 "\n", | 386 "\n", |
497 argv[0], | 387 argv[0], |
498 SET_DEF_WIDTH, SET_DEF_HEIGHT | 388 SET_DEF_WIDTH, SET_DEF_HEIGHT |
499 ); | 389 ); |
500 | 390 |
506 printf("ERROR: Invalid window width or height (%d x %d).\n", | 396 printf("ERROR: Invalid window width or height (%d x %d).\n", |
507 optWidth, optHeight); | 397 optWidth, optHeight); |
508 goto exit; | 398 goto exit; |
509 } | 399 } |
510 | 400 |
511 if (optModelPrefix.empty()) | 401 // Load the scene |
512 { | 402 if (!scene.loadInfo(optInputFilename)) |
513 printf("Model file prefix empty.\n"); | |
514 goto exit; | 403 goto exit; |
515 } | 404 |
516 | 405 if (scene.models.size() == 0) |
517 if (!dmLoadMesh(optModelPrefix + ".mesh", optModelPrefix + ".info", modelMesh)) | 406 { |
407 printf("ERROR: Scenefile '%s' contains no models.\n", | |
408 optInputFilename.c_str()); | |
518 goto exit; | 409 goto exit; |
519 | 410 } |
520 if (optUseShaders) | 411 |
521 { | 412 printf("INFO: Loading %ld model(s) ..\n", |
522 // Read shader files | 413 scene.models.size()); |
523 if (!dmReadText(optModelPrefix + ".frag", modelFragStr, SET_MAX_SHADER_SIZE) || | 414 |
524 !dmReadText(optModelPrefix + ".vert", modelVertStr, SET_MAX_SHADER_SIZE)) | 415 basePath = dmGetPath(optInputFilename); |
416 printf("INFO: Model base path '%s'\n", basePath.c_str()); | |
417 | |
418 for (DMModel &model : scene.models) | |
419 { | |
420 if (!model.loadFromPLY(basePath + model.modelFile)) | |
525 goto exit; | 421 goto exit; |
422 | |
423 if (optUseShaders) | |
424 { | |
425 std::string | |
426 fragFile = model.fragShaderFile.empty() ? "shader.frag" : basePath + model.fragShaderFile, | |
427 vertFile = model.vertShaderFile.empty() ? "shader.vert" : basePath + model.vertShaderFile; | |
428 | |
429 if (!dmReadText(fragFile, model.fragShaderStr, SET_MAX_SHADER_SIZE) || | |
430 !dmReadText(vertFile, model.vertShaderStr, SET_MAX_SHADER_SIZE)) | |
431 goto exit; | |
432 } | |
526 } | 433 } |
527 | 434 |
528 // Initialize SDL + OpenGL | 435 // Initialize SDL + OpenGL |
529 if (!dmInitSDLGL(optWidth, optHeight, "GLXDragon2")) | 436 if (!dmInitSDLGL(optWidth, optHeight, "GLDragon")) |
530 goto exit; | 437 goto exit; |
531 | 438 |
532 // According to our mode .. | 439 // According to our mode .. |
533 if (optUseShaders) | 440 if (optUseShaders) |
534 { | 441 { |
535 modelMesh.id_fs = dmCompileShader(GL_FRAGMENT_SHADER, modelFragStr); | 442 for (DMModel &model : scene.models) |
536 modelMesh.id_vs = dmCompileShader(GL_VERTEX_SHADER, modelVertStr); | 443 { |
537 dmLinkMeshShaders(modelMesh); | 444 model.id_fs = dmCompileShader(GL_FRAGMENT_SHADER, model.fragShaderStr); |
445 model.id_vs = dmCompileShader(GL_VERTEX_SHADER, model.vertShaderStr); | |
446 dmLinkModelShaders(model); | |
447 } | |
538 } | 448 } |
539 else | 449 else |
540 { | 450 { |
541 float specReflection[] = { 0.8f, 0.8f, 0.8f, 1.0f }; | 451 float specReflection[] = { 0.8f, 0.8f, 0.8f, 1.0f }; |
542 | 452 |
589 break; | 499 break; |
590 } | 500 } |
591 } | 501 } |
592 | 502 |
593 // Render the next frame | 503 // Render the next frame |
594 dmDrawBackground(); | 504 dmDrawScene(scene); |
595 dmDrawModelVA(modelMesh); | |
596 | 505 |
597 // Draw the current frame | 506 // Draw the current frame |
598 SDL_GL_SwapWindow(dmWindow); | 507 SDL_GL_SwapWindow(dmWindow); |
599 | 508 |
600 // Rotate for 2 degrees | 509 // Rotate for 2 degrees |