diff 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
line wrap: on
line diff
--- a/glxdragon.cpp	Fri Nov 22 00:14:16 2019 +0200
+++ b/glxdragon.cpp	Fri Nov 22 03:03:52 2019 +0200
@@ -33,11 +33,8 @@
 #include <GL/glu.h>
 #include <GL/glext.h>
 
-#include <iostream>
-#include <fstream>
-#include <string>
-#include <vector>
-#include <cstdio>
+#include "dmutil.h"
+#include "dmmodel.h"
 
 
 /* Default settings etc. constants
@@ -48,19 +45,6 @@
 #define SET_MAX_SHADER_SIZE  (128 * 1024)
 
 
-/* Structures
- */
-struct Mesh
-{
-    int nvertices, nfaces;
-
-    std::vector<float>    vertices;
-    std::vector<unsigned> faces;
-
-    GLuint id_prog, id_fs, id_vs;
-};
-
-
 /* Options
  */
 bool   optUseShaders = false;
@@ -68,7 +52,6 @@
        optHeight = SET_DEF_HEIGHT,
        optVSyncMode = 1;
 
-std::string optModelPrefix = "dragon";
 
 
 /* Globals
@@ -77,6 +60,8 @@
 SDL_GLContext dmGLContext = NULL;
 
 
+/* Helpers
+ */
 bool dmInitSDLGL(const int width, const int height, const char *title)
 {
     int ret;
@@ -169,7 +154,6 @@
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
 
-
     // Enable back face culling
     glEnable(GL_CULL_FACE);
 
@@ -179,6 +163,9 @@
     // Enable the depth buffer
     glEnable(GL_DEPTH_TEST);
 
+    // Enable normal rescaling
+    glEnable(GL_RESCALE_NORMAL);
+
     // Setup depth buffer
     glClearDepth(1.0f);
 
@@ -196,14 +183,14 @@
 }
 
 
-void dmDrawModelVA(const Mesh &mesh)
+void dmDrawModel(const DMModel &model)
 {
     int maxIndices;
 
     if (optUseShaders)
     {
         // Enable shader program
-        glUseProgram(mesh.id_prog);
+        glUseProgram(model.id_prog);
     }
     else
     {
@@ -215,15 +202,26 @@
     // Render the model
     glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &maxIndices);
 
-    glVertexPointer(3, GL_FLOAT, 3 * 4 * 2, &mesh.vertices[0]);
-    glNormalPointer(   GL_FLOAT, 3 * 4 * 2, &mesh.vertices[3]);
+    glPushMatrix();
+
+    // Add transforms
+    glScalef(model.scale.x, model.scale.y, model.scale.z);
+    glTranslatef(model.translate.x, model.translate.y, model.translate. z);
+    glRotatef(model.rotate.x, 1.0f, 0.0f, 0.0f);
+    glRotatef(model.rotate.y, 0.0f, 1.0f, 0.0f);
+    glRotatef(model.rotate.z, 0.0f, 0.0f, 1.0f);
 
-    for (int n = 0; n < mesh.nfaces; n += maxIndices)
+    glVertexPointer(3, GL_FLOAT, 0, &model.vertices[0]);
+    glNormalPointer(   GL_FLOAT, 0, &model.normals[0]);
+
+    for (int n = 0; n < model.nfaces; n += maxIndices)
     {
-        const int count = std::min(maxIndices, mesh.nfaces - n);
-        glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_INT, &mesh.faces[n * 3]);
+        const int count = std::min(maxIndices, model.nfaces - n);
+        glDrawElements(GL_TRIANGLES, count * 3, GL_UNSIGNED_INT, &model.faces[n * 3]);
     }
 
+    glPopMatrix();
+
     // Restore
     if (optUseShaders)
     {
@@ -232,7 +230,7 @@
 }
 
 
-void dmDrawBackground()
+void dmDrawScene(const DMSimpleScene &scene)
 {
     glClear(GL_DEPTH_BUFFER_BIT);
 
@@ -270,136 +268,10 @@
     glPopMatrix();
 
     glEnable(GL_DEPTH_TEST);
-}
 
