commit 958f1257971888e9786f27a75f1c9a439a982149
parent 5b67115268acf849b3e5c7ebb08895a4e38e05d1
Author: Christian Ermann <christianermann@gmail.com>
Date:   Fri, 13 Aug 2021 12:38:50 -0500
Use indexed drawing for chunk meshes
Diffstat:
9 files changed, 273 insertions(+), 128 deletions(-)
diff --git a/include/chunk.h b/include/chunk.h
@@ -1,27 +1,26 @@
 #ifndef CHUNK_H
 #define CHUNK_H
 
+#include "mesh.h"
 #include "sdf.h"
 
-#include <glad/glad.h>
-
 #define CHUNK_WIDTH 16
-#define CHUNK_VOLUME CHUNK_WIDTH * CHUNK_WIDTH * CHUNK_WIDTH
+#define CHUNK_VOLUME (CHUNK_WIDTH * CHUNK_WIDTH * CHUNK_WIDTH)
+
+#define CHUNK_PWIDTH (CHUNK_WIDTH + 1)
+#define CHUNK_PVOLUME (CHUNK_PWIDTH * CHUNK_PWIDTH * CHUNK_PWIDTH)
 
 typedef struct {
-    IVec3 id;
-    unsigned int vertex_count;
-    Vec3 vertices[CHUNK_VOLUME * 15];
-    GLuint vbo;
-    GLuint vao;
+    IVec3 origin;
+    Mesh mesh;
 } Chunk;
 
-void Chunk_init(Chunk *chunk, IVec3 id);
+void Chunk_init(Chunk *c, IVec3 origin);
 
-void Chunk_origin(const Chunk *chunk, Vec3 origin);
+void Chunk_free(Chunk *c);
 
-void Chunk_meshify(Chunk *chunk, SDF f, float isolevel);
+void Chunk_updateMesh(Chunk *c, SDF f, float isolevel);
 
-void Chunk_draw(const Chunk *chunk);
+void Chunk_drawMesh(const Chunk *c);
 
 #endif
diff --git a/include/chunk_manager.h b/include/chunk_manager.h
@@ -13,11 +13,14 @@ typedef struct {
     unsigned int chunk_count;
     IVec3 *offsets;
     Chunk *chunks;
+
 } ChunkManager;
 
 ChunkManager ChunkManager_create(const Vec3 target, int radius, SDF f,
         float isolevel);
 
+void ChunkManager_free(ChunkManager *cm);
+
 void ChunkManager_recenter(ChunkManager *cm, const Vec3 target);
 
 void ChunkManager_drawChunks(const ChunkManager *cm);
diff --git a/include/marching_cubes.h b/include/marching_cubes.h
@@ -4,7 +4,12 @@
 #include "vec.h"
 #include "sdf.h"
 
-int MC_polygonize(const Vec3 corners[8], SDF f, float isolevel,
-        Vec3 triangles[15]);
+int MC_index(const Vec3 corners[8], SDF f, float isolevel);
+
+int MC_vertices(const Vec3 corners[8], SDF f, float isolevel, int mc_index,
+        Vec3 vertices[3]);
+
+int MC_indices(int mc_index, unsigned int vertex_offset,
+        const unsigned int edge_offsets[12], unsigned int indices[15]);
 
 #endif
diff --git a/include/mesh.h b/include/mesh.h
@@ -0,0 +1,32 @@
+#ifndef MESH_H
+#define MESH_H
+
+#include "vec.h"
+
+typedef struct {
+    unsigned int vertex_capacity;
+    unsigned int vertex_count;
+    Vec3 *vertices;
+
+    unsigned int index_capacity;
+    unsigned int index_count;
+    unsigned int *indices;
+
+    unsigned int vao;
+    unsigned int vbo;
+    unsigned int ebo;
+} Mesh;
+
+void Mesh_init(Mesh *m);
+
+void Mesh_free(Mesh *m);
+
+void Mesh_reserveVertices(Mesh *m, unsigned int vertex_count);
+void Mesh_reserveIndices(Mesh *m, unsigned int index_count);
+
+void Mesh_updateVertexBuffer(const Mesh *m);
+void Mesh_updateIndexBuffer(const Mesh *m);
+
+void Mesh_draw(const Mesh *m);
+
+#endif
diff --git a/src/chunk.c b/src/chunk.c
@@ -1,32 +1,33 @@
 #include "chunk.h"
-#include "marching_cubes.h"
 
