diff --git a/README.md b/README.md index 93494ce..74198fc 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ library | lastest version | category | description **stb_rect_pack.h** | 0.05 | graphics | simple 2D rectangle packer with decent quality **stretchy_buffer.h** | 1.01 | utility | typesafe dynamic array for C (i.e. approximation to vector<>), doesn't compile as C++ **stb_textedit.h** | 1.5 | UI | guts of a text editor for games etc implementing them from scratch -**stb_voxel_render.h** | 0.77 | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features +**stb_voxel_render.h** | 0.78 | 3D graphics | Minecraft-esque voxel rendering "engine" with many more features **stb_dxt.h** | 1.04 | 3D graphics | Fabian "ryg" Giesen's real-time DXT compressor **stb_perlin.h** | 0.2 | 3D graphics | revised Perlin noise (3D input, 1D output) **stb_easy_font.h** | 0.5 | 3D graphics | quick-and-dirty easy-to-deploy bitmap font for printing frame rate, etc diff --git a/stb_voxel_render.h b/stb_voxel_render.h index e1f2ed1..072572b 100644 --- a/stb_voxel_render.h +++ b/stb_voxel_render.h @@ -1,4 +1,4 @@ -// stb_voxel_render.h - v0.77 - Sean Barrett, 2015 - public domain +// stb_voxel_render.h - v0.78 - Sean Barrett, 2015 - public domain // // This library helps render large-scale "voxel" worlds for games, // in this case, one with blocks that can have textures and that @@ -72,7 +72,9 @@ // - indexed-by-texture-#2-id blend mode (alpha composite or modulate/multiply); // the first is good for decals, the second for detail textures, "light maps", // etc; both modes are controlled by texture #2's alpha, scaled by the -// per-vertex texture crossfade and the per-face color (if enabled on texture #2) +// per-vertex texture crossfade and the per-face color (if enabled on texture #2); +// modulate/multiply multiplies by an extra factor of 2.0 so that if you +// make detail maps whose average brightness is 0.5 everything works nicely. // // - ambient lighting: half-lambert directional plus constant, all scaled by vertex ao // - face can be fullbright (emissive), controlled by per-face color @@ -168,10 +170,11 @@ // // Features Porting Bugfixes & Warnings // Sean Barrett github:r-leyh Jesus Fernandez -// Miguel Lechon +// Miguel Lechon github:Arbeiterunfallversicherungsgesetz // // VERSION HISTORY // +// 0.78 bad "#else", compile as C++ // 0.77 documentation tweaks, rename config var to STB_VOXEL_RENDER_STATIC // 0.76 typos, signed/unsigned shader issue, more documentation // 0.75 initial release @@ -1164,7 +1167,7 @@ struct stbvox_mesh_maker typedef stbvox_uint16 stbvox_mesh_vertex; #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ ((stbvox_uint16) ((x)+((z)<<6))+((ao)<<10)) -#else defined(STBVOX_ICONFIG_VERTEX_8) +#elif defined(STBVOX_ICONFIG_VERTEX_8) typedef stbvox_uint8 stbvox_mesh_vertex; #define stbvox_vertex_encode(x,y,z,ao,texlerp) \ ((stbvox_uint8) ((z)+((ao)<<6)) @@ -1255,11 +1258,117 @@ enum STBVF_count, }; -// get opposite-facing normal & texgen for opposite face, used to map up-facing vheight data to down-facing data -static unsigned char stbvox_reverse_face[STBVF_count]; -static float stbvox_default_texgen[2][32][3]; -static float stbvox_default_normals[32][3]; -static float stbvox_default_texscale[128][4]; +///////////////////////////////////////////////////////////////////////////// +// +// tables -- i'd prefer if these were at the end of the file, but: C++ +// + +static float stbvox_default_texgen[2][32][3] = +{ + { { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, { 0, 0,-1 }, + { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, { 0, 0,-1 }, + { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, { 0, 0,-1 }, + { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, { 0, 0,-1 }, + + { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, + { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, + { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, + { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, + }, + { { 0, 0,-1 }, { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, + { 0, 0,-1 }, { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, + { 0, 0,-1 }, { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, + { 0, 0,-1 }, { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, + + { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, + { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, + { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, + { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, + }, +}; + +#define STBVOX_RSQRT2 0.7071067811865f +#define STBVOX_RSQRT3 0.5773502691896f + +static float stbvox_default_normals[32][3] = +{ + { 1,0,0 }, // east + { 0,1,0 }, // north + { -1,0,0 }, // west + { 0,-1,0 }, // south + { 0,0,1 }, // up + { 0,0,-1 }, // down + { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up + { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down + + { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up + { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up + { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up + { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up + { STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // ne & up + { STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // ne & down + { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up + { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down + + { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down + { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down + { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down + { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down + { -STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // NW & up + { -STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // NW & down + { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up + { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down + + { STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NE & up crossed + { -STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NW & up crossed + { -STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SW & up crossed + { STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SE & up crossed + { -STBVOX_RSQRT3,-STBVOX_RSQRT3, STBVOX_RSQRT3 }, // SW & up + { -STBVOX_RSQRT3,-STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // SW & up + { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up + { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down +}; + +static float stbvox_default_texscale[128][4] = +{ + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, + {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, +}; + +static unsigned char stbvox_default_palette_compact[64][3] = +{ + { 255,255,255 }, { 238,238,238 }, { 221,221,221 }, { 204,204,204 }, + { 187,187,187 }, { 170,170,170 }, { 153,153,153 }, { 136,136,136 }, + { 119,119,119 }, { 102,102,102 }, { 85, 85, 85 }, { 68, 68, 68 }, + { 51, 51, 51 }, { 34, 34, 34 }, { 17, 17, 17 }, { 0, 0, 0 }, + { 255,240,240 }, { 255,220,220 }, { 255,160,160 }, { 255, 32, 32 }, + { 200,120,160 }, { 200, 60,150 }, { 220,100,130 }, { 255, 0,128 }, + { 240,240,255 }, { 220,220,255 }, { 160,160,255 }, { 32, 32,255 }, + { 120,160,200 }, { 60,150,200 }, { 100,130,220 }, { 0,128,255 }, + { 240,255,240 }, { 220,255,220 }, { 160,255,160 }, { 32,255, 32 }, + { 160,200,120 }, { 150,200, 60 }, { 130,220,100 }, { 128,255, 0 }, + { 255,255,240 }, { 255,255,220 }, { 220,220,180 }, { 255,255, 32 }, + { 200,160,120 }, { 200,150, 60 }, { 220,130,100 }, { 255,128, 0 }, + { 255,240,255 }, { 255,220,255 }, { 220,180,220 }, { 255, 32,255 }, + { 160,120,200 }, { 150, 60,200 }, { 130,100,220 }, { 128, 0,255 }, + { 240,255,255 }, { 220,255,255 }, { 180,220,220 }, { 32,255,255 }, + { 120,200,160 }, { 60,200,150 }, { 100,220,130 }, { 0,255,128 }, +}; + static float stbvox_default_ambient[4][4] = { { 0,0,1 ,0 }, // reversed lighting direction @@ -1268,7 +1377,6 @@ static float stbvox_default_ambient[4][4] = { 0.5,0.5,0.5,1.0f/1000.0f/1000.0f }, // fog data for simple_fog }; -static unsigned char stbvox_default_palette_compact[64][3]; static float stbvox_default_palette[64][4]; static void stbvox_build_default_palette(void) @@ -1381,7 +1489,7 @@ static char *stbvox_fragment_program = #if defined(STBVOX_ICONFIG_GLSL) "#define rlerp(t,x,y) mix(x,y,t)\n" #elif defined(STBVOX_CONFIG_HLSL) - "#define rlerp(t,x,y) lerp(x,t,y)\n" + "#define rlerp(t,x,y) lerp(x,y,t)\n" #else #error "need definition of rlerp()" #endif @@ -1872,1593 +1980,1442 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro return face_data; } +// these are the types of faces each block can have +enum +{ + STBVOX_FT_none , + STBVOX_FT_upper , + STBVOX_FT_lower , + STBVOX_FT_solid , + STBVOX_FT_diag_012, + STBVOX_FT_diag_023, + STBVOX_FT_diag_013, + STBVOX_FT_diag_123, + STBVOX_FT_force , // can't be covered up, used for internal faces, also hides nothing + STBVOX_FT_partial , // only covered by solid, never covers anything else + + STBVOX_FT_count +}; + static unsigned char stbvox_face_lerp[6] = { 0,2,0,2,4,4 }; static unsigned char stbvox_vert3_lerp[5] = { 0,3,6,9,12 }; static unsigned char stbvox_vert_lerp_for_face_lerp[4] = { 0, 4, 7, 7 }; static unsigned char stbvox_face3_lerp[6] = { 0,3,6,9,12,14 }; static unsigned char stbvox_vert_lerp_for_simple[4] = { 0,2,5,7 }; static unsigned char stbvox_face3_updown[8] = { 0,2,5,7,0,2,5,7 }; // ignore top bit + // vertex offsets for face vertices -static unsigned char stbvox_vertex_vector[6][4][3]; -static stbvox_mesh_vertex stbvox_vmesh_delta_normal[6][4]; -static stbvox_mesh_vertex stbvox_vmesh_pre_vheight[6][4]; -static stbvox_mesh_vertex stbvox_vmesh_delta_half_z[6][4]; -static stbvox_mesh_vertex stbvox_vmesh_crossed_pair[6][4]; +static unsigned char stbvox_vertex_vector[6][4][3] = +{ + { { 1,0,1 }, { 1,1,1 }, { 1,1,0 }, { 1,0,0 } }, // east + { { 1,1,1 }, { 0,1,1 }, { 0,1,0 }, { 1,1,0 } }, // north + { { 0,1,1 }, { 0,0,1 }, { 0,0,0 }, { 0,1,0 } }, // west + { { 0,0,1 }, { 1,0,1 }, { 1,0,0 }, { 0,0,0 } }, // south + { { 0,1,1 }, { 1,1,1 }, { 1,0,1 }, { 0,0,1 } }, // up + { { 0,0,0 }, { 1,0,0 }, { 1,1,0 }, { 0,1,0 } }, // down +}; // stbvox_vertex_vector, but read coordinates as binary numbers, zyx -static unsigned char stbvox_vertex_selector[6][4]; - -void stbvox_get_quad_vertex_pointer(stbvox_mesh_maker *mm, int mesh, stbvox_mesh_vertex **vertices, stbvox_mesh_face face) +static unsigned char stbvox_vertex_selector[6][4] = { - char *p = mm->output_cur[mesh][0]; - int step = mm->output_step[mesh][0]; - - // allocate a new quad from the mesh - vertices[0] = (stbvox_mesh_vertex *) p; p += step; - vertices[1] = (stbvox_mesh_vertex *) p; p += step; - vertices[2] = (stbvox_mesh_vertex *) p; p += step; - vertices[3] = (stbvox_mesh_vertex *) p; p += step; - mm->output_cur[mesh][0] = p; - - // output the face - #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE - // write face as interleaved vertex data - *(stbvox_mesh_face *) (vertices[0]+1) = face; - *(stbvox_mesh_face *) (vertices[1]+1) = face; - *(stbvox_mesh_face *) (vertices[2]+1) = face; - *(stbvox_mesh_face *) (vertices[3]+1) = face; - #else - *(stbvox_mesh_face *) mm->output_cur[mesh][1] = face; - mm->output_cur[mesh][1] += 4; - #endif -} + { 5,7,3,1 }, + { 7,6,2,3 }, + { 6,4,0,2 }, + { 4,5,1,0 }, + { 6,7,5,4 }, + { 0,1,3,2 }, +}; -void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, int normal) +static stbvox_mesh_vertex stbvox_vmesh_delta_normal[6][4] = { - stbvox_mesh_face face_data = stbvox_compute_mesh_face_value(mm,rot,face,v_off, normal); + { stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(0,0,1,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,1,0,0) , + stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,1,0,0) , + stbvox_vertex_encode(1,1,1,0,0) , + stbvox_vertex_encode(1,0,1,0,0) , + stbvox_vertex_encode(0,0,1,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; - // still need to compute ao & texlerp for each vertex +static stbvox_mesh_vertex stbvox_vmesh_pre_vheight[6][4] = +{ + { stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; - // first compute texlerp into p1 - stbvox_mesh_vertex p1[4] = { 0 }; +static stbvox_mesh_vertex stbvox_vmesh_delta_half_z[6][4] = +{ + { stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; - if (mm->input.block_texlerp) { - stbvox_block_type bt = mm->input.blocktype[v_off]; - unsigned char val = mm->input.block_texlerp[bt]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); - } else if (mm->input.block_texlerp_face) { - stbvox_block_type bt = mm->input.blocktype[v_off]; - unsigned char bt_face = STBVOX_ROTATE(face, rot.block); - unsigned char val = mm->input.block_texlerp_face[bt][bt_face]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); - } else if (mm->input.texlerp_face3) { - unsigned char val = (mm->input.texlerp_face3[v_off] >> stbvox_face3_lerp[face]) & 7; - if (face >= STBVOX_FACE_up) - val = stbvox_face3_updown[val]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); - } else if (mm->input.texlerp_simple) { - unsigned char val = mm->input.texlerp_simple[v_off]; - unsigned char lerp_face = (val >> 2) & 7; - if (lerp_face == face) { - p1[0] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][0]] >> 5) & 7; - p1[1] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][1]] >> 5) & 7; - p1[2] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][2]] >> 5) & 7; - p1[3] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][3]] >> 5) & 7; - p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); - p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); - p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); - p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); - } else { - unsigned char base = stbvox_vert_lerp_for_simple[val&3]; - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,base); - } - } else if (mm->input.texlerp) { - unsigned char facelerp = (mm->input.texlerp[v_off] >> stbvox_face_lerp[face]) & 3; - if (facelerp == STBVOX_TEXLERP_FACE_use_vert) { - if (mm->input.texlerp_vert3 && face != STBVOX_FACE_down) { - unsigned char shift = stbvox_vert3_lerp[face]; - p1[0] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][0]] >> shift) & 7; - p1[1] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][1]] >> shift) & 7; - p1[2] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][2]] >> shift) & 7; - p1[3] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][3]] >> shift) & 7; - } else { - p1[0] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][0]]>>6]; - p1[1] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][1]]>>6]; - p1[2] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][2]]>>6]; - p1[3] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][3]]>>6]; - } - p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); - p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); - p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); - p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); - } else { - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,stbvox_vert_lerp_for_face_lerp[facelerp]); - } - } else { - p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,7); - } +static stbvox_mesh_vertex stbvox_vmesh_crossed_pair[6][4] = +{ + { stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(0,1,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) }, + { stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) }, + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) }, + { stbvox_vertex_encode(0,0,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,0,0,0,0) }, + // not used, so we leave it non-degenerate to make sure it doesn't get gen'd accidentally + { stbvox_vertex_encode(0,1,2,0,0) , + stbvox_vertex_encode(1,1,2,0,0) , + stbvox_vertex_encode(1,0,2,0,0) , + stbvox_vertex_encode(0,0,2,0,0) }, + { stbvox_vertex_encode(0,0,0,0,0) , + stbvox_vertex_encode(1,0,0,0,0) , + stbvox_vertex_encode(1,1,0,0,0) , + stbvox_vertex_encode(0,1,0,0,0) } +}; - { - stbvox_mesh_vertex *mv[4]; - stbvox_get_quad_vertex_pointer(mm, mesh, mv, face_data); +#define STBVOX_MAX_GEOM 16 +#define STBVOX_NUM_ROTATION 4 - if (mm->input.lighting) { - // @TODO: lighting at block centers, but not gathered, instead constant-per-face - if (mm->input.lighting_at_vertices) { - int i; - for (i=0; i < 4; ++i) { - *mv[i] = vertbase + face_coord[i] - + stbvox_vertex_encode(0,0,0,mm->input.lighting[v_off + mm->cube_vertex_offset[face][i]] & 63,0) - + p1[i]; - } - } else { - unsigned char *amb = &mm->input.lighting[v_off]; - int i,j; - #ifdef STBVOX_ROTATION_IN_LIGHTING - #define STBVOX_GET_LIGHTING(light) ((light) & ~3) - #define STBVOX_LIGHTING_ROUNDOFF 8 - #else - #define STBVOX_GET_LIGHTING(light) (light) - #define STBVOX_LIGHTING_ROUNDOFF 2 - #endif +// this is used to determine if a face is ever generated at all +static unsigned char stbvox_hasface[STBVOX_MAX_GEOM][STBVOX_NUM_ROTATION] = +{ + { 0,0,0,0 }, // empty + { 0,0,0,0 }, // knockout + { 63,63,63,63 }, // solid + { 63,63,63,63 }, // transp + { 63,63,63,63 }, // slab + { 63,63,63,63 }, // slab + { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // floor slopes + { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // ceil slopes + { 47,47,47,47 }, // wall-projected diagonal with down face + { 31,31,31,31 }, // wall-projected diagonal with up face + { 63,63,63,63 }, // crossed-pair has special handling, but avoid early-out + { 63,63,63,63 }, // force + { 63,63,63,63 }, + { 63,63,63,63 }, + { 63,63,63,63 }, + { 63,63,63,63 }, +}; - for (i=0; i < 4; ++i) { - // for each vertex, gather from the four neighbor blocks it's facing - unsigned char *vamb = &amb[mm->cube_vertex_offset[face][i]]; - int total=0; - for (j=0; j < 4; ++j) - total += STBVOX_GET_LIGHTING(vamb[mm->vertex_gather_offset[face][j]]); - *mv[i] = vertbase + face_coord[i] - + stbvox_vertex_encode(0,0,0,(total+STBVOX_LIGHTING_ROUNDOFF)>>4,0) - + p1[i]; - // >> 4 is because: - // >> 2 to divide by 4 to get average over 4 samples - // >> 2 because input is 8 bits, output is 6 bits - } +// this determines which face type above is visible on each side of the geometry +static unsigned char stbvox_facetype[STBVOX_GEOM_count][6] = +{ + { 0, }, // STBVOX_GEOM_empty + { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // knockout + { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // solid + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // transp - // @TODO: note that gathering baked *lighting* - // is different from gathering baked ao; baked ao can count - // solid blocks as 0 ao, but baked lighting wants average - // of non-blocked--not take average & treat blocked as 0. And - // we can't bake the right value into the solid blocks - // because they can have different lighting values on - // different sides. So we need to actually gather and - // then divide by 0..4 (which we can do with a table-driven - // multiply, or have an 'if' for the 3 case) + { STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_solid, STBVOX_FT_force }, + { STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_force, STBVOX_FT_solid }, + { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_none, STBVOX_FT_force, STBVOX_FT_solid }, + { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_none, STBVOX_FT_solid, STBVOX_FT_force }, - } - } else { - vertbase += stbvox_vertex_encode(0,0,0,63,0); - *mv[0] = vertbase + face_coord[0] + p1[0]; - *mv[1] = vertbase + face_coord[1] + p1[1]; - *mv[2] = vertbase + face_coord[2] + p1[2]; - *mv[3] = vertbase + face_coord[3] + p1[3]; - } - } -} + { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_force, STBVOX_FT_none, STBVOX_FT_solid }, + { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_force, STBVOX_FT_solid, STBVOX_FT_none }, + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, 0,0 }, // crossed pair + { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // GEOM_force -#ifndef STBVOX_OPTIMIZED_VHEIGHT -static stbvox_face_up_normal_012[4][4][4]; -static stbvox_face_up_normal_013[4][4][4]; -static stbvox_face_up_normal_023[4][4][4]; -static stbvox_face_up_normal_123[4][4][4]; + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced + { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced +}; -// render non-planar quads by splitting into two triangles, rendering each as a degenerate quad -static void stbvox_make_12_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) +// This table indicates what normal to use for the "up" face of a sloped geom +// @TODO this could be done with math given the current arrangement of the enum, but let's not require it +static unsigned char stbvox_floor_slope_for_rot[4] = { - stbvox_mesh_vertex v[4]; - - unsigned char normal1 = stbvox_face_up_normal_012[ht[2]][ht[1]][ht[0]]; - unsigned char normal2 = stbvox_face_up_normal_123[ht[3]][ht[2]][ht[1]]; + STBVF_su, + STBVF_wu, // @TODO: why is this reversed from what it should be? this is a north-is-up face, so slope should be south&up + STBVF_nu, + STBVF_eu, +}; - if (face == STBVOX_FACE_down) { - normal1 = stbvox_reverse_face[normal1]; - normal2 = stbvox_reverse_face[normal2]; - } +static unsigned char stbvox_ceil_slope_for_rot[4] = +{ + STBVF_sd, + STBVF_ed, + STBVF_nd, + STBVF_wd, +}; - // the floor side face_coord is stored in order NW,NE,SE,SW, but ht[] is stored SW,SE,NW,NE - v[0] = face_coord[2]; - v[1] = face_coord[3]; - v[2] = face_coord[0]; - v[3] = face_coord[2]; - stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); - v[1] = face_coord[0]; - v[2] = face_coord[1]; - stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); -} +// this table indicates whether, for each pair of types above, a face is visible. +// each value indicates whether a given type is visible for all neighbor types +static unsigned short stbvox_face_visible[STBVOX_FT_count] = +{ + // we encode the table by listing which cases cause *obscuration*, and bitwise inverting that + // table is pre-shifted by 5 to save a shift when it's accessed + (unsigned short) ((~0x07ff )<<5), // none is completely obscured by everything + (unsigned short) ((~((1<y_stride_in_bytes; - int ew_off = mm->x_stride_in_bytes; - - unsigned char *blockptr = &mm->input.blocktype[v_off]; - stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); - - stbvox_rotate rot = { 0,0,0,0 }; - unsigned char simple_rot = 0; - - unsigned char mesh = mm->default_mesh; - - if (mm->input.selector) - mesh = mm->input.selector[v_off]; + { 0,1,3,2 }, // zyx=000 + { 1,3,2,0 }, // zyx=001 + { 2,0,1,3 }, // zyx=010 + { 3,2,0,1 }, // zyx=011 + { 4,5,7,6 }, // zyx=100 + { 5,7,6,4 }, // zyx=101 + { 6,4,5,7 }, // zyx=110 + { 7,6,4,5 }, // zyx=111 +}; - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { - mm->full = 1; - return; +#ifdef STBVOX_OPTIMIZED_VHEIGHT +// optimized vheight generates a single normal over the entire face, even if it's not planar +static stbvox_optimized_face_up_normal[4][4][4][4] = +{ + { + { + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_nu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_nu , }, + },{ + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, + },{ + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + }, + },{ + { + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + }, + },{ + { + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + }, + },{ + { + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, + { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_nw_u, }, + { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + },{ + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, + { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_nw_u, }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + }, + }, +}; +#else +// which normal to use for a given vheight that's planar +// @TODO: this table was constructed by hand and may have bugs +// nw se sw +static stbvox_planar_face_up_normal[4][4][4] = +{ + { // sw,se,nw,ne; ne = se+nw-sw + { STBVF_u , 0 , 0 , 0 }, // 0,0,0,0; 1,0,0,-1; 2,0,0,-2; 3,0,0,-3; + { STBVF_u , STBVF_u , 0 , 0 }, // 0,1,0,1; 1,1,0, 0; 2,1,0,-1; 3,1,0,-2; + { STBVF_wu , STBVF_nw_u, STBVF_nu , 0 }, // 0,2,0,2; 1,2,0, 1; 2,2,0, 0; 3,2,0,-1; + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nu }, // 0,3,0,3; 1,3,0, 2; 2,3,0, 1; 3,3,0, 0; + },{ + { STBVF_u , STBVF_u , 0 , 0 }, // 0,0,1,1; 1,0,1, 0; 2,0,1,-1; 3,0,1,-2; + { STBVF_sw_u, STBVF_u , STBVF_ne_u, 0 }, // 0,1,1,2; 1,1,1, 1; 2,1,1, 0; 3,1,1,-1; + { STBVF_sw_u, STBVF_u , STBVF_u , STBVF_ne_u }, // 0,2,1,3; 1,2,1, 2; 2,2,1, 1; 3,2,1, 0; + { 0 , STBVF_wu , STBVF_nw_u, STBVF_nu }, // 0,3,1,4; 1,3,1, 3; 2,3,1, 2; 3,3,1, 1; + },{ + { STBVF_su , STBVF_se_u, STBVF_eu , 0 }, // 0,0,2,2; 1,0,2, 1; 2,0,2, 0; 3,0,2,-1; + { STBVF_sw_u, STBVF_u , STBVF_u , STBVF_ne_u }, // 0,1,2,3; 1,1,2, 2; 2,1,2, 1; 3,1,2, 0; + { 0 , STBVF_sw_u, STBVF_u , STBVF_ne_u }, // 0,2,2,4; 1,2,2, 3; 2,2,2, 2; 3,2,2, 1; + { 0 , 0 , STBVF_u , STBVF_u }, // 0,3,2,5; 1,3,2, 4; 2,3,2, 3; 3,3,2, 2; + },{ + { STBVF_su , STBVF_se_u, STBVF_se_u, STBVF_eu }, // 0,0,3,3; 1,0,3, 2; 2,0,3, 1; 3,0,3, 0; + { 0 , STBVF_su , STBVF_se_u, STBVF_eu }, // 0,1,3,4; 1,1,3, 3; 2,1,3, 2; 3,1,3, 1; + { 0 , 0 , STBVF_u , STBVF_u }, // 0,2,3,5; 1,2,3, 4; 2,2,3, 3; 3,2,3, 2; + { 0 , 0 , 0 , STBVF_u }, // 0,3,3,6; 1,3,3, 5; 2,3,3, 4; 3,3,3, 3; } +}; - #ifdef STBVOX_ROTATION_IN_LIGHTING - simple_rot = mm->input.lighting[v_off] & 3; - #endif - - if (blockptr[ 1]==0) { - rot.facerot = simple_rot; - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_up , v_off, pos, basevert, vmesh+4*STBVOX_FACE_up, mesh, STBVOX_FACE_up); - } - if (blockptr[-1]==0) { - rot.facerot = (-simple_rot) & 3; - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_down, v_off, pos, basevert, vmesh+4*STBVOX_FACE_down, mesh, STBVOX_FACE_down); +// these tables were constructed automatically using a variant of the code +// below; however, they seem wrong, so who knows +static stbvox_face_up_normal_012[4][4][4] = +{ + { + { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nu , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, } +}; - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rot.block = (val >> 0) & 3; - rot.overlay = (val >> 2) & 3; - //rot.tex2 = (val >> 4) & 3; - rot.ecolor = (val >> 6) & 3; - } else { - rot.block = rot.overlay = rot.ecolor = simple_rot; +static stbvox_face_up_normal_013[4][4][4] = +{ + { + { STBVF_u , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_nw_u, STBVF_nu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, + { STBVF_wu , STBVF_u , STBVF_eu , STBVF_eu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_eu , }, + { STBVF_wu , STBVF_wu , STBVF_u , STBVF_eu , }, + { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, + },{ + { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, + { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_su , STBVF_eu , }, + { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_u , }, } - rot.facerot = 0; - - if (blockptr[ ns_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, vmesh+4*STBVOX_FACE_north, mesh, STBVOX_FACE_north); - if (blockptr[-ns_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, vmesh+4*STBVOX_FACE_south, mesh, STBVOX_FACE_south); - if (blockptr[ ew_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, vmesh+4*STBVOX_FACE_east, mesh, STBVOX_FACE_east); - if (blockptr[-ew_off]==0) - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, vmesh+4*STBVOX_FACE_west, mesh, STBVOX_FACE_west); -} - +}; -// void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) -// -// complex case for mesh generation: we have lots of different -// block types, and we don't want to generate faces of blocks -// if they're hidden by neighbors. -// -// we use lots of tables to determine this: we have a table -// which tells us what face type is generated for each type of -// geometry, and then a table that tells us whether that type -// is hidden by a neighbor. - - -#define STBVOX_MAX_GEOM 16 -#define STBVOX_NUM_ROTATION 4 - -// this is used to determine if a face is ever generated at all -static unsigned char stbvox_hasface[STBVOX_MAX_GEOM][STBVOX_NUM_ROTATION]; - -// this determines which face type above is visible on each side of the geometry -static unsigned char stbvox_facetype[STBVOX_GEOM_count][6]; - -// This table indicates what normal to use for the "up" face of a sloped geom -static unsigned char stbvox_floor_slope_for_rot[4]; -static unsigned char stbvox_ceil_slope_for_rot[4]; - -// these are the types of faces each block can have -enum +static stbvox_face_up_normal_023[4][4][4] = { - STBVOX_FT_none , - STBVOX_FT_upper , - STBVOX_FT_lower , - STBVOX_FT_solid , - STBVOX_FT_diag_012, - STBVOX_FT_diag_023, - STBVOX_FT_diag_013, - STBVOX_FT_diag_123, - STBVOX_FT_force , // can't be covered up, used for internal faces, also hides nothing - STBVOX_FT_partial , // only covered by solid, never covers anything else - - STBVOX_FT_count + { + { STBVF_u , STBVF_nu , STBVF_nu , STBVF_nu , }, + { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, + },{ + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + { STBVF_su , STBVF_u , STBVF_nu , STBVF_nu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + },{ + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + { STBVF_su , STBVF_su , STBVF_u , STBVF_nu , }, + { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, + },{ + { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_u , }, + } }; -// this table indicates whether, for each pair of types above, a face is visible. -// each value indicates whether a given type is visible for each neighbor type -static unsigned short stbvox_face_visible[STBVOX_FT_count]; +static stbvox_face_up_normal_123[4][4][4] = +{ + { + { STBVF_u , STBVF_nu , STBVF_nu , STBVF_nu , }, + { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, + },{ + { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, + { STBVF_su , STBVF_u , STBVF_nu , STBVF_nu , }, + { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, + },{ + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, + { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, + { STBVF_su , STBVF_su , STBVF_u , STBVF_nu , }, + { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, + },{ + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, + { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, + { STBVF_su , STBVF_su , STBVF_su , STBVF_u , }, + } +}; +#endif -// the vertex heights of the block types, in binary vertex order (zyx): -// lower: SW, SE, NW, NE; upper: SW, SE, NW, NE -static stbvox_mesh_vertex stbvox_geometry_vheight[8][8]; +void stbvox_get_quad_vertex_pointer(stbvox_mesh_maker *mm, int mesh, stbvox_mesh_vertex **vertices, stbvox_mesh_face face) +{ + char *p = mm->output_cur[mesh][0]; + int step = mm->output_step[mesh][0]; -// rotate vertices defined as [z][y][x] coords -static unsigned char stbvox_rotate_vertex[8][4]; + // allocate a new quad from the mesh + vertices[0] = (stbvox_mesh_vertex *) p; p += step; + vertices[1] = (stbvox_mesh_vertex *) p; p += step; + vertices[2] = (stbvox_mesh_vertex *) p; p += step; + vertices[3] = (stbvox_mesh_vertex *) p; p += step; + mm->output_cur[mesh][0] = p; -#ifdef STBVOX_OPTIMIZED_VHEIGHT -static stbvox_optimized_face_up_normal[4][4][4][4]; -#else -static stbvox_planar_face_up_normal[4][4][4]; -#endif + // output the face + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + // write face as interleaved vertex data + *(stbvox_mesh_face *) (vertices[0]+1) = face; + *(stbvox_mesh_face *) (vertices[1]+1) = face; + *(stbvox_mesh_face *) (vertices[2]+1) = face; + *(stbvox_mesh_face *) (vertices[3]+1) = face; + #else + *(stbvox_mesh_face *) mm->output_cur[mesh][1] = face; + mm->output_cur[mesh][1] += 4; + #endif +} -static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) +void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, int normal) { - int ns_off = mm->y_stride_in_bytes; - int ew_off = mm->x_stride_in_bytes; - int visible_faces, visible_base; - unsigned char mesh; - - // first gather the geometry info for this block and all neighbors + stbvox_mesh_face face_data = stbvox_compute_mesh_face_value(mm,rot,face,v_off, normal); - unsigned char bt, nbt[6]; - unsigned char geo, ngeo[6]; - unsigned char rot, nrot[6]; + // still need to compute ao & texlerp for each vertex - bt = mm->input.blocktype[v_off]; - nbt[0] = mm->input.blocktype[v_off + ew_off]; - nbt[1] = mm->input.blocktype[v_off + ns_off]; - nbt[2] = mm->input.blocktype[v_off - ew_off]; - nbt[3] = mm->input.blocktype[v_off - ns_off]; - nbt[4] = mm->input.blocktype[v_off + 1]; - nbt[5] = mm->input.blocktype[v_off - 1]; - if (mm->input.geometry) { - int i; - geo = mm->input.geometry[v_off]; - ngeo[0] = mm->input.geometry[v_off + ew_off]; - ngeo[1] = mm->input.geometry[v_off + ns_off]; - ngeo[2] = mm->input.geometry[v_off - ew_off]; - ngeo[3] = mm->input.geometry[v_off - ns_off]; - ngeo[4] = mm->input.geometry[v_off + 1]; - ngeo[5] = mm->input.geometry[v_off - 1]; + // first compute texlerp into p1 + stbvox_mesh_vertex p1[4] = { 0 }; - #ifndef STBVOX_ROTATION_IN_LIGHTING - rot = (geo >> 4) & 3; - geo &= 15; - for (i=0; i < 6; ++i) { - nrot[i] = (ngeo[i] >> 4) & 3; - ngeo[i] &= 15; - } - #endif - STBVOX_NOTUSED(i); - } else { - int i; - assert(mm->input.block_geometry); - geo = mm->input.block_geometry[bt]; - for (i=0; i < 6; ++i) - ngeo[i] = mm->input.block_geometry[nbt[i]]; - if (mm->input.selector) { - #ifndef STBVOX_ROTATION_IN_LIGHTING - rot = (mm->input.selector[v_off ] >> 4) & 3; - nrot[0] = (mm->input.selector[v_off + ew_off] >> 4) & 3; - nrot[1] = (mm->input.selector[v_off + ns_off] >> 4) & 3; - nrot[2] = (mm->input.selector[v_off - ew_off] >> 4) & 3; - nrot[3] = (mm->input.selector[v_off - ns_off] >> 4) & 3; - nrot[4] = (mm->input.selector[v_off + 1] >> 4) & 3; - nrot[5] = (mm->input.selector[v_off - 1] >> 4) & 3; - #endif + if (mm->input.block_texlerp) { + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char val = mm->input.block_texlerp[bt]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.block_texlerp_face) { + stbvox_block_type bt = mm->input.blocktype[v_off]; + unsigned char bt_face = STBVOX_ROTATE(face, rot.block); + unsigned char val = mm->input.block_texlerp_face[bt][bt_face]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.texlerp_face3) { + unsigned char val = (mm->input.texlerp_face3[v_off] >> stbvox_face3_lerp[face]) & 7; + if (face >= STBVOX_FACE_up) + val = stbvox_face3_updown[val]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,val); + } else if (mm->input.texlerp_simple) { + unsigned char val = mm->input.texlerp_simple[v_off]; + unsigned char lerp_face = (val >> 2) & 7; + if (lerp_face == face) { + p1[0] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][0]] >> 5) & 7; + p1[1] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][1]] >> 5) & 7; + p1[2] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][2]] >> 5) & 7; + p1[3] = (mm->input.texlerp_simple[v_off + mm->cube_vertex_offset[face][3]] >> 5) & 7; + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); } else { - #ifndef STBVOX_ROTATION_IN_LIGHTING - rot = (geo>>4)&3; - geo &= 15; - for (i=0; i < 6; ++i) { - nrot[i] = (ngeo[i]>>4)&3; - ngeo[i] &= 15; + unsigned char base = stbvox_vert_lerp_for_simple[val&3]; + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,base); + } + } else if (mm->input.texlerp) { + unsigned char facelerp = (mm->input.texlerp[v_off] >> stbvox_face_lerp[face]) & 3; + if (facelerp == STBVOX_TEXLERP_FACE_use_vert) { + if (mm->input.texlerp_vert3 && face != STBVOX_FACE_down) { + unsigned char shift = stbvox_vert3_lerp[face]; + p1[0] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][0]] >> shift) & 7; + p1[1] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][1]] >> shift) & 7; + p1[2] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][2]] >> shift) & 7; + p1[3] = (mm->input.texlerp_vert3[mm->cube_vertex_offset[face][3]] >> shift) & 7; + } else { + p1[0] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][0]]>>6]; + p1[1] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][1]]>>6]; + p1[2] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][2]]>>6]; + p1[3] = stbvox_vert_lerp_for_simple[mm->input.texlerp[mm->cube_vertex_offset[face][3]]>>6]; } - #endif + p1[0] = stbvox_vertex_encode(0,0,0,0,p1[0]); + p1[1] = stbvox_vertex_encode(0,0,0,0,p1[1]); + p1[2] = stbvox_vertex_encode(0,0,0,0,p1[2]); + p1[3] = stbvox_vertex_encode(0,0,0,0,p1[3]); + } else { + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,stbvox_vert_lerp_for_face_lerp[facelerp]); } + } else { + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,7); } - #ifdef STBVOX_ROTATION_IN_LIGHTING - rot = mm->input.lighting[v_off] & 3; - nrot[0] = (mm->input.lighting[v_off + ew_off]) & 3; - nrot[1] = (mm->input.lighting[v_off + ns_off]) & 3; - nrot[2] = (mm->input.lighting[v_off - ew_off]) & 3; - nrot[3] = (mm->input.lighting[v_off - ns_off]) & 3; - nrot[4] = (mm->input.lighting[v_off + 1]) & 3; - nrot[5] = (mm->input.lighting[v_off - 1]) & 3; - #endif - - if (geo == STBVOX_GEOM_transp) { - // transparency has a special rule: if the blocktype is the same, - // and the faces are compatible, then can hide them; otherwise, - // force them on - // Note that this means we don't support any transparentshapes other - // than solid blocks, since detecting them is too complicated. If - // you wanted to do something like minecraft water, you probably - // should just do that with a separate renderer anyway. (We don't - // support transparency sorting so you need to use alpha test - // anyway) - int i; - for (i=0; i < 6; ++i) - if (nbt[i] != bt) { - nbt[i] = 0; - ngeo[i] = STBVOX_GEOM_empty; - } else - ngeo[i] = STBVOX_GEOM_solid; - geo = STBVOX_GEOM_solid; - } - - // now compute the face visibility - visible_base = stbvox_hasface[geo][rot]; - // @TODO: assert(visible_base != 0); // we should have early-outted earlier in this case - visible_faces = 0; + { + stbvox_mesh_vertex *mv[4]; + stbvox_get_quad_vertex_pointer(mm, mesh, mv, face_data); - // now, for every face that might be visible, check if neighbor hides it - if (visible_base & (1 << STBVOX_FACE_east)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_east+ rot )&3]; - int ntype = stbvox_facetype[ngeo[0]][(STBVOX_FACE_west+nrot[0])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_east)) & (1 << STBVOX_FACE_east); - } - if (visible_base & (1 << STBVOX_FACE_north)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_north+ rot )&3]; - int ntype = stbvox_facetype[ngeo[1]][(STBVOX_FACE_south+nrot[1])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_north)) & (1 << STBVOX_FACE_north); - } - if (visible_base & (1 << STBVOX_FACE_west)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_west+ rot )&3]; - int ntype = stbvox_facetype[ngeo[2]][(STBVOX_FACE_east+nrot[2])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_west)) & (1 << STBVOX_FACE_west); - } - if (visible_base & (1 << STBVOX_FACE_south)) { - int type = stbvox_facetype[ geo ][(STBVOX_FACE_south+ rot )&3]; - int ntype = stbvox_facetype[ngeo[3]][(STBVOX_FACE_north+nrot[3])&3]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_south)) & (1 << STBVOX_FACE_south); - } - if (visible_base & (1 << STBVOX_FACE_up)) { - int type = stbvox_facetype[ geo ][STBVOX_FACE_up]; - int ntype = stbvox_facetype[ngeo[4]][STBVOX_FACE_down]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_up)) & (1 << STBVOX_FACE_up); - } - if (visible_base & (1 << STBVOX_FACE_down)) { - int type = stbvox_facetype[ geo ][STBVOX_FACE_down]; - int ntype = stbvox_facetype[ngeo[5]][STBVOX_FACE_up]; - visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_down)) & (1 << STBVOX_FACE_down); - } + if (mm->input.lighting) { + // @TODO: lighting at block centers, but not gathered, instead constant-per-face + if (mm->input.lighting_at_vertices) { + int i; + for (i=0; i < 4; ++i) { + *mv[i] = vertbase + face_coord[i] + + stbvox_vertex_encode(0,0,0,mm->input.lighting[v_off + mm->cube_vertex_offset[face][i]] & 63,0) + + p1[i]; + } + } else { + unsigned char *amb = &mm->input.lighting[v_off]; + int i,j; + #ifdef STBVOX_ROTATION_IN_LIGHTING + #define STBVOX_GET_LIGHTING(light) ((light) & ~3) + #define STBVOX_LIGHTING_ROUNDOFF 8 + #else + #define STBVOX_GET_LIGHTING(light) (light) + #define STBVOX_LIGHTING_ROUNDOFF 2 + #endif - if (geo == STBVOX_GEOM_force) - geo = STBVOX_GEOM_solid; + for (i=0; i < 4; ++i) { + // for each vertex, gather from the four neighbor blocks it's facing + unsigned char *vamb = &amb[mm->cube_vertex_offset[face][i]]; + int total=0; + for (j=0; j < 4; ++j) + total += STBVOX_GET_LIGHTING(vamb[mm->vertex_gather_offset[face][j]]); + *mv[i] = vertbase + face_coord[i] + + stbvox_vertex_encode(0,0,0,(total+STBVOX_LIGHTING_ROUNDOFF)>>4,0) + + p1[i]; + // >> 4 is because: + // >> 2 to divide by 4 to get average over 4 samples + // >> 2 because input is 8 bits, output is 6 bits + } - assert((geo == STBVOX_GEOM_crossed_pair) ? (visible_faces == 15) : 1); + // @TODO: note that gathering baked *lighting* + // is different from gathering baked ao; baked ao can count + // solid blocks as 0 ao, but baked lighting wants average + // of non-blocked--not take average & treat blocked as 0. And + // we can't bake the right value into the solid blocks + // because they can have different lighting values on + // different sides. So we need to actually gather and + // then divide by 0..4 (which we can do with a table-driven + // multiply, or have an 'if' for the 3 case) - // now we finally know for sure which faces are getting generated - if (visible_faces == 0) - return; + } + } else { + vertbase += stbvox_vertex_encode(0,0,0,63,0); + *mv[0] = vertbase + face_coord[0] + p1[0]; + *mv[1] = vertbase + face_coord[1] + p1[1]; + *mv[2] = vertbase + face_coord[2] + p1[2]; + *mv[3] = vertbase + face_coord[3] + p1[3]; + } + } +} - mesh = mm->default_mesh; - if (mm->input.selector) - mesh = mm->input.selector[v_off]; +#ifndef STBVOX_OPTIMIZED_VHEIGHT +// get opposite-facing normal & texgen for opposite face, used to map up-facing vheight data to down-facing data +static unsigned char stbvox_reverse_face[STBVF_count] = +{ + STBVF_w, STBVF_s, STBVF_e, STBVF_n, STBVF_d , STBVF_u , STBVF_wd, STBVF_wu, + 0, 0, 0, 0, STBVF_sw_d, STBVF_sw_u, STBVF_sd, STBVF_su, + 0, 0, 0, 0, STBVF_se_d, STBVF_se_u, STBVF_ed, STBVF_eu, + 0, 0, 0, 0, STBVF_ne_d, STBVF_ne_d, STBVF_nd, STBVF_nu +}; - if (geo <= STBVOX_GEOM_ceil_slope_north_is_bottom) { - // this is the simple case, we can just use regular block gen with special vmesh calculated with vheight - stbvox_mesh_vertex basevert; - stbvox_mesh_vertex vmesh[6][4]; - stbvox_rotate rotate = { 0,0,0,0 }; - unsigned char simple_rot = rot; - int i; - // we only need to do this for the displayed faces, but it's easier - // to just do it up front; @OPTIMIZE check if it's faster to do it - // for visible faces only - for (i=0; i < 6*4; ++i) { - int vert = stbvox_vertex_selector[0][i]; - vert = stbvox_rotate_vertex[vert][rot]; - vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] - + stbvox_geometry_vheight[geo][vert]; - } - basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); - if (mm->input.selector) { - mesh = mm->input.selector[v_off]; - } +// render non-planar quads by splitting into two triangles, rendering each as a degenerate quad +static void stbvox_make_12_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) +{ + stbvox_mesh_vertex v[4]; - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { - mm->full = 1; - return; - } + unsigned char normal1 = stbvox_face_up_normal_012[ht[2]][ht[1]][ht[0]]; + unsigned char normal2 = stbvox_face_up_normal_123[ht[3]][ht[2]][ht[1]]; - if (geo >= STBVOX_GEOM_floor_slope_north_is_top) { - if (visible_faces & (1 << STBVOX_FACE_up)) { - int normal = geo == STBVOX_GEOM_floor_slope_north_is_top ? stbvox_floor_slope_for_rot[simple_rot] : STBVOX_FACE_up; - rotate.facerot = simple_rot; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, normal); - } - if (visible_faces & (1 << STBVOX_FACE_down)) { - int normal = geo == STBVOX_GEOM_ceil_slope_north_is_bottom ? stbvox_ceil_slope_for_rot[simple_rot] : STBVOX_FACE_down; - rotate.facerot = (-rotate.facerot) & 3; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, normal); - } - } else { - if (visible_faces & (1 << STBVOX_FACE_up)) { - rotate.facerot = simple_rot; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); - } - if (visible_faces & (1 << STBVOX_FACE_down)) { - rotate.facerot = (-rotate.facerot) & 3; - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); - } - } + if (face == STBVOX_FACE_down) { + normal1 = stbvox_reverse_face[normal1]; + normal2 = stbvox_reverse_face[normal2]; + } - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rotate.block = (val >> 0) & 3; - rotate.overlay = (val >> 2) & 3; - //rotate.tex2 = (val >> 4) & 3; - rotate.ecolor = (val >> 6) & 3; - } else { - rotate.block = rotate.overlay = rotate.ecolor = simple_rot; - } + // the floor side face_coord is stored in order NW,NE,SE,SW, but ht[] is stored SW,SE,NW,NE + v[0] = face_coord[2]; + v[1] = face_coord[3]; + v[2] = face_coord[0]; + v[3] = face_coord[2]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); + v[1] = face_coord[0]; + v[2] = face_coord[1]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); +} - rotate.facerot = 0; +static void stbvox_make_03_split_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int face, int v_off, stbvox_pos pos, stbvox_mesh_vertex vertbase, stbvox_mesh_vertex *face_coord, unsigned char mesh, unsigned char *ht) +{ + stbvox_mesh_vertex v[4]; - if (visible_faces & (1 << STBVOX_FACE_north)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); - if (visible_faces & (1 << STBVOX_FACE_south)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); - if (visible_faces & (1 << STBVOX_FACE_east)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); - if (visible_faces & (1 << STBVOX_FACE_west)) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + unsigned char normal1 = stbvox_face_up_normal_013[ht[3]][ht[1]][ht[0]]; + unsigned char normal2 = stbvox_face_up_normal_023[ht[3]][ht[2]][ht[0]]; + + if (face == STBVOX_FACE_down) { + normal1 = stbvox_reverse_face[normal1]; + normal2 = stbvox_reverse_face[normal2]; } - if (geo >= STBVOX_GEOM_floor_vheight_03) { - // this case can also be generated with regular block gen with special vmesh, - // except: - // if we want to generate middle diagonal for 'weird' blocks - // it's more complicated to detect neighbor matchups - stbvox_mesh_vertex vmesh[6][4]; - stbvox_mesh_vertex cube[8]; - stbvox_mesh_vertex basevert; - stbvox_rotate rotate = { 0,0,0,0 }; - unsigned char simple_rot = rot; - unsigned char ht[4]; - int extreme; - // extract the heights - if (mm->input.vheight) { - unsigned char v = mm->input.vheight[v_off]; - ht[0] = (v >> 0) & 3; - ht[1] = (v >> 2) & 3; - ht[2] = (v >> 4) & 3; - ht[3] = (v >> 6) & 3; - } else if (mm->input.block_vheight) { - unsigned char v = mm->input.block_vheight[bt]; - unsigned char raw[4]; - int i; + v[0] = face_coord[1]; + v[1] = face_coord[2]; + v[2] = face_coord[3]; + v[3] = face_coord[1]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal1); + v[1] = face_coord[3]; + v[2] = face_coord[0]; + stbvox_make_mesh_for_face(mm, rot, face, v_off, pos, vertbase, v, mesh, normal2); // this one is correct! +} +#endif - raw[0] = (v >> 0) & 3; - raw[1] = (v >> 2) & 3; - raw[2] = (v >> 4) & 3; - raw[3] = (v >> 6) & 3; +#ifndef STBVOX_CONFIG_PRECISION_Z +#define STBVOX_CONFIG_PRECISION_Z 1 +#endif - for (i=0; i < 4; ++i) - ht[i] = raw[stbvox_rotate_vertex[i][rot]]; - } else { - assert(0); - } +// simple case for mesh generation: we have only solid and empty blocks +static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off, stbvox_mesh_vertex *vmesh) +{ + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; - // flag whether any sides go off the top of the block, which means - // our visible_faces test was wrong - extreme = (ht[0] == 3 || ht[1] == 3 || ht[2] == 3 || ht[3] == 3); + unsigned char *blockptr = &mm->input.blocktype[v_off]; + stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); - if (geo >= STBVOX_GEOM_ceil_vheight_03) { - cube[0] = stbvox_vertex_encode(0,0,ht[0],0,0); - cube[1] = stbvox_vertex_encode(0,0,ht[1],0,0); - cube[2] = stbvox_vertex_encode(0,0,ht[2],0,0); - cube[3] = stbvox_vertex_encode(0,0,ht[3],0,0); - cube[4] = stbvox_vertex_encode(0,0,2,0,0); - cube[5] = stbvox_vertex_encode(0,0,2,0,0); - cube[6] = stbvox_vertex_encode(0,0,2,0,0); - cube[7] = stbvox_vertex_encode(0,0,2,0,0); - } else { - cube[0] = stbvox_vertex_encode(0,0,0,0,0); - cube[1] = stbvox_vertex_encode(0,0,0,0,0); - cube[2] = stbvox_vertex_encode(0,0,0,0,0); - cube[3] = stbvox_vertex_encode(0,0,0,0,0); - cube[4] = stbvox_vertex_encode(0,0,ht[0],0,0); - cube[5] = stbvox_vertex_encode(0,0,ht[1],0,0); - cube[6] = stbvox_vertex_encode(0,0,ht[2],0,0); - cube[7] = stbvox_vertex_encode(0,0,ht[3],0,0); - } - if (!mm->input.vheight && mm->input.block_vheight) { - // @TODO: support block vheight here, I've forgotten what needs to be done specially - } + stbvox_rotate rot = { 0,0,0,0 }; + unsigned char simple_rot = 0; - // build vertex mesh - { - int i; - for (i=0; i < 6*4; ++i) { - int vert = stbvox_vertex_selector[0][i]; - vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] - + cube[vert]; - } - } + unsigned char mesh = mm->default_mesh; - basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { - mm->full = 1; - return; - } + if (mm->input.selector) + mesh = mm->input.selector[v_off]; - // @TODO generate split faces - if (visible_faces & (1 << STBVOX_FACE_up)) { - if (geo >= STBVOX_GEOM_ceil_vheight_03) - // flat - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); - else { - #ifndef STBVOX_OPTIMIZED_VHEIGHT - // check if it's non-planar - if (cube[5] + cube[6] != cube[4] + cube[7]) { - // not planar, split along diagonal and make degenerate quads - if (geo == STBVOX_GEOM_floor_vheight_03) - stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); - else - stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); - } else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]); - #else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]); - #endif - } - } - if (visible_faces & (1 << STBVOX_FACE_down)) { - if (geo < STBVOX_GEOM_ceil_vheight_03) - // flat - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); - else { - #ifndef STBVOX_OPTIMIZED_VHEIGHT - // check if it's non-planar - if (cube[1] + cube[2] != cube[0] + cube[3]) { - // not planar, split along diagonal and make degenerate quads - if (geo == STBVOX_GEOM_ceil_vheight_03) - stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); - else - stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); - } else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]]); - #else - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]]); - #endif - } - } + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rotate.block = (val >> 0) & 3; - rotate.overlay = (val >> 2) & 3; - //rotate.tex2 = (val >> 4) & 3; - rotate.ecolor = (val >> 6) & 3; - } else if (mm->input.selector) { - rotate.block = rotate.overlay = rotate.ecolor = simple_rot; - } + #ifdef STBVOX_ROTATION_IN_LIGHTING + simple_rot = mm->input.lighting[v_off] & 3; + #endif - if ((visible_faces & (1 << STBVOX_FACE_north)) || (extreme && (ht[2] == 3 || ht[3] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); - if ((visible_faces & (1 << STBVOX_FACE_south)) || (extreme && (ht[0] == 3 || ht[1] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); - if ((visible_faces & (1 << STBVOX_FACE_east)) || (extreme && (ht[1] == 3 || ht[3] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); - if ((visible_faces & (1 << STBVOX_FACE_west)) || (extreme && (ht[0] == 3 || ht[2] == 3))) - stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + if (blockptr[ 1]==0) { + rot.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_up , v_off, pos, basevert, vmesh+4*STBVOX_FACE_up, mesh, STBVOX_FACE_up); + } + if (blockptr[-1]==0) { + rot.facerot = (-simple_rot) & 3; + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_down, v_off, pos, basevert, vmesh+4*STBVOX_FACE_down, mesh, STBVOX_FACE_down); } - if (geo == STBVOX_GEOM_crossed_pair) { - // this can be generated with a special vmesh - stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); - unsigned char simple_rot=0; - stbvox_rotate rot = { 0,0,0,0 }; - unsigned char mesh = mm->default_mesh; - if (mm->input.selector) { - mesh = mm->input.selector[v_off]; - simple_rot = mesh >> 4; - mesh &= 15; - } - - // check if we're going off the end - if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*4 > mm->output_end[mesh][0]) { - mm->full = 1; - return; - } + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rot.block = (val >> 0) & 3; + rot.overlay = (val >> 2) & 3; + //rot.tex2 = (val >> 4) & 3; + rot.ecolor = (val >> 6) & 3; + } else { + rot.block = rot.overlay = rot.ecolor = simple_rot; + } + rot.facerot = 0; - if (mm->input.rotate) { - unsigned char val = mm->input.rotate[v_off]; - rot.block = (val >> 0) & 3; - rot.overlay = (val >> 2) & 3; - //rot.tex2 = (val >> 4) & 3; - rot.ecolor = (val >> 6) & 3; - } else if (mm->input.selector) { - rot.block = rot.overlay = rot.ecolor = simple_rot; - } - rot.facerot = 0; + if (blockptr[ ns_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, vmesh+4*STBVOX_FACE_north, mesh, STBVOX_FACE_north); + if (blockptr[-ns_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, vmesh+4*STBVOX_FACE_south, mesh, STBVOX_FACE_south); + if (blockptr[ ew_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, vmesh+4*STBVOX_FACE_east, mesh, STBVOX_FACE_east); + if (blockptr[-ew_off]==0) + stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, vmesh+4*STBVOX_FACE_west, mesh, STBVOX_FACE_west); +} - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_north, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_north], mesh, STBVF_ne_u_cross); - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_south, v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_south], mesh, STBVF_sw_u_cross); - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_east , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_east ], mesh, STBVF_se_u_cross); - stbvox_make_mesh_for_face(mm, rot, STBVOX_FACE_west , v_off, pos, basevert, stbvox_vmesh_crossed_pair[STBVOX_FACE_west ], mesh, STBVF_nw_u_cross); - } +// void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) +// +// complex case for mesh generation: we have lots of different +// block types, and we don't want to generate faces of blocks +// if they're hidden by neighbors. +// +// we use lots of tables to determine this: we have a table +// which tells us what face type is generated for each type of +// geometry, and then a table that tells us whether that type +// is hidden by a neighbor. - // @TODO - // STBVOX_GEOM_floor_slope_north_is_top_as_wall, - // STBVOX_GEOM_ceil_slope_north_is_bottom_as_wall, -} -static void stbvox_make_mesh_for_column(stbvox_mesh_maker *mm, int x, int y, int z0) +static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) { - stbvox_pos pos = { x,y,0 }; - int v_off = x * mm->x_stride_in_bytes + y * mm->y_stride_in_bytes; int ns_off = mm->y_stride_in_bytes; int ew_off = mm->x_stride_in_bytes; + int visible_faces, visible_base; + unsigned char mesh; + + // first gather the geometry info for this block and all neighbors + + unsigned char bt, nbt[6]; + unsigned char geo, ngeo[6]; + unsigned char rot, nrot[6]; + + bt = mm->input.blocktype[v_off]; + nbt[0] = mm->input.blocktype[v_off + ew_off]; + nbt[1] = mm->input.blocktype[v_off + ns_off]; + nbt[2] = mm->input.blocktype[v_off - ew_off]; + nbt[3] = mm->input.blocktype[v_off - ns_off]; + nbt[4] = mm->input.blocktype[v_off + 1]; + nbt[5] = mm->input.blocktype[v_off - 1]; if (mm->input.geometry) { - unsigned char *bt = mm->input.blocktype + v_off; - unsigned char *geo = mm->input.geometry + v_off; - int z; - for (z=z0; z < mm->z1; ++z) { - if (bt[z] && ( !bt[z+ns_off] || !STBVOX_GET_GEO(geo[z+ns_off]) || !bt[z-ns_off] || !STBVOX_GET_GEO(geo[z-ns_off]) - || !bt[z+ew_off] || !STBVOX_GET_GEO(geo[z+ew_off]) || !bt[z-ew_off] || !STBVOX_GET_GEO(geo[z-ew_off]))) - { // TODO check up and down - pos.z = z; - stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); - if (mm->full) { - mm->cur_z = z; - return; - } - } - } - } else if (mm->input.block_geometry) { - int z; - unsigned char *bt = mm->input.blocktype + v_off; - unsigned char *geo = mm->input.block_geometry; - for (z=z0; z < mm->z1; ++z) { - if (bt[z] && ( geo[bt[z+ns_off]] != STBVOX_GEOM_solid - || geo[bt[z-ns_off]] != STBVOX_GEOM_solid - || geo[bt[z+ew_off]] != STBVOX_GEOM_solid - || geo[bt[z-ew_off]] != STBVOX_GEOM_solid - || geo[bt[z-1]] != STBVOX_GEOM_solid - || geo[bt[z+1]] != STBVOX_GEOM_solid)) - { - pos.z = z; - stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); - if (mm->full) { - mm->cur_z = z; - return; - } - } + int i; + geo = mm->input.geometry[v_off]; + ngeo[0] = mm->input.geometry[v_off + ew_off]; + ngeo[1] = mm->input.geometry[v_off + ns_off]; + ngeo[2] = mm->input.geometry[v_off - ew_off]; + ngeo[3] = mm->input.geometry[v_off - ns_off]; + ngeo[4] = mm->input.geometry[v_off + 1]; + ngeo[5] = mm->input.geometry[v_off - 1]; + + #ifndef STBVOX_ROTATION_IN_LIGHTING + rot = (geo >> 4) & 3; + geo &= 15; + for (i=0; i < 6; ++i) { + nrot[i] = (ngeo[i] >> 4) & 3; + ngeo[i] &= 15; } - } else { - unsigned char *bt = mm->input.blocktype + v_off; - int z; - #if STBVOX_CONFIG_PRECISION_Z == 1 - stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_half_z[0]; - #else - stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_normal[0]; #endif - for (z=z0; z < mm->z1; ++z) { - // if it's solid and at least one neighbor isn't solid - if (bt[z] && (!bt[z+ns_off] || !bt[z-ns_off] || !bt[z+ew_off] || !bt[z-ew_off] || !bt[z-1] || !bt[z+1])) { - pos.z = z; - stbvox_make_mesh_for_block(mm, pos, v_off+z, vmesh); - if (mm->full) { - mm->cur_z = z; - return; - } + STBVOX_NOTUSED(i); + } else { + int i; + assert(mm->input.block_geometry); + geo = mm->input.block_geometry[bt]; + for (i=0; i < 6; ++i) + ngeo[i] = mm->input.block_geometry[nbt[i]]; + if (mm->input.selector) { + #ifndef STBVOX_ROTATION_IN_LIGHTING + rot = (mm->input.selector[v_off ] >> 4) & 3; + nrot[0] = (mm->input.selector[v_off + ew_off] >> 4) & 3; + nrot[1] = (mm->input.selector[v_off + ns_off] >> 4) & 3; + nrot[2] = (mm->input.selector[v_off - ew_off] >> 4) & 3; + nrot[3] = (mm->input.selector[v_off - ns_off] >> 4) & 3; + nrot[4] = (mm->input.selector[v_off + 1] >> 4) & 3; + nrot[5] = (mm->input.selector[v_off - 1] >> 4) & 3; + #endif + } else { + #ifndef STBVOX_ROTATION_IN_LIGHTING + rot = (geo>>4)&3; + geo &= 15; + for (i=0; i < 6; ++i) { + nrot[i] = (ngeo[i]>>4)&3; + ngeo[i] &= 15; } + #endif } } -} -static void stbvox_bring_up_to_date(stbvox_mesh_maker *mm) -{ - if (mm->config_dirty) { - int i; - #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE - mm->num_mesh_slots = 1; - for (i=0; i < STBVOX_MAX_MESHES; ++i) { - mm->output_size[i][0] = 32; - mm->output_step[i][0] = 8; - } - #else - mm->num_mesh_slots = 2; - for (i=0; i < STBVOX_MAX_MESHES; ++i) { - mm->output_size[i][0] = 16; - mm->output_step[i][0] = 4; - mm->output_size[i][1] = 4; - mm->output_step[i][1] = 4; - } - #endif + #ifdef STBVOX_ROTATION_IN_LIGHTING + rot = mm->input.lighting[v_off] & 3; + nrot[0] = (mm->input.lighting[v_off + ew_off]) & 3; + nrot[1] = (mm->input.lighting[v_off + ns_off]) & 3; + nrot[2] = (mm->input.lighting[v_off - ew_off]) & 3; + nrot[3] = (mm->input.lighting[v_off - ns_off]) & 3; + nrot[4] = (mm->input.lighting[v_off + 1]) & 3; + nrot[5] = (mm->input.lighting[v_off - 1]) & 3; + #endif - mm->config_dirty = 0; + if (geo == STBVOX_GEOM_transp) { + // transparency has a special rule: if the blocktype is the same, + // and the faces are compatible, then can hide them; otherwise, + // force them on + // Note that this means we don't support any transparentshapes other + // than solid blocks, since detecting them is too complicated. If + // you wanted to do something like minecraft water, you probably + // should just do that with a separate renderer anyway. (We don't + // support transparency sorting so you need to use alpha test + // anyway) + int i; + for (i=0; i < 6; ++i) + if (nbt[i] != bt) { + nbt[i] = 0; + ngeo[i] = STBVOX_GEOM_empty; + } else + ngeo[i] = STBVOX_GEOM_solid; + geo = STBVOX_GEOM_solid; } -} -int stbvox_make_mesh(stbvox_mesh_maker *mm) -{ - int x,y; - stbvox_bring_up_to_date(mm); - mm->full = 0; - if (mm->cur_x > mm->x0 || mm->cur_y > mm->y0 || mm->cur_z > mm->z0) { - stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->cur_z); - if (mm->full) - return 0; - ++mm->cur_y; - while (mm->cur_y < mm->y1 && !mm->full) { - stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->z0); - if (mm->full) - return 0; - ++mm->cur_y; - } - ++mm->cur_x; + // now compute the face visibility + visible_base = stbvox_hasface[geo][rot]; + // @TODO: assert(visible_base != 0); // we should have early-outted earlier in this case + visible_faces = 0; + + // now, for every face that might be visible, check if neighbor hides it + if (visible_base & (1 << STBVOX_FACE_east)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_east+ rot )&3]; + int ntype = stbvox_facetype[ngeo[0]][(STBVOX_FACE_west+nrot[0])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_east)) & (1 << STBVOX_FACE_east); } - for (x=mm->cur_x; x < mm->x1; ++x) { - for (y=mm->y0; y < mm->y1; ++y) { - stbvox_make_mesh_for_column(mm, x, y, mm->z0); - if (mm->full) { - mm->cur_x = x; - mm->cur_y = y; - return 0; - } - } + if (visible_base & (1 << STBVOX_FACE_north)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_north+ rot )&3]; + int ntype = stbvox_facetype[ngeo[1]][(STBVOX_FACE_south+nrot[1])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_north)) & (1 << STBVOX_FACE_north); + } + if (visible_base & (1 << STBVOX_FACE_west)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_west+ rot )&3]; + int ntype = stbvox_facetype[ngeo[2]][(STBVOX_FACE_east+nrot[2])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_west)) & (1 << STBVOX_FACE_west); + } + if (visible_base & (1 << STBVOX_FACE_south)) { + int type = stbvox_facetype[ geo ][(STBVOX_FACE_south+ rot )&3]; + int ntype = stbvox_facetype[ngeo[3]][(STBVOX_FACE_north+nrot[3])&3]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_south)) & (1 << STBVOX_FACE_south); + } + if (visible_base & (1 << STBVOX_FACE_up)) { + int type = stbvox_facetype[ geo ][STBVOX_FACE_up]; + int ntype = stbvox_facetype[ngeo[4]][STBVOX_FACE_down]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_up)) & (1 << STBVOX_FACE_up); + } + if (visible_base & (1 << STBVOX_FACE_down)) { + int type = stbvox_facetype[ geo ][STBVOX_FACE_down]; + int ntype = stbvox_facetype[ngeo[5]][STBVOX_FACE_up]; + visible_faces |= ((stbvox_face_visible[type]) >> (ntype + 5 - STBVOX_FACE_down)) & (1 << STBVOX_FACE_down); } - return 1; -} -void stbvox_init_mesh_maker(stbvox_mesh_maker *mm) -{ - memset(mm, 0, sizeof(*mm)); - stbvox_build_default_palette(); + if (geo == STBVOX_GEOM_force) + geo = STBVOX_GEOM_solid; - mm->config_dirty = 1; - mm->default_mesh = 0; -} + assert((geo == STBVOX_GEOM_crossed_pair) ? (visible_faces == 15) : 1); -int stbvox_get_buffer_count(stbvox_mesh_maker *mm) -{ - stbvox_bring_up_to_date(mm); - return mm->num_mesh_slots; -} + // now we finally know for sure which faces are getting generated + if (visible_faces == 0) + return; -int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int n) -{ - return mm->output_size[0][n]; -} + mesh = mm->default_mesh; + if (mm->input.selector) + mesh = mm->input.selector[v_off]; -void stbvox_reset_buffers(stbvox_mesh_maker *mm) -{ - int i; - for (i=0; i < STBVOX_MAX_MESHES*STBVOX_MAX_MESH_SLOTS; ++i) { - mm->output_cur[0][i] = 0; - mm->output_buffer[0][i] = 0; - } -} + if (geo <= STBVOX_GEOM_ceil_slope_north_is_bottom) { + // this is the simple case, we can just use regular block gen with special vmesh calculated with vheight + stbvox_mesh_vertex basevert; + stbvox_mesh_vertex vmesh[6][4]; + stbvox_rotate rotate = { 0,0,0,0 }; + unsigned char simple_rot = rot; + int i; + // we only need to do this for the displayed faces, but it's easier + // to just do it up front; @OPTIMIZE check if it's faster to do it + // for visible faces only + for (i=0; i < 6*4; ++i) { + int vert = stbvox_vertex_selector[0][i]; + vert = stbvox_rotate_vertex[vert][rot]; + vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] + + stbvox_geometry_vheight[geo][vert]; + } -void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len) -{ - int i; - stbvox_bring_up_to_date(mm); - mm->output_buffer[mesh][slot] = (char *) buffer; - mm->output_cur [mesh][slot] = (char *) buffer; - mm->output_len [mesh][slot] = len; - mm->output_end [mesh][slot] = (char *) buffer + len; - for (i=0; i < STBVOX_MAX_MESH_SLOTS; ++i) { - if (mm->output_buffer[mesh][i]) { - assert(mm->output_len[mesh][i] / mm->output_size[mesh][i] == mm->output_len[mesh][slot] / mm->output_size[mesh][slot]); + basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); + if (mm->input.selector) { + mesh = mm->input.selector[v_off]; } - } -} -void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh) -{ - mm->default_mesh = mesh; -} + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } -int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh) -{ - return (mm->output_cur[mesh][0] - mm->output_buffer[mesh][0]) / mm->output_size[mesh][0]; -} + if (geo >= STBVOX_GEOM_floor_slope_north_is_top) { + if (visible_faces & (1 << STBVOX_FACE_up)) { + int normal = geo == STBVOX_GEOM_floor_slope_north_is_top ? stbvox_floor_slope_for_rot[simple_rot] : STBVOX_FACE_up; + rotate.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, normal); + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + int normal = geo == STBVOX_GEOM_ceil_slope_north_is_bottom ? stbvox_ceil_slope_for_rot[simple_rot] : STBVOX_FACE_down; + rotate.facerot = (-rotate.facerot) & 3; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, normal); + } + } else { + if (visible_faces & (1 << STBVOX_FACE_up)) { + rotate.facerot = simple_rot; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + rotate.facerot = (-rotate.facerot) & 3; + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); + } + } -stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm) -{ - return &mm->input; -} + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rotate.block = (val >> 0) & 3; + rotate.overlay = (val >> 2) & 3; + //rotate.tex2 = (val >> 4) & 3; + rotate.ecolor = (val >> 6) & 3; + } else { + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; + } -void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1) -{ - mm->x0 = x0; - mm->y0 = y0; - mm->z0 = z0; + rotate.facerot = 0; - mm->x1 = x1; - mm->y1 = y1; - mm->z1 = z1; - - mm->cur_x = x0; - mm->cur_y = y0; - mm->cur_z = z0; + if (visible_faces & (1 << STBVOX_FACE_north)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); + if (visible_faces & (1 << STBVOX_FACE_south)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); + if (visible_faces & (1 << STBVOX_FACE_east)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); + if (visible_faces & (1 << STBVOX_FACE_west)) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + } + if (geo >= STBVOX_GEOM_floor_vheight_03) { + // this case can also be generated with regular block gen with special vmesh, + // except: + // if we want to generate middle diagonal for 'weird' blocks + // it's more complicated to detect neighbor matchups + stbvox_mesh_vertex vmesh[6][4]; + stbvox_mesh_vertex cube[8]; + stbvox_mesh_vertex basevert; + stbvox_rotate rotate = { 0,0,0,0 }; + unsigned char simple_rot = rot; + unsigned char ht[4]; + int extreme; - // @TODO validate that this range is representable in this mode -} + // extract the heights + if (mm->input.vheight) { + unsigned char v = mm->input.vheight[v_off]; + ht[0] = (v >> 0) & 3; + ht[1] = (v >> 2) & 3; + ht[2] = (v >> 4) & 3; + ht[3] = (v >> 6) & 3; + } else if (mm->input.block_vheight) { + unsigned char v = mm->input.block_vheight[bt]; + unsigned char raw[4]; + int i; -void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]) -{ - // scale - transform[0][0] = 1.0; - transform[0][1] = 1.0; - #if STBVOX_CONFIG_PRECISION_Z==1 - transform[0][2] = 0.5f; - #else - transform[0][2] = 1.0f; - #endif - // translation - transform[1][0] = (float) (mm->pos_x); - transform[1][1] = (float) (mm->pos_y); - transform[1][2] = (float) (mm->pos_z); - // texture coordinate projection translation - transform[2][0] = (float) (mm->pos_x & 255); // @TODO depends on max texture scale - transform[2][1] = (float) (mm->pos_y & 255); - transform[2][2] = (float) (mm->pos_z & 255); -} + raw[0] = (v >> 0) & 3; + raw[1] = (v >> 2) & 3; + raw[2] = (v >> 4) & 3; + raw[3] = (v >> 6) & 3; -void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]) -{ - bounds[0][0] = (float) (mm->pos_x + mm->x0); - bounds[0][1] = (float) (mm->pos_y + mm->y0); - bounds[0][2] = (float) (mm->pos_z + mm->z0); - bounds[1][0] = (float) (mm->pos_x + mm->x1); - bounds[1][1] = (float) (mm->pos_y + mm->y1); - bounds[1][2] = (float) (mm->pos_z + mm->z1); -} + for (i=0; i < 4; ++i) + ht[i] = raw[stbvox_rotate_vertex[i][rot]]; + } else { + assert(0); + } -void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z) -{ - mm->pos_x = x; - mm->pos_y = y; - mm->pos_z = z; -} + // flag whether any sides go off the top of the block, which means + // our visible_faces test was wrong + extreme = (ht[0] == 3 || ht[1] == 3 || ht[2] == 3 || ht[3] == 3); -void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_bytes, int y_stride_in_bytes) -{ - int f,v; - mm->x_stride_in_bytes = x_stride_in_bytes; - mm->y_stride_in_bytes = y_stride_in_bytes; - for (f=0; f < 6; ++f) { - for (v=0; v < 4; ++v) { - mm->cube_vertex_offset[f][v] = stbvox_vertex_vector[f][v][0] * mm->x_stride_in_bytes - + stbvox_vertex_vector[f][v][1] * mm->y_stride_in_bytes - + stbvox_vertex_vector[f][v][2] ; - mm->vertex_gather_offset[f][v] = (stbvox_vertex_vector[f][v][0]-1) * mm->x_stride_in_bytes - + (stbvox_vertex_vector[f][v][1]-1) * mm->y_stride_in_bytes - + (stbvox_vertex_vector[f][v][2]-1) ; + if (geo >= STBVOX_GEOM_ceil_vheight_03) { + cube[0] = stbvox_vertex_encode(0,0,ht[0],0,0); + cube[1] = stbvox_vertex_encode(0,0,ht[1],0,0); + cube[2] = stbvox_vertex_encode(0,0,ht[2],0,0); + cube[3] = stbvox_vertex_encode(0,0,ht[3],0,0); + cube[4] = stbvox_vertex_encode(0,0,2,0,0); + cube[5] = stbvox_vertex_encode(0,0,2,0,0); + cube[6] = stbvox_vertex_encode(0,0,2,0,0); + cube[7] = stbvox_vertex_encode(0,0,2,0,0); + } else { + cube[0] = stbvox_vertex_encode(0,0,0,0,0); + cube[1] = stbvox_vertex_encode(0,0,0,0,0); + cube[2] = stbvox_vertex_encode(0,0,0,0,0); + cube[3] = stbvox_vertex_encode(0,0,0,0,0); + cube[4] = stbvox_vertex_encode(0,0,ht[0],0,0); + cube[5] = stbvox_vertex_encode(0,0,ht[1],0,0); + cube[6] = stbvox_vertex_encode(0,0,ht[2],0,0); + cube[7] = stbvox_vertex_encode(0,0,ht[3],0,0); + } + if (!mm->input.vheight && mm->input.block_vheight) { + // @TODO: support block vheight here, I've forgotten what needs to be done specially } - } -} -///////////////////////////////////////////////////////////////////////////// -// -// tables -// + // build vertex mesh + { + int i; + for (i=0; i < 6*4; ++i) { + int vert = stbvox_vertex_selector[0][i]; + vmesh[0][i] = stbvox_vmesh_pre_vheight[0][i] + + cube[vert]; + } + } -// get opposite-facing normal & texgen for opposite face, used to map up-facing vheight data to down-facing data -static unsigned char stbvox_reverse_face[STBVF_count] = -{ - STBVF_w, STBVF_s, STBVF_e, STBVF_n, STBVF_d , STBVF_u , STBVF_wd, STBVF_wu, - 0, 0, 0, 0, STBVF_sw_d, STBVF_sw_u, STBVF_sd, STBVF_su, - 0, 0, 0, 0, STBVF_se_d, STBVF_se_u, STBVF_ed, STBVF_eu, - 0, 0, 0, 0, STBVF_ne_d, STBVF_ne_d, STBVF_nd, STBVF_nu -}; + basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z, 0,0); + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*6 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } -static float stbvox_default_texgen[2][32][3] = -{ - { { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, { 0, 0,-1 }, - { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, { 0, 0,-1 }, - { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, { 0, 0,-1 }, - { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, { 0, 0,-1 }, + // @TODO generate split faces + if (visible_faces & (1 << STBVOX_FACE_up)) { + if (geo >= STBVOX_GEOM_ceil_vheight_03) + // flat + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, STBVOX_FACE_up); + else { + #ifndef STBVOX_OPTIMIZED_VHEIGHT + // check if it's non-planar + if (cube[5] + cube[6] != cube[4] + cube[7]) { + // not planar, split along diagonal and make degenerate quads + if (geo == STBVOX_GEOM_floor_vheight_03) + stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); + else + stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_up, v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, ht); + } else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]); + #else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_up , v_off, pos, basevert, vmesh[STBVOX_FACE_up], mesh, stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]); + #endif + } + } + if (visible_faces & (1 << STBVOX_FACE_down)) { + if (geo < STBVOX_GEOM_ceil_vheight_03) + // flat + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, STBVOX_FACE_down); + else { + #ifndef STBVOX_OPTIMIZED_VHEIGHT + // check if it's non-planar + if (cube[1] + cube[2] != cube[0] + cube[3]) { + // not planar, split along diagonal and make degenerate quads + if (geo == STBVOX_GEOM_ceil_vheight_03) + stbvox_make_03_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); + else + stbvox_make_12_split_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, ht); + } else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_planar_face_up_normal[ht[2]][ht[1]][ht[0]]]); + #else + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_down, v_off, pos, basevert, vmesh[STBVOX_FACE_down], mesh, stbvox_reverse_face[stbvox_optimized_face_up_normal[ht[3]][ht[2]][ht[1]][ht[0]]]); + #endif + } + } - { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, - { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, - { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, - { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, - }, - { { 0, 0,-1 }, { 0, 1,0 }, { 0, 0, 1 }, { 0,-1,0 }, - { 0, 0,-1 }, { -1, 0,0 }, { 0, 0, 1 }, { 1, 0,0 }, - { 0, 0,-1 }, { 0,-1,0 }, { 0, 0, 1 }, { 0, 1,0 }, - { 0, 0,-1 }, { 1, 0,0 }, { 0, 0, 1 }, { -1, 0,0 }, + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rotate.block = (val >> 0) & 3; + rotate.overlay = (val >> 2) & 3; + //rotate.tex2 = (val >> 4) & 3; + rotate.ecolor = (val >> 6) & 3; + } else if (mm->input.selector) { + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; + } - { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, - { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, - { 0,-1, 0 }, { 1, 0,0 }, { 0, 1, 0 }, { -1, 0,0 }, - { 0, 1, 0 }, { -1, 0,0 }, { 0,-1, 0 }, { 1, 0,0 }, - }, -}; + if ((visible_faces & (1 << STBVOX_FACE_north)) || (extreme && (ht[2] == 3 || ht[3] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_north, v_off, pos, basevert, vmesh[STBVOX_FACE_north], mesh, STBVOX_FACE_north); + if ((visible_faces & (1 << STBVOX_FACE_south)) || (extreme && (ht[0] == 3 || ht[1] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_south, v_off, pos, basevert, vmesh[STBVOX_FACE_south], mesh, STBVOX_FACE_south); + if ((visible_faces & (1 << STBVOX_FACE_east)) || (extreme && (ht[1] == 3 || ht[3] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_east , v_off, pos, basevert, vmesh[STBVOX_FACE_east ], mesh, STBVOX_FACE_east); + if ((visible_faces & (1 << STBVOX_FACE_west)) || (extreme && (ht[0] == 3 || ht[2] == 3))) + stbvox_make_mesh_for_face(mm, rotate, STBVOX_FACE_west , v_off, pos, basevert, vmesh[STBVOX_FACE_west ], mesh, STBVOX_FACE_west); + } -#define STBVOX_RSQRT2 0.7071067811865f -#define STBVOX_RSQRT3 0.5773502691896f + if (geo == STBVOX_GEOM_crossed_pair) { + // this can be generated with a special vmesh + stbvox_mesh_vertex basevert = stbvox_vertex_encode(pos.x, pos.y, pos.z << STBVOX_CONFIG_PRECISION_Z , 0,0); + unsigned char simple_rot=0; + stbvox_rotate rot = { 0,0,0,0 }; + unsigned char mesh = mm->default_mesh; + if (mm->input.selector) { + mesh = mm->input.selector[v_off]; + simple_rot = mesh >> 4; + mesh &= 15; + } -static float stbvox_default_normals[32][3] = -{ - { 1,0,0 }, // east - { 0,1,0 }, // north - { -1,0,0 }, // west - { 0,-1,0 }, // south - { 0,0,1 }, // up - { 0,0,-1 }, // down - { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up - { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down + // check if we're going off the end + if (mm->output_cur[mesh][0] + mm->output_size[mesh][0]*4 > mm->output_end[mesh][0]) { + mm->full = 1; + return; + } - { STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // east & up - { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up - { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up - { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up - { STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // ne & up - { STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // ne & down - { 0, STBVOX_RSQRT2, STBVOX_RSQRT2 }, // north & up - { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down - - { STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // east & down - { 0, STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // north & down - { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down - { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down - { -STBVOX_RSQRT3, STBVOX_RSQRT3, STBVOX_RSQRT3 }, // NW & up - { -STBVOX_RSQRT3, STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // NW & down - { -STBVOX_RSQRT2,0, STBVOX_RSQRT2 }, // west & up - { -STBVOX_RSQRT2,0, -STBVOX_RSQRT2 }, // west & down - - { STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NE & up crossed - { -STBVOX_RSQRT3, STBVOX_RSQRT3,STBVOX_RSQRT3 }, // NW & up crossed - { -STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SW & up crossed - { STBVOX_RSQRT3,-STBVOX_RSQRT3,STBVOX_RSQRT3 }, // SE & up crossed - { -STBVOX_RSQRT3,-STBVOX_RSQRT3, STBVOX_RSQRT3 }, // SW & up - { -STBVOX_RSQRT3,-STBVOX_RSQRT3,-STBVOX_RSQRT3 }, // SW & up - { 0,-STBVOX_RSQRT2, STBVOX_RSQRT2 }, // south & up - { 0,-STBVOX_RSQRT2, -STBVOX_RSQRT2 }, // south & down -}; - -static float stbvox_default_texscale[128][4] = -{ - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, - {1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0},{1,1,0,0}, -}; - -static unsigned char stbvox_default_palette_compact[64][3] = -{ - { 255,255,255 }, { 238,238,238 }, { 221,221,221 }, { 204,204,204 }, - { 187,187,187 }, { 170,170,170 }, { 153,153,153 }, { 136,136,136 }, - { 119,119,119 }, { 102,102,102 }, { 85, 85, 85 }, { 68, 68, 68 }, - { 51, 51, 51 }, { 34, 34, 34 }, { 17, 17, 17 }, { 0, 0, 0 }, - { 255,240,240 }, { 255,220,220 }, { 255,160,160 }, { 255, 32, 32 }, - { 200,120,160 }, { 200, 60,150 }, { 220,100,130 }, { 255, 0,128 }, - { 240,240,255 }, { 220,220,255 }, { 160,160,255 }, { 32, 32,255 }, - { 120,160,200 }, { 60,150,200 }, { 100,130,220 }, { 0,128,255 }, - { 240,255,240 }, { 220,255,220 }, { 160,255,160 }, { 32,255, 32 }, - { 160,200,120 }, { 150,200, 60 }, { 130,220,100 }, { 128,255, 0 }, - { 255,255,240 }, { 255,255,220 }, { 220,220,180 }, { 255,255, 32 }, - { 200,160,120 }, { 200,150, 60 }, { 220,130,100 }, { 255,128, 0 }, - { 255,240,255 }, { 255,220,255 }, { 220,180,220 }, { 255, 32,255 }, - { 160,120,200 }, { 150, 60,200 }, { 130,100,220 }, { 128, 0,255 }, - { 240,255,255 }, { 220,255,255 }, { 180,220,220 }, { 32,255,255 }, - { 120,200,160 }, { 60,200,150 }, { 100,220,130 }, { 0,255,128 }, -}; - -static unsigned char stbvox_vertex_vector[6][4][3] = -{ - { { 1,0,1 }, { 1,1,1 }, { 1,1,0 }, { 1,0,0 } }, // east - { { 1,1,1 }, { 0,1,1 }, { 0,1,0 }, { 1,1,0 } }, // north - { { 0,1,1 }, { 0,0,1 }, { 0,0,0 }, { 0,1,0 } }, // west - { { 0,0,1 }, { 1,0,1 }, { 1,0,0 }, { 0,0,0 } }, // south - { { 0,1,1 }, { 1,1,1 }, { 1,0,1 }, { 0,0,1 } }, // up - { { 0,0,0 }, { 1,0,0 }, { 1,1,0 }, { 0,1,0 } }, // down -}; - -// stbvox_vertex_vector, but read coordinates as binary numbers, zyx -static unsigned char stbvox_vertex_selector[6][4] = -{ - { 5,7,3,1 }, - { 7,6,2,3 }, - { 6,4,0,2 }, - { 4,5,1,0 }, - { 6,7,5,4 }, - { 0,1,3,2 }, -}; - -static stbvox_mesh_vertex stbvox_vmesh_delta_normal[6][4] = -{ - { stbvox_vertex_encode(1,0,1,0,0) , - stbvox_vertex_encode(1,1,1,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,1,0,0) , - stbvox_vertex_encode(0,1,1,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,1,0,0) , - stbvox_vertex_encode(0,0,1,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,1,0,0) , - stbvox_vertex_encode(1,0,1,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,1,1,0,0) , - stbvox_vertex_encode(1,1,1,0,0) , - stbvox_vertex_encode(1,0,1,0,0) , - stbvox_vertex_encode(0,0,1,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - -static stbvox_mesh_vertex stbvox_vmesh_pre_vheight[6][4] = -{ - { stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - -static stbvox_mesh_vertex stbvox_vmesh_delta_half_z[6][4] = -{ - { stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - -static stbvox_mesh_vertex stbvox_vmesh_crossed_pair[6][4] = -{ - { stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(0,1,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) }, - { stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) }, - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) }, - { stbvox_vertex_encode(0,0,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,0,0,0,0) }, - // not used, so we leave it non-degenerate to make sure it doesn't get gen'd accidentally - { stbvox_vertex_encode(0,1,2,0,0) , - stbvox_vertex_encode(1,1,2,0,0) , - stbvox_vertex_encode(1,0,2,0,0) , - stbvox_vertex_encode(0,0,2,0,0) }, - { stbvox_vertex_encode(0,0,0,0,0) , - stbvox_vertex_encode(1,0,0,0,0) , - stbvox_vertex_encode(1,1,0,0,0) , - stbvox_vertex_encode(0,1,0,0,0) } -}; - - -// this is used to determine if a face is ever generated at all -static unsigned char stbvox_hasface[STBVOX_MAX_GEOM][STBVOX_NUM_ROTATION] = -{ - { 0,0,0,0 }, // empty - { 0,0,0,0 }, // knockout - { 63,63,63,63 }, // solid - { 63,63,63,63 }, // transp - { 63,63,63,63 }, // slab - { 63,63,63,63 }, // slab - { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // floor slopes - { 1|2|4|48, 8|1|2|48, 4|8|1|48, 2|4|8|48, }, // ceil slopes - { 47,47,47,47 }, // wall-projected diagonal with down face - { 31,31,31,31 }, // wall-projected diagonal with up face - { 63,63,63,63 }, // crossed-pair has special handling, but avoid early-out - { 63,63,63,63 }, // force - { 63,63,63,63 }, - { 63,63,63,63 }, - { 63,63,63,63 }, - { 63,63,63,63 }, -}; - -// this determines which face type above is visible on each side of the geometry -static unsigned char stbvox_facetype[STBVOX_GEOM_count][6] = -{ - { 0, }, // STBVOX_GEOM_empty - { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // knockout - { STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid, STBVOX_FT_solid }, // solid - { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // transp - - { STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_upper, STBVOX_FT_solid, STBVOX_FT_force }, - { STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_lower, STBVOX_FT_force, STBVOX_FT_solid }, - { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_none, STBVOX_FT_force, STBVOX_FT_solid }, - { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_none, STBVOX_FT_solid, STBVOX_FT_force }, - - { STBVOX_FT_diag_123, STBVOX_FT_solid, STBVOX_FT_diag_023, STBVOX_FT_force, STBVOX_FT_none, STBVOX_FT_solid }, - { STBVOX_FT_diag_012, STBVOX_FT_solid, STBVOX_FT_diag_013, STBVOX_FT_force, STBVOX_FT_solid, STBVOX_FT_none }, - { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, 0,0 }, // crossed pair - { STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force, STBVOX_FT_force }, // GEOM_force - - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_force, STBVOX_FT_solid }, // floor vheight, all neighbors forced - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced - { STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial,STBVOX_FT_partial, STBVOX_FT_solid, STBVOX_FT_force }, // ceil vheight, all neighbors forced -}; - -// This table indicates what normal to use for the "up" face of a sloped geom -// @TODO this could be done with math given the current arrangement of the enum, but let's not require it -static unsigned char stbvox_floor_slope_for_rot[4] = -{ - STBVF_su, - STBVF_wu, // @TODO: why is this reversed from what it should be? this is a north-is-up face, so slope should be south&up - STBVF_nu, - STBVF_eu, -}; - -static unsigned char stbvox_ceil_slope_for_rot[4] = -{ - STBVF_sd, - STBVF_ed, - STBVF_nd, - STBVF_wd, -}; + if (mm->input.rotate) { + unsigned char val = mm->input.rotate[v_off]; + rot.block = (val >> 0) & 3; + rot.overlay = (val >> 2) & 3; + //rot.tex2 = (val >> 4) & 3; + rot.ecolor = (val >> 6) & 3; + } else if (mm->input.selector) { + rot.block = rot.overlay = rot.ecolor = simple_rot; + } + rot.facerot = 0; -// this table indicates whether, for each pair of types above, a face is visible. -// each value indicates whether a given type is visible for all neighbor types -static unsigned short stbvox_face_visible[STBVOX_FT_count] = -{ - // we encode the table by listing which cases cause *obscuration*, and bitwise inverting that - // table is pre-shifted by 5 to save a shift when it's accessed - (unsigned short) ((~0x07ff )<<5), // none is completely obscured by everything - (unsigned short) ((~((1<x_stride_in_bytes + y * mm->y_stride_in_bytes; + int ns_off = mm->y_stride_in_bytes; + int ew_off = mm->x_stride_in_bytes; + if (mm->input.geometry) { + unsigned char *bt = mm->input.blocktype + v_off; + unsigned char *geo = mm->input.geometry + v_off; + int z; + for (z=z0; z < mm->z1; ++z) { + if (bt[z] && ( !bt[z+ns_off] || !STBVOX_GET_GEO(geo[z+ns_off]) || !bt[z-ns_off] || !STBVOX_GET_GEO(geo[z-ns_off]) + || !bt[z+ew_off] || !STBVOX_GET_GEO(geo[z+ew_off]) || !bt[z-ew_off] || !STBVOX_GET_GEO(geo[z-ew_off]))) + { // TODO check up and down + pos.z = z; + stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } else if (mm->input.block_geometry) { + int z; + unsigned char *bt = mm->input.blocktype + v_off; + unsigned char *geo = mm->input.block_geometry; + for (z=z0; z < mm->z1; ++z) { + if (bt[z] && ( geo[bt[z+ns_off]] != STBVOX_GEOM_solid + || geo[bt[z-ns_off]] != STBVOX_GEOM_solid + || geo[bt[z+ew_off]] != STBVOX_GEOM_solid + || geo[bt[z-ew_off]] != STBVOX_GEOM_solid + || geo[bt[z-1]] != STBVOX_GEOM_solid + || geo[bt[z+1]] != STBVOX_GEOM_solid)) + { + pos.z = z; + stbvox_make_mesh_for_block_with_geo(mm, pos, v_off+z); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } else { + unsigned char *bt = mm->input.blocktype + v_off; + int z; + #if STBVOX_CONFIG_PRECISION_Z == 1 + stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_half_z[0]; + #else + stbvox_mesh_vertex *vmesh = stbvox_vmesh_delta_normal[0]; + #endif + for (z=z0; z < mm->z1; ++z) { + // if it's solid and at least one neighbor isn't solid + if (bt[z] && (!bt[z+ns_off] || !bt[z-ns_off] || !bt[z+ew_off] || !bt[z-ew_off] || !bt[z-1] || !bt[z+1])) { + pos.z = z; + stbvox_make_mesh_for_block(mm, pos, v_off+z, vmesh); + if (mm->full) { + mm->cur_z = z; + return; + } + } + } + } +} -#ifdef STBVOX_OPTIMIZED_VHEIGHT -// optimized vheight generates a single normal over the entire face, even if it's not planar -static stbvox_optimized_face_up_normal[4][4][4][4] = +static void stbvox_bring_up_to_date(stbvox_mesh_maker *mm) { - { - { - { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_nu , }, - { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_nu , }, - },{ - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, - { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_nu , }, - },{ - { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, - { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nu , STBVF_nu , STBVF_ne_u, }, - },{ - { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, - { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - }, - },{ - { - { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, - { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, - { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, - { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, - },{ - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_nu , }, - },{ - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, - }, - },{ - { - { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, - { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, - { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, - { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, - },{ - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, - { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, - { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, - { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, - }, - },{ - { - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, - { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, - { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_nw_u, }, - { STBVF_wu , STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, - },{ - { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, - { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, - { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_nw_u, }, - },{ - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, - { STBVF_sw_u, STBVF_wu , STBVF_wu , STBVF_nw_u, }, - },{ - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, - }, - }, -}; -#else -// which normal to use for a given vheight that's planar -// @TODO: this table was constructed by hand and may have bugs -// nw se sw -static stbvox_planar_face_up_normal[4][4][4] = -{ - { // sw,se,nw,ne; ne = se+nw-sw - { STBVF_u , 0 , 0 , 0 }, // 0,0,0,0; 1,0,0,-1; 2,0,0,-2; 3,0,0,-3; - { STBVF_u , STBVF_u , 0 , 0 }, // 0,1,0,1; 1,1,0, 0; 2,1,0,-1; 3,1,0,-2; - { STBVF_wu , STBVF_nw_u, STBVF_nu , 0 }, // 0,2,0,2; 1,2,0, 1; 2,2,0, 0; 3,2,0,-1; - { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nu }, // 0,3,0,3; 1,3,0, 2; 2,3,0, 1; 3,3,0, 0; - },{ - { STBVF_u , STBVF_u , 0 , 0 }, // 0,0,1,1; 1,0,1, 0; 2,0,1,-1; 3,0,1,-2; - { STBVF_sw_u, STBVF_u , STBVF_ne_u, 0 }, // 0,1,1,2; 1,1,1, 1; 2,1,1, 0; 3,1,1,-1; - { STBVF_sw_u, STBVF_u , STBVF_u , STBVF_ne_u }, // 0,2,1,3; 1,2,1, 2; 2,2,1, 1; 3,2,1, 0; - { 0 , STBVF_wu , STBVF_nw_u, STBVF_nu }, // 0,3,1,4; 1,3,1, 3; 2,3,1, 2; 3,3,1, 1; - },{ - { STBVF_su , STBVF_se_u, STBVF_eu , 0 }, // 0,0,2,2; 1,0,2, 1; 2,0,2, 0; 3,0,2,-1; - { STBVF_sw_u, STBVF_u , STBVF_u , STBVF_ne_u }, // 0,1,2,3; 1,1,2, 2; 2,1,2, 1; 3,1,2, 0; - { 0 , STBVF_sw_u, STBVF_u , STBVF_ne_u }, // 0,2,2,4; 1,2,2, 3; 2,2,2, 2; 3,2,2, 1; - { 0 , 0 , STBVF_u , STBVF_u }, // 0,3,2,5; 1,3,2, 4; 2,3,2, 3; 3,3,2, 2; - },{ - { STBVF_su , STBVF_se_u, STBVF_se_u, STBVF_eu }, // 0,0,3,3; 1,0,3, 2; 2,0,3, 1; 3,0,3, 0; - { 0 , STBVF_su , STBVF_se_u, STBVF_eu }, // 0,1,3,4; 1,1,3, 3; 2,1,3, 2; 3,1,3, 1; - { 0 , 0 , STBVF_u , STBVF_u }, // 0,2,3,5; 1,2,3, 4; 2,2,3, 3; 3,2,3, 2; - { 0 , 0 , 0 , STBVF_u }, // 0,3,3,6; 1,3,3, 5; 2,3,3, 4; 3,3,3, 3; + if (mm->config_dirty) { + int i; + #ifdef STBVOX_ICONFIG_FACE_ATTRIBUTE + mm->num_mesh_slots = 1; + for (i=0; i < STBVOX_MAX_MESHES; ++i) { + mm->output_size[i][0] = 32; + mm->output_step[i][0] = 8; + } + #else + mm->num_mesh_slots = 2; + for (i=0; i < STBVOX_MAX_MESHES; ++i) { + mm->output_size[i][0] = 16; + mm->output_step[i][0] = 4; + mm->output_size[i][1] = 4; + mm->output_step[i][1] = 4; + } + #endif + + mm->config_dirty = 0; } -}; +} -// these tables were constructed automatically using a variant of the code -// below; however, they seem wrong, so who knows -static stbvox_face_up_normal_012[4][4][4] = +int stbvox_make_mesh(stbvox_mesh_maker *mm) { - { - { STBVF_u , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - { STBVF_wu , STBVF_nu , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_wu , STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, - { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_sw_u, STBVF_u , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_sw_u, STBVF_wu , STBVF_nu , STBVF_ne_u, }, - { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, - { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_ne_u, }, - { STBVF_sw_u, STBVF_sw_u, STBVF_u , STBVF_ne_u, }, - { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nu , }, - },{ - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_u , }, + int x,y; + stbvox_bring_up_to_date(mm); + mm->full = 0; + if (mm->cur_x > mm->x0 || mm->cur_y > mm->y0 || mm->cur_z > mm->z0) { + stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->cur_z); + if (mm->full) + return 0; + ++mm->cur_y; + while (mm->cur_y < mm->y1 && !mm->full) { + stbvox_make_mesh_for_column(mm, mm->cur_x, mm->cur_y, mm->z0); + if (mm->full) + return 0; + ++mm->cur_y; + } + ++mm->cur_x; } -}; + for (x=mm->cur_x; x < mm->x1; ++x) { + for (y=mm->y0; y < mm->y1; ++y) { + stbvox_make_mesh_for_column(mm, x, y, mm->z0); + if (mm->full) { + mm->cur_x = x; + mm->cur_y = y; + return 0; + } + } + } + return 1; +} -static stbvox_face_up_normal_013[4][4][4] = +void stbvox_init_mesh_maker(stbvox_mesh_maker *mm) { - { - { STBVF_u , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_nw_u, STBVF_nu , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_eu , }, - { STBVF_wu , STBVF_u , STBVF_eu , STBVF_eu , }, - { STBVF_nw_u, STBVF_nw_u, STBVF_nu , STBVF_ne_u, }, - { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_su , STBVF_eu , STBVF_eu , }, - { STBVF_wu , STBVF_wu , STBVF_u , STBVF_eu , }, - { STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, STBVF_nu , }, - },{ - { STBVF_su , STBVF_su , STBVF_su , STBVF_eu , }, - { STBVF_sw_u, STBVF_su , STBVF_su , STBVF_su , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_su , STBVF_eu , }, - { STBVF_wu , STBVF_wu , STBVF_wu , STBVF_u , }, + memset(mm, 0, sizeof(*mm)); + stbvox_build_default_palette(); + + mm->config_dirty = 1; + mm->default_mesh = 0; +} + +int stbvox_get_buffer_count(stbvox_mesh_maker *mm) +{ + stbvox_bring_up_to_date(mm); + return mm->num_mesh_slots; +} + +int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int n) +{ + return mm->output_size[0][n]; +} + +void stbvox_reset_buffers(stbvox_mesh_maker *mm) +{ + int i; + for (i=0; i < STBVOX_MAX_MESHES*STBVOX_MAX_MESH_SLOTS; ++i) { + mm->output_cur[0][i] = 0; + mm->output_buffer[0][i] = 0; } -}; +} -static stbvox_face_up_normal_023[4][4][4] = +void stbvox_set_buffer(stbvox_mesh_maker *mm, int mesh, int slot, void *buffer, size_t len) { - { - { STBVF_u , STBVF_nu , STBVF_nu , STBVF_nu , }, - { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, - { STBVF_eu , STBVF_eu , STBVF_eu , STBVF_eu , }, - },{ - { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, - { STBVF_su , STBVF_u , STBVF_nu , STBVF_nu , }, - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - },{ - { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, - { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, - { STBVF_su , STBVF_su , STBVF_u , STBVF_nu , }, - { STBVF_su , STBVF_su , STBVF_eu , STBVF_eu , }, - },{ - { STBVF_wu , STBVF_nw_u, STBVF_nw_u, STBVF_nw_u, }, - { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, - { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, - { STBVF_su , STBVF_su , STBVF_su , STBVF_u , }, + int i; + stbvox_bring_up_to_date(mm); + mm->output_buffer[mesh][slot] = (char *) buffer; + mm->output_cur [mesh][slot] = (char *) buffer; + mm->output_len [mesh][slot] = len; + mm->output_end [mesh][slot] = (char *) buffer + len; + for (i=0; i < STBVOX_MAX_MESH_SLOTS; ++i) { + if (mm->output_buffer[mesh][i]) { + assert(mm->output_len[mesh][i] / mm->output_size[mesh][i] == mm->output_len[mesh][slot] / mm->output_size[mesh][slot]); + } } -}; +} -static stbvox_face_up_normal_123[4][4][4] = +void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh) { - { - { STBVF_u , STBVF_nu , STBVF_nu , STBVF_nu , }, - { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - { STBVF_eu , STBVF_ne_u, STBVF_ne_u, STBVF_ne_u, }, - },{ - { STBVF_sw_u, STBVF_wu , STBVF_nw_u, STBVF_nw_u, }, - { STBVF_su , STBVF_u , STBVF_nu , STBVF_nu , }, - { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, - { STBVF_eu , STBVF_eu , STBVF_ne_u, STBVF_ne_u, }, - },{ - { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, - { STBVF_sw_u, STBVF_sw_u, STBVF_wu , STBVF_nw_u, }, - { STBVF_su , STBVF_su , STBVF_u , STBVF_nu , }, - { STBVF_su , STBVF_eu , STBVF_eu , STBVF_ne_u, }, - },{ - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, - { STBVF_sw_u, STBVF_sw_u, STBVF_sw_u, STBVF_wu , }, - { STBVF_su , STBVF_su , STBVF_su , STBVF_u , }, + mm->default_mesh = mesh; +} + +int stbvox_get_quad_count(stbvox_mesh_maker *mm, int mesh) +{ + return (mm->output_cur[mesh][0] - mm->output_buffer[mesh][0]) / mm->output_size[mesh][0]; +} + +stbvox_input_description *stbvox_get_input_description(stbvox_mesh_maker *mm) +{ + return &mm->input; +} + +void stbvox_set_input_range(stbvox_mesh_maker *mm, int x0, int y0, int z0, int x1, int y1, int z1) +{ + mm->x0 = x0; + mm->y0 = y0; + mm->z0 = z0; + + mm->x1 = x1; + mm->y1 = y1; + mm->z1 = z1; + + mm->cur_x = x0; + mm->cur_y = y0; + mm->cur_z = z0; + + // @TODO validate that this range is representable in this mode +} + +void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3]) +{ + // scale + transform[0][0] = 1.0; + transform[0][1] = 1.0; + #if STBVOX_CONFIG_PRECISION_Z==1 + transform[0][2] = 0.5f; + #else + transform[0][2] = 1.0f; + #endif + // translation + transform[1][0] = (float) (mm->pos_x); + transform[1][1] = (float) (mm->pos_y); + transform[1][2] = (float) (mm->pos_z); + // texture coordinate projection translation + transform[2][0] = (float) (mm->pos_x & 255); // @TODO depends on max texture scale + transform[2][1] = (float) (mm->pos_y & 255); + transform[2][2] = (float) (mm->pos_z & 255); +} + +void stbvox_get_bounds(stbvox_mesh_maker *mm, float bounds[2][3]) +{ + bounds[0][0] = (float) (mm->pos_x + mm->x0); + bounds[0][1] = (float) (mm->pos_y + mm->y0); + bounds[0][2] = (float) (mm->pos_z + mm->z0); + bounds[1][0] = (float) (mm->pos_x + mm->x1); + bounds[1][1] = (float) (mm->pos_y + mm->y1); + bounds[1][2] = (float) (mm->pos_z + mm->z1); +} + +void stbvox_set_mesh_coordinates(stbvox_mesh_maker *mm, int x, int y, int z) +{ + mm->pos_x = x; + mm->pos_y = y; + mm->pos_z = z; +} + +void stbvox_set_input_stride(stbvox_mesh_maker *mm, int x_stride_in_bytes, int y_stride_in_bytes) +{ + int f,v; + mm->x_stride_in_bytes = x_stride_in_bytes; + mm->y_stride_in_bytes = y_stride_in_bytes; + for (f=0; f < 6; ++f) { + for (v=0; v < 4; ++v) { + mm->cube_vertex_offset[f][v] = stbvox_vertex_vector[f][v][0] * mm->x_stride_in_bytes + + stbvox_vertex_vector[f][v][1] * mm->y_stride_in_bytes + + stbvox_vertex_vector[f][v][2] ; + mm->vertex_gather_offset[f][v] = (stbvox_vertex_vector[f][v][0]-1) * mm->x_stride_in_bytes + + (stbvox_vertex_vector[f][v][1]-1) * mm->y_stride_in_bytes + + (stbvox_vertex_vector[f][v][2]-1) ; + } } -}; -#endif +} ///////////////////////////////////////////////////////////////////////////// // diff --git a/tests/test_cpp_compilation.cpp b/tests/test_cpp_compilation.cpp index f97c06b..59b7969 100644 --- a/tests/test_cpp_compilation.cpp +++ b/tests/test_cpp_compilation.cpp @@ -7,6 +7,8 @@ #define STB_IMAGE_IMPLEMENTATION #define STB_HERRINGBONE_WANG_TILE_IMPLEMENTATION #define STB_RECT_PACK_IMPLEMENTATION +#define STB_VOXEL_RENDER_IMPLEMENTATION +#define STBVOX_CONFIG_MODE 1 #define STBI_MALLOC my_malloc #define STBI_FREE my_free @@ -25,6 +27,7 @@ void my_free(void *) { } #include "stb_c_lexer.h" #include "stb_divide.h" #include "stb_herringbone_wang_tile.h" +#include "stb_voxel_render.h" #define STBTE_DRAW_RECT(x0,y0,x1,y1,color) do ; while(0) #define STBTE_DRAW_TILE(x,y,id,highlight,data) do ; while(0)