-
-bool dmReadText(const std::string &filename, std::string &buf, const int maxSize)
-{
-    std::ifstream in(filename.c_str());
-
-    if (!in.is_open())
-    {
-        printf("ERROR: Unable to open file '%s'.\n",
-            filename.c_str());
-        return false;
-    }
-
-    in.seekg(0, std::ios::end);
-
-    if (in.tellg() > maxSize)
-    {
-        printf("ERROR: File '%s' is too large.\n",
-            filename.c_str());
-        return false;
-    }
-
-    buf.reserve(in.tellg());
-    in.seekg(0, std::ios::beg);
-
-    buf.assign((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
-
-    return true;
-}
-
-
-bool dmLoadMesh(const std::string &filename, const std::string &infofilename, Mesh &mesh)
-{
-    std::ifstream info(infofilename.c_str());
-    int required = 0, nline = 0;
-
-    printf("INFO: Trying to read mesh meta from '%s'.\n",
-        infofilename.c_str());
-
-    if (!info.is_open())
-    {
-        printf("ERROR: Unable to open file '%s'.\n",
-            infofilename.c_str());
-        return false;
-    }
-
-    while (required != 0x03)
-    {
-        std::string tmp;
-        int value;
-        char key;
-
-        // Read one line
-        if (!std::getline(info, tmp))
-        {
-            printf("ERROR: Could not read from file '%s'.\n",
-                infofilename.c_str());
-            return false;
-        }
-
-        nline++;
-
-        // Skip empty lines and comments
-        if (tmp.empty() || tmp.substr(0, 1) == "#")
-            continue;
-
-        if (sscanf(tmp.c_str(), "%c:%d", &key, &value) != 2)
-        {
-            printf("ERROR: Syntax error in '%s' line #%d\n'%s'\n",
-                infofilename.c_str(),
-                nline,
-                tmp.c_str());
-            return false;
-        }
-
-        switch (key)
-        {
-            case 'v':
-                mesh.nvertices = value;
-                required |= 0x01;
-                break;
-
-            case 'f':
-                mesh.nfaces = value;
-                required |= 0x02;
-                break;
-
-            default:
-                printf("ERROR: Syntax error in '%s' line #%d\nUnknown key value '%c'.\n",
-                    infofilename.c_str(),
-                    nline,
-                    key);
-                break;
-        }
-    }
-
-    if (mesh.nvertices < 3 || mesh.nfaces < 1)
-    {
-        printf("ERROR: Invalid nvertices (%d) and/or nfaces (%d) in '%s'.\n",
-            mesh.nvertices, mesh.nfaces,
-            infofilename.c_str());
-        return false;
-    }
-
-    printf("INFO: %d vertices, %d faces\n", mesh.nvertices, mesh.nfaces);
-    printf("INFO: Trying to read mesh data from '%s'.\n",
-        filename.c_str());
-
-    std::ifstream in(filename.c_str(), std::ios::binary);
-
-    if (!in.is_open())
-    {
-        printf("ERROR: Unable to open file '%s'.\n",
-            filename.c_str());
-        return false;
-    }
-
-    mesh.vertices.resize(mesh.nvertices * 6);
-    in.read(reinterpret_cast<char*>(&mesh.vertices[0]), mesh.nvertices * 6 * 4);
-
-    mesh.faces.resize(mesh.nfaces * 3);
-
-    for (int i = 0; i < mesh.nfaces; i++)
-    {
-        in.seekg(1, std::ios::cur);
-        in.read(reinterpret_cast<char*>(&mesh.faces[i * 3]), 3 * 4);
-    }
-
-    return true;
+    // Draw models
+    for (const DMModel &model : scene.models)
+        dmDrawModel(model);
 }
 
 
@@ -415,22 +287,25 @@
 }
 
 
-void dmLinkMeshShaders(Mesh &mesh)
+void dmLinkModelShaders(DMModel &model)
 {
-    mesh.id_prog = glCreateProgram();
-    glAttachShader(mesh.id_prog, mesh.id_fs);
-    glAttachShader(mesh.id_prog, mesh.id_vs);
-    glLinkProgram(mesh.id_prog);
+    model.id_prog = glCreateProgram();
+    glAttachShader(model.id_prog, model.id_fs);
+    glAttachShader(model.id_prog, model.id_vs);
+    glLinkProgram(model.id_prog);
 }
 
 
 int main(int argc, char *argv[])
 {
-    std::string modelVertStr, modelFragStr;
-    Mesh modelMesh;
-    bool exitFlag = false, optShowHelp = false;
     int startTime, cycleStart, cycleFrames = 0, totalFrames = 0;
     double totalTime;
+    bool
+        exitFlag = false,
+        optShowHelp = false,
+        optSetInputFilename = false;
+    std::string optInputFilename = "dragon.scene", basePath;
+    DMSimpleScene scene;
 
     // Check commandline argument for enabling shaders
     for (int narg = 1; narg < argc; narg++)
@@ -469,7 +344,6 @@
                     {
                         case 'w': optWidth = atoi(opt + 1); break;
                         case 'h': optHeight = atoi(opt + 1); break;
-                        case 'm': optModelPrefix = std::string(opt + 1); break;
                         case 'v': optVSyncMode = atoi(opt + 1); break;
                     }
                     break;
@@ -479,20 +353,36 @@
                     goto exit;
             }
         }