-void Chunk_init(Chunk *chunk, IVec3 id)
-{
-    chunk->id[0] = id[0];
-    chunk->id[1] = id[1];
-    chunk->id[2] = id[2];
+#include "marching_cubes.h"
 
-    chunk->vertex_count = 0;
+#include <stdlib.h>
 
-    glGenVertexArrays(1, &chunk->vao);
-    glBindVertexArray(chunk->vao);
+void Chunk_init(Chunk *c, IVec3 origin)
+{
+    c->origin[0] = origin[0];
+    c->origin[1] = origin[1];
+    c->origin[2] = origin[2];
 
-    glGenBuffers(1, &chunk->vbo);
-    glBindBuffer(GL_ARRAY_BUFFER, chunk->vbo);
+    Mesh_init(&c->mesh);
+    Mesh_reserveVertices(&c->mesh, CHUNK_PVOLUME * 3);
+    Mesh_reserveIndices(&c->mesh, CHUNK_VOLUME * 15);
+}
 
-    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), (GLvoid*)0);
-    glEnableVertexAttribArray(0);
+void Chunk_free(Chunk *c)
+{
+    Mesh_free(&c->mesh);
 }
 
-void Chunk_origin(const Chunk *chunk, Vec3 origin)
+static void worldOrigin(const IVec3 chunk_origin, Vec3 world_origin)
 {
-    origin[0] = chunk->id[0] * CHUNK_WIDTH;
-    origin[1] = chunk->id[1] * CHUNK_WIDTH;
-    origin[2] = chunk->id[2] * CHUNK_WIDTH;
+    world_origin[0] = chunk_origin[0] * CHUNK_WIDTH;
+    world_origin[1] = chunk_origin[1] * CHUNK_WIDTH;
+    world_origin[2] = chunk_origin[2] * CHUNK_WIDTH;
 }
 
-static void Cube_corners(const Vec3 origin, float width, Vec3 corners[8])
+static void cubeCorners(const Vec3 origin, float width, Vec3 corners[8])
 {
     corners[0][0] = origin[0];
     corners[0][1] = origin[1];
@@ -61,39 +62,72 @@ static void Cube_corners(const Vec3 origin, float width, Vec3 corners[8])
     corners[7][2] = origin[2] + width;
 }
 
-void Chunk_meshify(Chunk *chunk, SDF f, float isolevel)
+const static unsigned int EDGE_OFFSETS[] = {
+    0,
+    4,
+    3 * CHUNK_PWIDTH,
+    1,
+    3 * CHUNK_PWIDTH * CHUNK_PWIDTH,
+    3 * CHUNK_PWIDTH * CHUNK_PWIDTH + 4,
+    3 * CHUNK_PWIDTH * CHUNK_PWIDTH + 3 * CHUNK_PWIDTH,
+    3 * CHUNK_PWIDTH * CHUNK_PWIDTH + 1,
+    2,
+    5,
+    3 * CHUNK_PWIDTH + 5,
+    3 * CHUNK_PWIDTH + 2
+};
+
+static void Chunk_updateMeshData(Chunk *c, SDF f, float isolevel)
 {
-    chunk->vertex_count = 0;
-    for (int i = 0; i < CHUNK_WIDTH; i++)
+    c->mesh.vertex_count = 0;
+    c->mesh.index_count = 0;
+    for (int i = 0; i < CHUNK_PWIDTH; i++)
     {
-        for (int j = 0; j < CHUNK_WIDTH; j++)
+        for (int j = 0; j < CHUNK_PWIDTH; j++)
         {
-            for (int k = 0; k < CHUNK_WIDTH; k++)
+            for (int k = 0; k < CHUNK_PWIDTH; k++)
             {
-                Vec3 chunk_origin;
-                Chunk_origin(chunk, chunk_origin);
+                Vec3 mesh_origin;
+                worldOrigin(c->origin, mesh_origin);
 
                 Vec3 cell_origin = {
-                    chunk_origin[0] + i,
-                    chunk_origin[1] + j,
-                    chunk_origin[2] + k
+                    mesh_origin[0] + k,
+                    mesh_origin[1] + i,
+                    mesh_origin[2] + j
                 };
                 
                 Vec3 cell_corners[8];
-                Cube_corners(cell_origin, 1, cell_corners);
+                cubeCorners(cell_origin, 1, cell_corners);
+
+                int mc_index = MC_index(cell_corners, f, isolevel);
 
-                chunk->vertex_count += MC_polygonize(cell_corners, f, isolevel,
-                        &chunk->vertices[chunk->vertex_count]);
+                if (i < CHUNK_WIDTH && j < CHUNK_WIDTH && k < CHUNK_WIDTH)
+                {
+                    c->mesh.index_count += MC_indices(mc_index,
+                            c->mesh.vertex_count, EDGE_OFFSETS,
+                            &c->mesh.indices[c->mesh.index_count]);
+                }
+
+                c->mesh.vertex_count += MC_vertices(cell_corners, f, isolevel,
+                        mc_index, &c->mesh.vertices[c->mesh.vertex_count]);
             }
         }
     }
-    glBindBuffer(GL_ARRAY_BUFFER, chunk->vbo);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(Vec3) * chunk->vertex_count,
-            chunk->vertices, GL_STATIC_DRAW);
 }
 
