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