OpenGL BSP renderer acting really weird

Started by
2 comments, last by L. Spiro 7 years, 4 months ago
I've recently gotten into OpenGL game development, and decided to make my game BSP map based. I've been following this guys Q3 map specs (quake 3)
Its been tough, but I finally got it to render. My problem persists in that its drawing some weird "pyramid" type amodel. Which is definitely not what I'm looking for. I want it to be drawing the map.
It should be a room with just a ramp. Nothing even remotely close to that.
Here is my header file for the BSP renderer

    #pragma once
    
    // Include standard headers
    #include <stdio.h>
    #include <stdlib.h>
    // Include GLEW
    #include <GL/glew.h>
    
    // Include GLM
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    #include <vector>
    
    #define FACE_POLYGON 1
    
    enum LUMPS
    {
     LUMP_ENTITIES = 0, // Game-related object descriptions
     LUMP_TEXTURE, // Surface descriptions.
     LUMP_PLANES,   // Planes used by map geometry.
     LUMP_NODES, // BSP tree nodes.
     LUMP_LEAFS, // BSP tree leaves.
     LUMP_LEAFFACES, // Lists of face indices, one list per leaf.
     LUMP_LEAFBRUSHES, // Lists of brush indices, one list per leaf.
     LUMP_MODELS, // Descriptions of rigid world geometry in map.
     LUMP_BRUSHES, // Convex polyhedra used to describe solid space.
     LUMP_BRUSHSIDES, // Brush surfaces.
     LUMP_VERTEXES, // Vertices used to describe faces.
     LUMP_INDICES, // Lists of offsets, one list per mesh.
     LUMP_EFFECTS, // List of special map effects.
     LUMP_FACES, // Surface geometry.
     LUMP_LIGHTMAPS, // Packed lightmap data.
     LUMP_LIGHTVOLS, // Local illumination data.
     LUMP_VISDATA, // Cluster-cluster visibility data.
     LUMP_ALL_DATA // A constant to store the number of lumps
    };
    
    struct Lump
    {
     int offset;
     int length;
    };
    
    struct Header
    {
     char magic[4];  // Magic number.Always "IBSP".
     int version; // Version number. 0x2e for the BSP files distributed with Quake 3.
    };
    
    struct Faces
    {
     int textureID; // The index into the texture array 
     int effect; // The index for the effects (or -1 = n/a) 
     int type; // 1=polygon, 2=patch, 3=mesh, 4=billboard 
     int startVertIndex; // The starting index into this face's first vertex 
     int numOfVerts; // The number of vertices for this face 
     int startIndex; // The starting index into the indices array for this face
     int numOfIndices; // The number of indices for this face
     int lightmapID; // The texture index for the lightmap 
     int lMapCorner[2]; // The face's lightmap corner in the image 
     int lMapSize[2]; // The size of the lightmap section 
     glm::vec3 lMapPos; // The 3D origin of lightmap. 
     glm::vec3 lMapVecs[2]; // The 3D space for s and t unit vectors. 
     glm::vec3 vNormal; // The face normal. 
     int size[2]; // The bezier patch dimensions. 
    };
    
    struct Vertexes
    {
     glm::vec3 position; // vertex position
     glm::vec2 texCoord; // vertex texture coords
     glm::vec2 lightMapCoord; // vertex lightmap coords
     glm::vec3 normal; // vertex normal
     char color[4]; // vertex color
    };
    
    struct Meshverts
    {
     int offset; // Vertex index offset, relative to first vertex of corresponding face.
    };
    
    struct Textures
    {
     char name[64]; //  Texture name.
     int flags; // Surface flags.
     int contents; // Content flags.
    };
    
    class BSPLoader
    {
    public:
     void LoadBSP(const char* bspFile);
     void DrawBSP();
     void DrawFaces(int index);
    
     GLuint numOfFaces;
     GLuint numOfVertexes;
     GLuint numOfIndices;
     GLuint numOfTextures;
    
     Faces* pointerFaces;
     Vertexes* pointerVertexes;
     Meshverts* pointerIndices;
     Textures* pointerTextures;
     GLuint indiceVbo;
     Faces* pFace;
    
     unsigned int maxTextures[1000];
    
    private:
    };