-void Chunk_draw(const Chunk *chunk)
+static void Chunk_updateMeshBuffers(const Chunk *c)
+{
+    Mesh_updateVertexBuffer(&c->mesh);
+    Mesh_updateIndexBuffer(&c->mesh);
+}
+
+void Chunk_updateMesh(Chunk *c, SDF f, float isolevel)
+{
+    Chunk_updateMeshData(c, f, isolevel);
+    Chunk_updateMeshBuffers(c);
+}
+
+void Chunk_drawMesh(const Chunk *c)
 {
-    glBindVertexArray(chunk->vao);
-    glDrawArrays(GL_TRIANGLES, 0, chunk->vertex_count);
+    Mesh_draw(&c->mesh);
 }
diff --git a/src/chunk_manager.c b/src/chunk_manager.c
@@ -41,7 +41,7 @@ static void ChunkManager_createChunks(ChunkManager *cm)
     for (int i = 0; i < cm->chunk_count; i++)
     {
         Chunk_init(&cm->chunks[i], cm->offsets[i]);
-        Chunk_meshify(&cm->chunks[i], cm->f, cm->isolevel);
+        Chunk_updateMesh(&cm->chunks[i], cm->f, cm->isolevel);
     }
 }
 
@@ -58,15 +58,15 @@ static void ChunkManager_updateChunks(ChunkManager *cm)
     }
 
     // Determine which offsets are new and which chunks are old
-    IVec3 *new_ids = malloc(sizeof *new_ids * cm->chunk_count);
+    IVec3 *new_origins = malloc(sizeof *new_origins * cm->chunk_count);
     for (int i = 0; i < cm->chunk_count; i++)
     {
-        new_ids[i][0] = cm->origin[0] + cm->offsets[i][0];
-        new_ids[i][1] = cm->origin[1] + cm->offsets[i][1];
-        new_ids[i][2] = cm->origin[2] + cm->offsets[i][2];
+        new_origins[i][0] = cm->origin[0] + cm->offsets[i][0];
+        new_origins[i][1] = cm->origin[1] + cm->offsets[i][1];
+        new_origins[i][2] = cm->origin[2] + cm->offsets[i][2];
         for (int j = 0; j < cm->chunk_count; j++)
         {
-            if (IVec3_equal(new_ids[i], cm->chunks[j].id) != 0)
+            if (IVec3_equal(new_origins[i], cm->chunks[j].origin) != 0)
             {
                 is_new[i] = false;
                 is_old[j] = false;
@@ -75,7 +75,7 @@ static void ChunkManager_updateChunks(ChunkManager *cm)
         }
     }
 
-    // Set old chunks to have new ids
+    // Set old chunks to have new origins
     for (int i = 0; i < cm->chunk_count; i++)
     {
         if (is_new[i])
@@ -84,10 +84,10 @@ static void ChunkManager_updateChunks(ChunkManager *cm)
             {
                 if (is_old[j])
                 {
-                    cm->chunks[j].id[0] = new_ids[i][0];
-                    cm->chunks[j].id[1] = new_ids[i][1];
-                    cm->chunks[j].id[2] = new_ids[i][2];
-                    Chunk_meshify(&cm->chunks[j], cm->f, cm->isolevel);
+                    cm->chunks[j].origin[0] = new_origins[i][0];
+                    cm->chunks[j].origin[1] = new_origins[i][1];
+                    cm->chunks[j].origin[2] = new_origins[i][2];
+                    Chunk_updateMesh(&cm->chunks[j], cm->f, cm->isolevel);
                     is_new[i] = false;
                     is_old[j] = false;
                     break;
@@ -95,6 +95,10 @@ static void ChunkManager_updateChunks(ChunkManager *cm)
             }
         }
     }
+
+    free(new_origins);
+    free(is_old);
+    free(is_new);
 }
 
 static void ChunkManager_worldToChunk(const Vec3 src, IVec3 dst)
@@ -122,6 +126,19 @@ ChunkManager ChunkManager_create(const Vec3 target, int radius, SDF f,
     return cm;
 }
 