+        else
+        {
+            if (optSetInputFilename)
+            {
+                printf("ERROR: Please specify only one scene file.\n");
+                goto exit;
+            }
+
+            optSetInputFilename = true;
+            optInputFilename = std::string(arg);
+            if (optInputFilename.empty())
+            {
+                printf("ERROR: Invalid input filename.\n");
+                goto exit;
+            }
+
+        }
     }
 
     if (optShowHelp)
     {
         printf(
-            "Usage: %s [options]\n"
+            "Usage: %s [options] [<scenefile.scene>]\n"
             "-?            Show this help\n"
             "-g            Use GLSL shader instead of basic OpenGL lighting\n"
             "-w<width>     Window width (default %d)\n"
             "-h<height>    Window height (default %d)\n"
-            "-m<modelfile> Set model filenames prefix. Using \"-mfoo\" will\n"
-            "              specify \"foo.mesh\", \"foo.frag\", \"foo.vert\".\n"
             "-v<1-3>       Set vsync mode: 1 = no vsync, 2 = vsync, 3 = adaptive\n"
-            "              Default is no vsync.\n"
+            "              Default is no vsync. Using vsync will result in FPS being\n"
+            "              approx whatever your monitor refresh rate is.\n"
             "\n",
             argv[0],
             SET_DEF_WIDTH, SET_DEF_HEIGHT
@@ -508,33 +398,53 @@
         goto exit;
     }
 
-    if (optModelPrefix.empty())
+    // Load the scene
+    if (!scene.loadInfo(optInputFilename))
+        goto exit;
+
+    if (scene.models.size() == 0)
     {
-        printf("Model file prefix empty.\n");
+        printf("ERROR: Scenefile '%s' contains no models.\n",
+            optInputFilename.c_str());
         goto exit;
     }
 
-    if (!dmLoadMesh(optModelPrefix + ".mesh", optModelPrefix + ".info", modelMesh))
-        goto exit;
+    printf("INFO: Loading %ld model(s) ..\n",
+        scene.models.size());
 
-    if (optUseShaders)
+    basePath = dmGetPath(optInputFilename);
+    printf("INFO: Model base path '%s'\n", basePath.c_str());
+
+    for (DMModel &model : scene.models)
     {
-        // Read shader files
-        if (!dmReadText(optModelPrefix + ".frag", modelFragStr, SET_MAX_SHADER_SIZE) ||
-            !dmReadText(optModelPrefix + ".vert", modelVertStr, SET_MAX_SHADER_SIZE))
+        if (!model.loadFromPLY(basePath + model.modelFile))
             goto exit;
+
+        if (optUseShaders)
+        {
+            std::string
+                fragFile = model.fragShaderFile.empty() ? "shader.frag" : basePath + model.fragShaderFile,
+                vertFile = model.vertShaderFile.empty() ? "shader.vert" : basePath + model.vertShaderFile;
+
+            if (!dmReadText(fragFile, model.fragShaderStr, SET_MAX_SHADER_SIZE) ||
+                !dmReadText(vertFile, model.vertShaderStr, SET_MAX_SHADER_SIZE))
+                goto exit;
+        }
     }
 
     // Initialize SDL + OpenGL
-    if (!dmInitSDLGL(optWidth, optHeight, "GLXDragon2"))
+    if (!dmInitSDLGL(optWidth, optHeight, "GLDragon"))
         goto exit;
 
     // According to our mode ..
     if (optUseShaders)
     {
-        modelMesh.id_fs = dmCompileShader(GL_FRAGMENT_SHADER, modelFragStr);
-        modelMesh.id_vs = dmCompileShader(GL_VERTEX_SHADER, modelVertStr);
-        dmLinkMeshShaders(modelMesh);
+        for (DMModel &model : scene.models)
+        {
+            model.id_fs = dmCompileShader(GL_FRAGMENT_SHADER, model.fragShaderStr);
+            model.id_vs = dmCompileShader(GL_VERTEX_SHADER, model.vertShaderStr);
+            dmLinkModelShaders(model);
+        }
     }
     else
     {
@@ -591,8 +501,7 @@
         }
 
         // Render the next frame
-        dmDrawBackground();
-        dmDrawModelVA(modelMesh);
+        dmDrawScene(scene);
 
         // Draw the current frame
         SDL_GL_SwapWindow(dmWindow);