terrain

Real-time terrain generation using marching cubes
git clone git://git.christianermann.dev/terrain
Log | Files | Refs | README | LICENSE

commit 5b67115268acf849b3e5c7ebb08895a4e38e05d1
parent 0bd78557e526be25ade169f09af521a9da1bf9fc
Author: Christian Ermann <christianermann@gmail.com>
Date:   Thu,  5 Aug 2021 19:49:36 -0500

Load surrounding chunks

Diffstat:
Minclude/chunk.h | 23+++++++++++++++++------
Minclude/chunk_manager.h | 19+++++++++++++------
Ainclude/marching_cubes.h | 10++++++++++
Ainclude/sdf.h | 8++++++++
Ainclude/vec.h | 12++++++++++++
Msrc/chunk.c | 146+++++++++++++++++++++++++++++++++++++++++--------------------------------------
Msrc/chunk_manager.c | 166++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Msrc/main.c | 47+++++++++++++----------------------------------
Asrc/marching_cubes.c | 410+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/vec.c | 6++++++
10 files changed, 678 insertions(+), 169 deletions(-)

diff --git a/include/chunk.h b/include/chunk.h @@ -1,16 +1,27 @@ #ifndef CHUNK_H #define CHUNK_H -extern const int CHUNK_WIDTH; +#include "sdf.h" + +#include <glad/glad.h> + +#define CHUNK_WIDTH 16 +#define CHUNK_VOLUME CHUNK_WIDTH * CHUNK_WIDTH * CHUNK_WIDTH typedef struct { - int id[3]; - float origin[3]; + IVec3 id; + unsigned int vertex_count; + Vec3 vertices[CHUNK_VOLUME * 15]; + GLuint vbo; + GLuint vao; } Chunk; -Chunk Chunk_create(int x, int y, int z); +void Chunk_init(Chunk *chunk, IVec3 id); + +void Chunk_origin(const Chunk *chunk, Vec3 origin); + +void Chunk_meshify(Chunk *chunk, SDF f, float isolevel); -void Chunk_outline(const Chunk* chunk, float corners[24], unsigned int offset, - unsigned int indices[24]); +void Chunk_draw(const Chunk *chunk); #endif diff --git a/include/chunk_manager.h b/include/chunk_manager.h @@ -4,15 +4,22 @@ #include "chunk.h" typedef struct { + IVec3 origin; int radius; - int chunk_count; - int origin[3]; - int* offsets; - Chunk* chunks; + + SDF f; + float isolevel; + + unsigned int chunk_count; + IVec3 *offsets; + Chunk *chunks; } ChunkManager; -ChunkManager ChunkManager_create(int radius, float player[3]); +ChunkManager ChunkManager_create(const Vec3 target, int radius, SDF f, + float isolevel); + +void ChunkManager_recenter(ChunkManager *cm, const Vec3 target); -void ChunkManager_update(ChunkManager* chunk_manager, float player[3]); +void ChunkManager_drawChunks(const ChunkManager *cm); #endif diff --git a/include/marching_cubes.h b/include/marching_cubes.h @@ -0,0 +1,10 @@ +#ifndef MC_H +#define MC_H + +#include "vec.h" +#include "sdf.h" + +int MC_polygonize(const Vec3 corners[8], SDF f, float isolevel, + Vec3 triangles[15]); + +#endif diff --git a/include/sdf.h b/include/sdf.h @@ -0,0 +1,8 @@ +#ifndef SDF_H +#define SDF_H + +#include "vec.h" + +typedef float (*SDF)(const Vec3 p); + +#endif diff --git a/include/vec.h b/include/vec.h @@ -0,0 +1,12 @@ +#ifndef VEC_H +#define VEC_H + +#include <stdbool.h> + +typedef int IVec3[3]; + +bool IVec3_equal(const IVec3 a, const IVec3 b); + +typedef float Vec3[3]; + +#endif diff --git a/src/chunk.c b/src/chunk.c @@ -1,93 +1,99 @@ #include "chunk.h" +#include "marching_cubes.h" -const int CHUNK_WIDTH = 16; - -Chunk Chunk_create(int x, int y, int z) +void Chunk_init(Chunk *chunk, IVec3 id) { - Chunk chunk; + chunk->id[0] = id[0]; + chunk->id[1] = id[1]; + chunk->id[2] = id[2]; + + chunk->vertex_count = 0; - chunk.id[0] = x; - chunk.id[1] = y; - chunk.id[2] = z; + glGenVertexArrays(1, &chunk->vao); + glBindVertexArray(chunk->vao); - chunk.origin[0] = x * CHUNK_WIDTH; - chunk.origin[1] = y * CHUNK_WIDTH; - chunk.origin[2] = z * CHUNK_WIDTH; + glGenBuffers(1, &chunk->vbo); + glBindBuffer(GL_ARRAY_BUFFER, chunk->vbo); - return chunk; + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), (GLvoid*)0); + glEnableVertexAttribArray(0); +} + +void Chunk_origin(const Chunk *chunk, Vec3 origin) +{ + origin[0] = chunk->id[0] * CHUNK_WIDTH; + origin[1] = chunk->id[1] * CHUNK_WIDTH; + origin[2] = chunk->id[2] * CHUNK_WIDTH; } -void Cube_corners(const float origin[3], float width, float corners[24]) +static void Cube_corners(const Vec3 origin, float width, Vec3 corners[8]) { - corners[0] = origin[0]; - corners[1] = origin[1]; - corners[2] = origin[2]; + corners[0][0] = origin[0]; + corners[0][1] = origin[1]; + corners[0][2] = origin[2]; - corners[3] = origin[0] + width; - corners[4] = origin[1]; - corners[5] = origin[2]; + corners[1][0] = origin[0] + width; + corners[1][1] = origin[1]; + corners[1][2] = origin[2]; - corners[6] = origin[0] + width; - corners[7] = origin[1]; - corners[8] = origin[2] + width; + corners[2][0] = origin[0] + width; + corners[2][1] = origin[1]; + corners[2][2] = origin[2] + width; - corners[9] = origin[0]; - corners[10] = origin[1]; - corners[11] = origin[2] + width; + corners[3][0] = origin[0]; + corners[3][1] = origin[1]; + corners[3][2] = origin[2] + width; - corners[12] = origin[0]; - corners[13] = origin[1] + width; - corners[14] = origin[2]; + corners[4][0] = origin[0]; + corners[4][1] = origin[1] + width; + corners[4][2] = origin[2]; - corners[15] = origin[0] + width; - corners[16] = origin[1] + width; - corners[17] = origin[2]; + corners[5][0] = origin[0] + width; + corners[5][1] = origin[1] + width; + corners[5][2] = origin[2]; - corners[18] = origin[0] + width; - corners[19] = origin[1] + width; - corners[20] = origin[2] + width; + corners[6][0] = origin[0] + width; + corners[6][1] = origin[1] + width; + corners[6][2] = origin[2] + width; - corners[21] = origin[0]; - corners[22] = origin[1] + width; - corners[23] = origin[2] + width; + corners[7][0] = origin[0]; + corners[7][1] = origin[1] + width; + corners[7][2] = origin[2] + width; } -void Cube_outlineIndices(unsigned int offset, unsigned int indices[24]) +void Chunk_meshify(Chunk *chunk, SDF f, float isolevel) { - // Bottom edges - indices[0] = 0 + offset; - indices[1] = 1 + offset; - indices[2] = 1 + offset; - indices[3] = 2 + offset; - indices[4] = 2 + offset; - indices[5] = 3 + offset; - indices[6] = 3 + offset; - indices[7] = 0 + offset; - - // Top edges - indices[8] = 4 + offset; - indices[9] = 5 + offset; - indices[10] = 5 + offset; - indices[11] = 6 + offset; - indices[12] = 6 + offset; - indices[13] = 7 + offset; - indices[14] = 7 + offset; - indices[15] = 4 + offset; - - // Middle edges - indices[16] = 0 + offset; - indices[17] = 4 + offset; - indices[18] = 1 + offset; - indices[19] = 5 + offset; - indices[20] = 2 + offset; - indices[21] = 6 + offset; - indices[22] = 3 + offset; - indices[23] = 7 + offset; + chunk->vertex_count = 0; + for (int i = 0; i < CHUNK_WIDTH; i++) + { + for (int j = 0; j < CHUNK_WIDTH; j++) + { + for (int k = 0; k < CHUNK_WIDTH; k++) + { + Vec3 chunk_origin; + Chunk_origin(chunk, chunk_origin); + + Vec3 cell_origin = { + chunk_origin[0] + i, + chunk_origin[1] + j, + chunk_origin[2] + k + }; + + Vec3 cell_corners[8]; + Cube_corners(cell_origin, 1, cell_corners); + + chunk->vertex_count += MC_polygonize(cell_corners, f, isolevel, + &chunk->vertices[chunk->vertex_count]); + } + } + } + glBindBuffer(GL_ARRAY_BUFFER, chunk->vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(Vec3) * chunk->vertex_count, + chunk->vertices, GL_STATIC_DRAW); } -void Chunk_outline(const Chunk* chunk, float corners[24], - unsigned int offset, unsigned int indices[24]) +void Chunk_draw(const Chunk *chunk) { - Cube_corners(chunk->origin, CHUNK_WIDTH, corners); - Cube_outlineIndices(offset, indices); + glBindVertexArray(chunk->vao); + glDrawArrays(GL_TRIANGLES, 0, chunk->vertex_count); } diff --git a/src/chunk_manager.c b/src/chunk_manager.c @@ -1,84 +1,144 @@ - #include "chunk_manager.h" -#include <stdlib.h> #include <math.h> +#include <stdlib.h> -int ChunkManager_offsets(int radius, int* offsets) +static void ChunkManager_setChunkCount(ChunkManager *cm) { - int count = 0; - for (int x = -radius; x <= radius; x++) + cm->chunk_count = 0; + for (int x = -cm->radius; x <= cm->radius; x++) { - for (int y = -radius; y <= radius; y++) + for (int y = -cm->radius; y <= cm->radius; y++) { - for (int z = -radius; z <= radius; z++) + for (int z = -cm->radius; z <= cm->radius; z++) { - if (x * x + y * y + z * z <= radius * radius) - { - if (offsets != NULL) - { - offsets[count] = x; - offsets[count + 1] = y; - offsets[count + 2] = z; - } - count += 3; - } + cm->chunk_count += 1; } } } - return count; } -void ChunkManager_worldToChunk(const float src[3], int dst[3]) +static void ChunkManager_createOffsets(ChunkManager *cm) { - dst[0] = (int)floor(src[0] / CHUNK_WIDTH); - dst[1] = (int)floor(src[1] / CHUNK_WIDTH); - dst[2] = (int)floor(src[2] / CHUNK_WIDTH); + cm->offsets = malloc(sizeof *cm->offsets * cm->chunk_count); + for (int i = 0, x = -cm->radius; x <= cm->radius; x++) + { + for (int y = -cm->radius; y <= cm->radius; y++) + { + for (int z = -cm->radius; z <= cm->radius; z++, i++) + { + cm->offsets[i][0] = x; + cm->offsets[i][1] = y; + cm->offsets[i][2] = z; + } + } + } } -void ChunkManager_recenter(ChunkManager* chunk_manager, const int origin[3]) +static void ChunkManager_createChunks(ChunkManager *cm) { - chunk_manager->origin[0] = origin[0]; - chunk_manager->origin[1] = origin[1]; - chunk_manager->origin[2] = origin[2]; - for (int i = 0; i < chunk_manager->chunk_count; i++) + cm->chunks = malloc(sizeof *cm->chunks * cm->chunk_count); + for (int i = 0; i < cm->chunk_count; i++) { - chunk_manager->chunks[i] = Chunk_create( - origin[0] + chunk_manager->offsets[i * 3], - origin[1] + chunk_manager->offsets[i * 3 + 1], - origin[2] + chunk_manager->offsets[i * 3 + 2] - ); + Chunk_init(&cm->chunks[i], cm->offsets[i]); + Chunk_meshify(&cm->chunks[i], cm->f, cm->isolevel); } } -ChunkManager ChunkManager_create(int radius, float player[3]) +static void ChunkManager_updateChunks(ChunkManager *cm) { - ChunkManager chunk_manager; + // is_new indicates whether an offset is new + bool *is_new = malloc(sizeof *is_new * cm->chunk_count); + // is_old indicates whether a chunk is old + bool *is_old = malloc(sizeof *is_old * cm->chunk_count); + for (int i = 0; i < cm->chunk_count; i++) + { + is_new[i] = true; + is_old[i] = true; + } - chunk_manager.radius = radius; - - int offset_count = ChunkManager_offsets(radius, NULL); - chunk_manager.offsets = (int*)malloc(offset_count * sizeof(int)); - ChunkManager_offsets(radius, chunk_manager.offsets); - - chunk_manager.chunk_count = offset_count / 3; - chunk_manager.chunks = (Chunk*)malloc(chunk_manager.chunk_count - * sizeof(Chunk)); + // Determine which offsets are new and which chunks are old + IVec3 *new_ids = malloc(sizeof *new_ids * 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]; + for (int j = 0; j < cm->chunk_count; j++) + { + if (IVec3_equal(new_ids[i], cm->chunks[j].id) != 0) + { + is_new[i] = false; + is_old[j] = false; + break; + } + } + } + + // Set old chunks to have new ids + for (int i = 0; i < cm->chunk_count; i++) + { + if (is_new[i]) + { + for (int j = 0; j < cm->chunk_count; j++) + { + 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); + is_new[i] = false; + is_old[j] = false; + break; + } + } + } + } +} + +static void ChunkManager_worldToChunk(const Vec3 src, IVec3 dst) +{ + dst[0] = (int)floorf(src[0] / CHUNK_WIDTH); + dst[1] = (int)floorf(src[1] / CHUNK_WIDTH); + dst[2] = (int)floorf(src[2] / CHUNK_WIDTH); +} + +ChunkManager ChunkManager_create(const Vec3 target, int radius, SDF f, + float isolevel) +{ + ChunkManager cm; + + ChunkManager_worldToChunk(target, cm.origin); + cm.radius = radius; + + cm.f = f; + cm.isolevel = isolevel; - ChunkManager_worldToChunk(player, chunk_manager.origin); - ChunkManager_recenter(&chunk_manager, chunk_manager.origin); + ChunkManager_setChunkCount(&cm); + ChunkManager_createOffsets(&cm); + ChunkManager_createChunks(&cm); - return chunk_manager; + return cm; +} + +void ChunkManager_recenter(ChunkManager *cm, const Vec3 target) +{ + IVec3 new_origin; + ChunkManager_worldToChunk(target, new_origin); + if (!IVec3_equal(new_origin, cm->origin)) + { + cm->origin[0] = new_origin[0]; + cm->origin[1] = new_origin[1]; + cm->origin[2] = new_origin[2]; + ChunkManager_updateChunks(cm); + } } -void ChunkManager_update(ChunkManager* chunk_manager, float player[3]) +void ChunkManager_drawChunks(const ChunkManager *cm) { - int new_origin[3]; - ChunkManager_worldToChunk(player, new_origin); - if (new_origin[0] != chunk_manager->origin[0] - || new_origin[1] != chunk_manager->origin[1] - || new_origin[2] != chunk_manager->origin[2]) + for (int i = 0; i < cm->chunk_count; i++) { - ChunkManager_recenter(chunk_manager, new_origin); + Chunk_draw(&cm->chunks[i]); } } diff --git a/src/main.c b/src/main.c @@ -1,16 +1,12 @@ #include "camera.h" -#include "chunk.h" #include "chunk_manager.h" -#include "glh/buffer.h" #include "glh/shader.h" #include "glad/glad.h" #include <cglm/cglm.h> - #include <SDL2/SDL.h> #include <SDL2/SDL_opengl.h> -#include <stdbool.h> #include <stdio.h> const int WIDTH = 512, HEIGHT = 512; @@ -31,6 +27,11 @@ void Camera_update(Camera* camera) Camera_updateMatrix(camera); } +float sphereSDF(const float p[3]) +{ + return sqrtf(p[0] * p[0] + p[1] * p[1] + p[2] * p[2]) - 25.0f; +} + int main(int argc, char** argv) { if (SDL_Init(SDL_INIT_VIDEO) < 0) @@ -47,6 +48,7 @@ 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); @@ -62,7 +64,7 @@ int main(int argc, char** argv) SDL_ShowCursor(SDL_DISABLE); SDL_SetRelativeMouseMode(SDL_TRUE); - Camera camera = Camera_create(0, 0, 3, -90, 0); + Camera camera = Camera_create(0, 0, -3, 0, 0); gladLoadGLLoader(SDL_GL_GetProcAddress); @@ -72,23 +74,11 @@ int main(int argc, char** argv) glClearColor(0.4f, 0.4f, 0.6f, 1.0f); - ChunkManager chunk_manager = ChunkManager_create(2, camera.position); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - float vertices[24 * chunk_manager.chunk_count]; - unsigned int indices[24 * chunk_manager.chunk_count]; - for (int i = 0; i < chunk_manager.chunk_count; i++) - { - Chunk_outline(&chunk_manager.chunks[i], &vertices[i * 24], i * 8, - &indices[i * 24]); - } - GLuint vertex_buffer = createVertexBuffer(24 * chunk_manager.chunk_count - * sizeof(float), vertices, GL_DYNAMIC_DRAW); - GLuint index_buffer = createIndexBuffer(24 * chunk_manager.chunk_count - * sizeof(unsigned int), indices, GL_STATIC_DRAW); - - int vertex[] = { 3 }; - GLuint vertex_array = createVertexArray(vertex_buffer, index_buffer, 1, - vertex); + sphereSDF(camera.position); + ChunkManager chunk_manager = ChunkManager_create(camera.position, 1, + sphereSDF, 0.0f); Shader shader = Shader_create("shaders/basic.vs", "shaders/basic.fs"); glUseProgram(shader.program); @@ -123,19 +113,8 @@ int main(int argc, char** argv) Camera_update(&camera); glUniformMatrix4fv(cam_loc, 1, GL_FALSE, camera.matrix[0]); - ChunkManager_update(&chunk_manager, camera.position); - for (int i = 0; i < chunk_manager.chunk_count; i++) - { - Chunk_outline(&chunk_manager.chunks[i], &vertices[i * 24], i * 8, - &indices[i * 24]); - } - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); - glBufferSubData(GL_ARRAY_BUFFER, 0, 24 * chunk_manager.chunk_count - * sizeof(float), vertices); - - glBindVertexArray(vertex_array); - glDrawElements(GL_LINES, 24 * chunk_manager.chunk_count, - GL_UNSIGNED_INT, 0); + ChunkManager_recenter(&chunk_manager, camera.position); + ChunkManager_drawChunks(&chunk_manager); SDL_GL_SwapWindow(window); } diff --git a/src/marching_cubes.c b/src/marching_cubes.c @@ -0,0 +1,410 @@ +#include "marching_cubes.h" + +#include <math.h> + +const 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, + 0xf99, 0xe90, 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, + 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30, 0x3a0, 0x2a9, + 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, + 0xfaa, 0xea3, 0xda9, 0xca0, 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, + 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60, + 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, + 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0, 0x650, 0x759, 0x453, 0x55a, + 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, + 0x859, 0x950, 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , + 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0, 0x8c0, 0x9c9, + 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, + 0x4ca, 0x5c3, 0x6c9, 0x7c0, 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, + 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650, + 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, + 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0, 0xb60, 0xa69, 0x963, 0x86a, + 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, + 0x569, 0x460, 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, + 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0, 0xd30, 0xc39, + 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, + 0x13a, 0x33 , 0x339, 0x230, 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, + 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190, + 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, + 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x0 +}; + +const 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}, + { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1}, + { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1}, + { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1}, + { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1}, + { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1}, + { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1}, + { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1}, + { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1}, + {10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1}, + { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1}, + { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1}, + { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1}, + { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1}, + { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1}, + { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1}, + {11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1}, + { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1}, + {11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1}, + {11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1}, + { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1}, + { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1}, + { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1}, + { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1}, + { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1}, + { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1}, + { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1}, + { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1}, + { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1}, + { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1}, + { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1}, + { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1}, + { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1}, + { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1}, + { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1}, + {10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1}, + {10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1}, + { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1}, + { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1}, + { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1}, + { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1}, + { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1}, + { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1}, + { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1}, + {10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1}, + {10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1}, + { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1}, + { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1}, + { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1}, + { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1}, + {11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1}, + { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1}, + { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1}, + { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + {10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1}, + { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1}, + { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1}, + { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1}, + {10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1}, + { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1}, + { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1}, + { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1}, + { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1}, + { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1}, + {10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1}, + { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1}, + { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1}, + { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1}, + {10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1}, + { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1}, + {10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1}, + { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1}, + { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1}, + { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1}, + { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1}, + { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1}, + { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1}, + { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1}, + { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1}, + { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1}, + { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1}, + { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1}, + { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1}, + { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1}, + { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1}, + { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1}, + { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1}, + { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1}, + {11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1}, + { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1}, + { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1}, + { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1}, + { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1}, + { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1}, + {10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1}, + { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1}, + { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1}, + {10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1}, + {11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1}, + { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1}, + { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1}, + { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1}, + { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1}, + { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1}, + { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1}, + { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1}, + { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1}, + { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1}, + {10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1}, + { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1}, + { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1}, + { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1}, + { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1}, + { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1}, + { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1}, + { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1}, + { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1}, + { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1}, + { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1}, + { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1}, + { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1}, + { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1}, + {11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1}, + {11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1}, + { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1}, + { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1}, + { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1}, + { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1}, + { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1}, + { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1}, + { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1}, + { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1}, + { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1}, + { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1}, + { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1}, + { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} +}; + +int MC_index(const Vec3 corners[8], SDF f, float isolevel) +{ + int mc_index = 0; + if (f(corners[0]) < isolevel) mc_index |= 1; + if (f(corners[1]) < isolevel) mc_index |= 2; + if (f(corners[2]) < isolevel) mc_index |= 4; + if (f(corners[3]) < isolevel) mc_index |= 8; + if (f(corners[4]) < isolevel) mc_index |= 16; + if (f(corners[5]) < isolevel) mc_index |= 32; + if (f(corners[6]) < isolevel) mc_index |= 64; + if (f(corners[7]) < isolevel) mc_index |= 128; + return mc_index; +} + +void MC_interpVertex(const Vec3 a, const Vec3 b, SDF f, float isolevel, + Vec3 dst) +{ + float fa = f(a); + float fb = f(b); + if (fabsf(isolevel - fa) < 0.0001f || fabsf(fa - fb) < 0.0001f) + { + dst[0] = a[0]; + dst[1] = a[1]; + dst[2] = a[2]; + return; + } + if (fabsf(isolevel - fb) < 0.0001f) + { + dst[0] = b[0]; + dst[1] = b[1]; + dst[2] = b[2]; + return; + } + float t = (isolevel - fa) / (fb - fa); + dst[0] = a[0] + t * (b[0] - a[0]); + dst[1] = a[1] + t * (b[1] - a[1]); + dst[2] = a[2] + t * (b[2] - a[2]); + return; +} + +void MC_vertices(const Vec3 corners[8], SDF f, float isolevel, int mc_index, + Vec3 vertices[12]) +{ + 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]); + } + 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]); + } +} + +int MC_triangles(const Vec3 vertices[12], SDF f, float isolevel, int mc_index, + Vec3* triangles) +{ + int vertex_count = 0; + for (int i = 0; TRI_TABLE[mc_index][i] != -1; i++) + { + 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++; + } + 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); +} diff --git a/src/vec.c b/src/vec.c @@ -0,0 +1,6 @@ +#include "vec.h" + +bool IVec3_equal(const IVec3 a, const IVec3 b) +{ + return (a[0] == b[0]) & (a[1] == b[1]) & (a[2] == b[2]); +}