Here is my C++ file for that header

    // Include standard headers
    #include <stdio.h>
    #include <stdlib.h>
    #include <iostream>
    // Include GLEWa
    #include <GL/glew.h>
    
    // Include GLM
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    #include <maps/bspLoader.hpp>
    #include <common/texture.hpp>
    
    void BSPLoader::LoadBSP(const char* bspFile)
    {
     FILE* file = std::fopen(bspFile, "rb");
    
     std::printf("Successfully opened %s\n", bspFile);
    
     Header head = { 0 };
     Lump lump[LUMPS::LUMP_ALL_DATA] = { 0 };
    
     // read through header and lump data
     std::fread(&head, 1, sizeof(Header), file);
     std::fread(lump, LUMPS::LUMP_ALL_DATA, sizeof(Lump), file);
    
     numOfFaces = lump[LUMPS::LUMP_FACES].length / sizeof(Faces); // as specified by the Q3 map specs, divide the length of the lump itself by the sizeof the struct
     pointerFaces = new Faces[numOfFaces]; // allocate memory for the faces pointer
    
     numOfVertexes = lump[LUMPS::LUMP_VERTEXES].length / sizeof(Vertexes);
     pointerVertexes = new Vertexes[numOfVertexes];
    
     numOfIndices = lump[LUMPS::LUMP_INDICES].length / sizeof(Meshverts);
     pointerIndices = new Meshverts[numOfIndices];
    
     std::fseek(file, lump[LUMPS::LUMP_VERTEXES].offset, SEEK_SET);
    
     for (int i = 0; i < numOfVertexes; i++)
     {
     printf("Vertexes = %i\n", i);
    
     std::fread(&pointerVertexes[0], sizeof(pointerVertexes[0]), numOfVertexes, file);
     }
    
     std::fseek(file, lump[LUMPS::LUMP_INDICES].offset, SEEK_SET);
     std::fread(&pointerIndices[0], sizeof(pointerIndices), numOfIndices, file);
    
     std::fseek(file, lump[LUMPS::LUMP_FACES].offset, SEEK_SET);
     std::fread(&pointerFaces[0], sizeof(pointerFaces), numOfFaces, file);
    
     std::fclose(file);
    }
    
    void BSPLoader::DrawFaces(int index)
    {
     Faces* pFace = &pointerFaces[index];
     glVertexPointer(3, GL_FLOAT, sizeof(Vertexes), &(pointerVertexes[pFace->startVertIndex].position));
     glEnableClientState(GL_VERTEX_ARRAY);
     glDrawElements(GL_TRIANGLES, numOfIndices, GL_UNSIGNED_INT, &(pointerIndices[pFace->startIndex]));
    }
    
    void BSPLoader::DrawBSP()
    {
     for (int i = 0; i < numOfFaces; i++) // go through faces
     {
     if (pointerFaces[i].type != FACE_POLYGON)
     {
     continue;
     }
    
     DrawFaces(i);
     }
    }
