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);
+}