+void ChunkManager_free(ChunkManager *cm)
+{
+    free(cm->offsets);
+
+    for (int i = 0; i < cm->chunk_count; i++)
+    {
+        Chunk_free(&cm->chunks[i]);
+    }
+    free(cm->chunks);
+
+    cm->chunk_count = 0;
+}
+
 void ChunkManager_recenter(ChunkManager *cm, const Vec3 target)
 {
     IVec3 new_origin;
@@ -139,6 +156,6 @@ void ChunkManager_drawChunks(const ChunkManager *cm)
 {
     for (int i = 0; i < cm->chunk_count; i++)
     {
-        Chunk_draw(&cm->chunks[i]);
+        Chunk_drawMesh(&cm->chunks[i]);
     }
 }
diff --git a/src/main.c b/src/main.c
@@ -48,7 +48,6 @@ int main(int argc, char** argv)
         exit(EXIT_FAILURE);
     }
 
-
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK,
             SDL_GL_CONTEXT_PROFILE_CORE);
     SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
@@ -119,6 +118,8 @@ int main(int argc, char** argv)
         SDL_GL_SwapWindow(window);
     }
 
+    ChunkManager_free(&chunk_manager);
+
     SDL_DestroyWindow(window);
     SDL_Quit();
 }
diff --git a/src/marching_cubes.c b/src/marching_cubes.c
@@ -2,7 +2,7 @@
 
 #include <math.h>
 
-const int EDGE_TABLE[256] = {
+const static int EDGE_TABLE[256] = {
     0x0  , 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905,
     0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00, 0x190, 0x99 , 0x393, 0x29a,
     0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93,
@@ -31,7 +31,7 @@ const int EDGE_TABLE[256] = {
     0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0
 };
 
-const int TRI_TABLE[256][16] = {
+const static int TRI_TABLE[256][16] = {
     {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
 	{ 0,  8,  3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
 	{ 0,  1,  9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
@@ -304,7 +304,7 @@ int MC_index(const Vec3 corners[8], SDF f, float isolevel)
     return mc_index;
 }
 
-void MC_interpVertex(const Vec3 a, const Vec3 b, SDF f, float isolevel,
+static void MC_interpVertex(const Vec3 a, const Vec3 b, SDF f, float isolevel,
         Vec3 dst)
 {
     float fa = f(a);
@@ -330,81 +330,33 @@ void MC_interpVertex(const Vec3 a, const Vec3 b, SDF f, float isolevel,
     return;
 }
 
-void MC_vertices(const Vec3 corners[8], SDF f, float isolevel, int mc_index,
-        Vec3 vertices[12])
+int MC_vertices(const Vec3 corners[8], SDF f, float isolevel, int mc_index,
+        Vec3 vertices[3])
 {
     if (EDGE_TABLE[mc_index] & 1)
     {
         MC_interpVertex(corners[0], corners[1], f, isolevel, vertices[0]);
     }
-    if (EDGE_TABLE[mc_index] & 2)
-    {
-        MC_interpVertex(corners[1], corners[2], f, isolevel, vertices[1]);
-    }
-    if (EDGE_TABLE[mc_index] & 4)
-    {
-        MC_interpVertex(corners[2], corners[3], f, isolevel, vertices[2]);
-    }
     if (EDGE_TABLE[mc_index] & 8)
     {
-        MC_interpVertex(corners[3], corners[0], f, isolevel, vertices[3]);
-    }
-    if (EDGE_TABLE[mc_index] & 16)
-    {
-        MC_interpVertex(corners[4], corners[5], f, isolevel, vertices[4]);
-    }
-    if (EDGE_TABLE[mc_index] & 32)
-    {
-        MC_interpVertex(corners[5], corners[6], f, isolevel, vertices[5]);
-    }
-    if (EDGE_TABLE[mc_index] & 64)
-    {
-        MC_interpVertex(corners[6], corners[7], f, isolevel, vertices[6]);
-    }
-    if (EDGE_TABLE[mc_index] & 128)
-    {
-        MC_interpVertex(corners[7], corners[4], f, isolevel, vertices[7]);
+        MC_interpVertex(corners[3], corners[0], f, isolevel, vertices[1]);
     }
     if (EDGE_TABLE[mc_index] & 256)
     {
-        MC_interpVertex(corners[0], corners[4], f, isolevel, vertices[8]);
-    }
-    if (EDGE_TABLE[mc_index] & 512)
-    {
-        MC_interpVertex(corners[1], corners[5], f, isolevel, vertices[9]);
-    }
-    if (EDGE_TABLE[mc_index] & 1024)
-    {
-        MC_interpVertex(corners[2], corners[6], f, isolevel, vertices[10]);
-    }
-    if (EDGE_TABLE[mc_index] & 2048)
-    {
-        MC_interpVertex(corners[3], corners[7], f, isolevel, vertices[11]);
+        MC_interpVertex(corners[0], corners[4], f, isolevel, vertices[2]);
     }
+    return 3;
 }
 
-int MC_triangles(const Vec3 vertices[12], SDF f, float isolevel, int mc_index,
-        Vec3* triangles)
+int MC_indices(int mc_index, unsigned int vertex_offset,
+        const unsigned int edge_offsets[12], unsigned int indices[15])
 {
-    int vertex_count = 0;
-    for (int i = 0; TRI_TABLE[mc_index][i] != -1; i++)
+    int index_count = 0;
+    while (TRI_TABLE[mc_index][index_count] != -1)
     {
-        triangles[i][0] = vertices[TRI_TABLE[mc_index][i]][0];
-        triangles[i][1] = vertices[TRI_TABLE[mc_index][i]][1];
-        triangles[i][2] = vertices[TRI_TABLE[mc_index][i]][2];
-        vertex_count++;
+        int edge_id = TRI_TABLE[mc_index][index_count];
+        indices[index_count] = vertex_offset + edge_offsets[edge_id];
+        index_count += 1;
     }
-    return vertex_count;
-}
-
-int MC_polygonize(const Vec3 corners[8], SDF f, float isolevel,
-        Vec3 triangles[15])
-{
-    int mc_index = MC_index(corners, f, isolevel);
-
-    Vec3 vertices[12];
-    MC_vertices(corners, f, isolevel, mc_index, vertices);
-
-    return MC_triangles(vertices, f, isolevel, mc_index,
-            triangles);
+    return index_count;
 }
diff --git a/src/mesh.c b/src/mesh.c
@@ -0,0 +1,102 @@
+#include "mesh.h"
+
+#include "glad/glad.h"
+
+#include <stdlib.h>
+
+void Mesh_init(Mesh *m)
+{
+    m->vertex_capacity = 0;
+    m->vertex_count = 0;
+    m->vertices = NULL;
+
+    m->index_capacity = 0;
+    m->index_count = 0;
+    m->indices = NULL;
+
+    glGenVertexArrays(1, &m->vao);
+    glBindVertexArray(m->vao);
+
+    glGenBuffers(1, &m->vbo);
+    glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
+
+    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), (GLvoid*)0);
+    glEnableVertexAttribArray(0);
+
+    glGenBuffers(1, &m->ebo);
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo);
+
+    glBindVertexArray(0);
+}
+
+void Mesh_free(Mesh *m)
+{
+    if (m->vertices)
+    {
+        free(m->vertices);
+        m->vertices = NULL;
+    }
+    m->vertex_capacity = 0;
+    m->vertex_count = 0;
+
+    if (m->indices)
+    {
+        free(m->indices);
+        m->indices = NULL;
+    }
+    m->index_capacity = 0;
+    m->index_count = 0;
+
+    glDeleteVertexArrays(1, &m->vao);
+    glDeleteBuffers(1, &m->vbo);
+    glDeleteBuffers(1, &m->ebo);
+}
+
+void Mesh_reserveVertices(Mesh *m, unsigned int vertex_count)
+{
+    if (vertex_count > m->vertex_capacity)
+    {
+        if (m->vertices)
+        {
+            free(m->vertices);
+            m->vertices = NULL;
+        }
+        m->vertices = malloc(sizeof *m->vertices * vertex_count);
+        m->vertex_capacity = vertex_count;
+    }
+}
+
+void Mesh_reserveIndices(Mesh *m, unsigned int index_count)
+{
+    if (index_count > m->index_capacity)
+    {
+        if (m->indices)
+        {
+            free(m->indices);
+            m->indices = NULL;
+        }
+        m->indices = malloc(sizeof *m->indices * index_count);
+        m->index_capacity = index_count;
+    }
+}
+
+void Mesh_updateVertexBuffer(const Mesh *m)
+{
+    glBindBuffer(GL_ARRAY_BUFFER, m->vbo);
+    glBufferData(GL_ARRAY_BUFFER, sizeof *m->vertices * m->vertex_count,
+            m->vertices, GL_STATIC_DRAW);
+}
+
+void Mesh_updateIndexBuffer(const Mesh *m)
+{
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m->ebo);
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof *m->indices * m->index_count,
+            m->indices, GL_STATIC_DRAW);
+}
+
+void Mesh_draw(const Mesh *m)
+{
+    glBindVertexArray(m->vao);
+    glDrawElements(GL_TRIANGLES, m->index_count, GL_UNSIGNED_INT, (GLvoid*)0);
+    glBindVertexArray(0);
+}