And finally, here is my main.cpp

    // Include standard headers
    #include <stdio.h>
    #include <stdlib.h>
    
    // Include GLEW
    #include <GL/glew.h>
    
    // Include GLFW
    #include <glfw/glfw3.h>
    GLFWwindow* window;
    
    // Include GLM
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    using namespace glm;
    
    #include <common/shader.hpp>
    #include <common/texture.hpp>
    #include <common/controls.hpp>
    
    #include <maps/bspLoader.hpp>
    
    BSPLoader bsp;
    
    int main(void)
    {
     // Initialise GLFW
     if (!glfwInit())
     {
     fprintf(stderr, "Failed to initialize GLFW\n");
     getchar();
     return -1;
     }
    
     glfwWindowHint(GLFW_SAMPLES, 4);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
     glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed
     glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    
     // Open a window and create its OpenGL context
     window = glfwCreateWindow(1024, 768, "Vire", NULL, NULL);
     if (window == NULL) {
     fprintf(stderr, "Failed to open GLFW window. If you have an Intel GPU, they are not 3.3 compatible. Try the 2.1 version of the tutorials.\n");
     getchar();
     glfwTerminate();
     return -1;
     }
     glfwMakeContextCurrent(window);
    
     // Initialize GLEW
     glewExperimental = true; // Needed for core profile
     if (glewInit() != GLEW_OK) {
     fprintf(stderr, "Failed to initialize GLEW\n");
     getchar();
     glfwTerminate();
     return -1;
     }
    
     // Ensure we can capture the escape key being pressed below
     glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE);
     // Hide the mouse and enable unlimited mouvement
     glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
    
     // Set the mouse at the center of the screen
     glfwPollEvents();
     glfwSetCursorPos(window, 1024 / 2, 768 / 2);
    
     // Dark blue background
     glClearColor(0.0f, 0.0f, 0.4f, 0.0f);
    
     // Enable depth test
     glEnable(GL_DEPTH_TEST);
     // Accept fragment if it closer to the camera than the former one
     glDepthFunc(GL_LESS);
    
     // Cull triangles which normal is not towards the camera
     glEnable(GL_CULL_FACE);
    
     GLuint VertexArrayID;
     glGenVertexArrays(1, &VertexArrayID);
     glBindVertexArray(VertexArrayID);
    
     // Create and compile our GLSL program from the shaders
     GLuint programID = LoadShaders("TransformVertexShader.vertexshader", "TextureFragmentShader.fragmentshader");
    
     // Get a handle for our "MVP" uniform
     GLuint MatrixID = glGetUniformLocation(programID, "MVP");
    
     // Load the texture
     GLuint Texture = loadDDS("uvtemplate.DDS");
    
     // Get a handle for our "myTextureSampler" uniform
     GLuint TextureID = glGetUniformLocation(programID, "myTextureSampler");
    
     // Our vertices. Tree consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.
     // A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices
     static const GLfloat g_vertex_buffer_data[] = {
     -1.0f,-1.0f,-1.0f,
     -1.0f,-1.0f, 1.0f,
     -1.0f, 1.0f, 1.0f,
     1.0f, 1.0f,-1.0f,
     -1.0f,-1.0f,-1.0f,
     -1.0f, 1.0f,-1.0f,
     1.0f,-1.0f, 1.0f,
     -1.0f,-1.0f,-1.0f,
     1.0f,-1.0f,-1.0f,
     1.0f, 1.0f,-1.0f,
     1.0f,-1.0f,-1.0f,
     -1.0f,-1.0f,-1.0f,
     -1.0f,-1.0f,-1.0f,
     -1.0f, 1.0f, 1.0f,
     -1.0f, 1.0f,-1.0f,
     1.0f,-1.0f, 1.0f,
     -1.0f,-1.0f, 1.0f,
     -1.0f,-1.0f,-1.0f,
     -1.0f, 1.0f, 1.0f,
     -1.0f,-1.0f, 1.0f,
     1.0f,-1.0f, 1.0f,
     1.0f, 1.0f, 1.0f,
     1.0f,-1.0f,-1.0f,
     1.0f, 1.0f,-1.0f,
     1.0f,-1.0f,-1.0f,
     1.0f, 1.0f, 1.0f,
     1.0f,-1.0f, 1.0f,
     1.0f, 1.0f, 1.0f,
     1.0f, 1.0f,-1.0f,
     -1.0f, 1.0f,-1.0f,
     1.0f, 1.0f, 1.0f,
     -1.0f, 1.0f,-1.0f,
     -1.0f, 1.0f, 1.0f,
     1.0f, 1.0f, 1.0f,
     -1.0f, 1.0f, 1.0f,
     1.0f,-1.0f, 1.0f
     };
    
     // Two UV coordinatesfor each vertex. They were created with Blender.
     static const GLfloat g_uv_buffer_data[] = {
     0.000059f, 0.000004f,
     0.000103f, 0.336048f,
     0.335973f, 0.335903f,
     1.000023f, 0.000013f,
     0.667979f, 0.335851f,
     0.999958f, 0.336064f,
     0.667979f, 0.335851f,
     0.336024f, 0.671877f,
     0.667969f, 0.671889f,
     1.000023f, 0.000013f,
     0.668104f, 0.000013f,
     0.667979f, 0.335851f,
     0.000059f, 0.000004f,
     0.335973f, 0.335903f,
     0.336098f, 0.000071f,
     0.667979f, 0.335851f,
     0.335973f, 0.335903f,
     0.336024f, 0.671877f,
     1.000004f, 0.671847f,
     0.999958f, 0.336064f,
     0.667979f, 0.335851f,
     0.668104f, 0.000013f,
     0.335973f, 0.335903f,
     0.667979f, 0.335851f,
     0.335973f, 0.335903f,
     0.668104f, 0.000013f,
     0.336098f, 0.000071f,
     0.000103f, 0.336048f,
     0.000004f, 0.671870f,
     0.336024f, 0.671877f,
     0.000103f, 0.336048f,
     0.336024f, 0.671877f,
     0.335973f, 0.335903f,
     0.667969f, 0.671889f,
     1.000004f, 0.671847f,
     0.667979f, 0.335851f
     };
    
     GLuint uvbuffer;
     glGenBuffers(1, &uvbuffer);
     glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
     glBufferData(GL_ARRAY_BUFFER, sizeof(g_uv_buffer_data), g_uv_buffer_data, GL_STATIC_DRAW);
    
     bsp.LoadBSP("maps/Level.bsp");
     bsp.DrawBSP();
    
     do
     {
     // Clear the screen
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
     // Use our shader
     glUseProgram(programID);
    
     // Compute the MVP matrix from keyboard and mouse input
     computeMatricesFromInputs();
     glm::mat4 ProjectionMatrix = getProjectionMatrix();
     glm::mat4 ViewMatrix = getViewMatrix();
     glm::mat4 ModelMatrix = glm::mat4(1.0);
     glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
    
     // Send our transformation to the currently bound shader, 
     // in the "MVP" uniform
     glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);
    
     // Bind our texture in Texture Unit 0
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, Texture);
     // Set our "myTextureSampler" sampler to user Texture Unit 0
     glUniform1i(TextureID, 0);
    
     // 1rst attribute buffer : vertices
     glEnableVertexAttribArray(0);
     glBindBuffer(GL_VERTEX_ARRAY, bsp.numOfFaces);
     glVertexAttribPointer(
     0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
     3,                  // size
     GL_FLOAT,           // type
     GL_FALSE,           // normalized?
     0,                  // stride
     (void*)0            // array buffer offset
     );
    
     // 2nd attribute buffer : UVs
     glEnableVertexAttribArray(1);
     glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
     glVertexAttribPointer(
     1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
     2,                                // size : U+V => 2
     GL_FLOAT,                         // type
     GL_FALSE,                         // normalized?
     0,                                // stride
     (void*)0                          // array buffer offset
     );
    
     bsp.DrawBSP();
    
     glDisableVertexAttribArray(0);
     glDisableVertexAttribArray(1);
    
     // Swap buffers
     glfwSwapBuffers(window);
     glfwPollEvents();
    
     } // Check if the ESC key was pressed or the window was closed
     while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS &&
     glfwWindowShouldClose(window) == 0);
    
     // Cleanup VBO and shader
     glDeleteBuffers(1, &uvbuffer);
     glDeleteProgram(programID);
     glDeleteTextures(1, &TextureID);
     glDeleteVertexArrays(1, &VertexArrayID);
    
     // Close OpenGL window and terminate GLFW
     glfwTerminate();
    
     return 0;
    }
Originally, the problem persisted within the file reading. Its fixed (I think), but its still not rendering the map correctly. Also, I tried GL_QUADS, GL_TRIANGLES, GL_TRIANGLE_STRIP, and GL_LINES in the glDrawElements function in the bsp header, all of them EXCEPT GL_QUADS generated the same looking result. GL_QUADS generated nothing. (I expected this as Q3 map specs stated it must be a triangle)
Thanks! All helps appreciated

Advertisement

Are you aware that the Quake 3 engine is open source? I suggest cross-checking your loading and drawing code with that in the engine to see if you can identify any area where you may be going wrong.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

you are not using face indices for drawing.

It sounds as though all of your triangles have a [0,0,0] vertex. This gives each triangle 1 or 2 correct vertices spread over the map, and a 3rd at [0,0,0], which would create a strange pyramid shape if the map is centered below the world center.

You should use a debugger to see your final triangle mesh and/or print it, and use RenderDoc to see what vertices are being passed to OpenGL.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement