diff --git a/stb_voxel_render.h b/stb_voxel_render.h index 6517b3a..e178776 100644 --- a/stb_voxel_render.h +++ b/stb_voxel_render.h @@ -117,6 +117,27 @@ // then you wouldn't even have to change anything but the mode number.) // // +// IMPROVEMENTS FOR SHIP-WORTHY PROGRAMS USING THIS LIBRARY +// +// I currently tolerate a certain level of "bugginess" in this library. +// +// I'm referring to things which look a little wrong (as long as they +// don't cause holes or cracks in the output meshes), or things which +// do not produce as optimal a mesh as possible. Notable examples: +// +// - incorrect lighting on slopes +// - inefficient meshes for vheight blocks +// +// I am willing to do the work to improve these things if someone is +// going to ship a substantial program that would be improved by them. +// (It need not be commercial, nor need it be a game.) I just didn't +// want to do the work up front if it might never be leveraged. So just +// submit a bug report as usual (github is preferred), but add a note +// that this is for a thing that is really going to ship. (That means +// you need to be far enough into the project that it's clear you're +// committed to it; not during early exploratory development.) +// +// // VOXEL MESH API // // Context @@ -169,6 +190,7 @@ // // VERSION HISTORY // +// 0.81 (2015-04-12) added input.packed_compact to store rot, vheight & texlerp efficiently // 0.80 (2015-04-11) fix broken STBVOX_CONFIG_ROTATION_IN_LIGHTING refactoring // change STBVOX_MAKE_LIGHTING to STBVOX_MAKE_LIGHTING_EXT so // that header defs don't need to see config vars @@ -959,6 +981,19 @@ struct stbvox_input_description // Indexed by 3D coordinates, this defines the four // vheight values to use if the geometry is STBVOX_GEOM_vheight*. // See the vheight discussion. + + unsigned char *packed_compact; + // Stores block rotation, vheight, and texlerp values: + // block rotation: 2 bits + // vertex vheight: 2 bits + // use_texlerp : 1 bit + // vertex texlerp: 3 bits + // If STBVOX_CONFIG_UP_TEXLERP_PACKED is defined, then 'vertex texlerp' is + // used for up faces if use_texlerp is 1. If STBVOX_CONFIG_DOWN_TEXLERP_PACKED + // is defined, then 'vertex texlerp' is used for down faces if use_texlerp is 1. + // Note if those symbols are defined but packed_compact is NULL, the normal + // texlerp default will be used. + // Encode with STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, use_texlerp) }; // @OPTIMIZE allow specializing; build a single struct with all of the // 3D-indexed arrays combined so it's AoS instead of SoA for better @@ -969,6 +1004,17 @@ struct stbvox_input_description // // VHEIGHT DOCUMENTATION // +// "vheight" is the internal name for the special block types +// with sloped tops or bottoms. "vheight" stands for "vertex height". +// +// Note that these blocks are very flexible (there are 256 of them, +// although at least 17 of them should never be used), but they +// also have a disadvantage that they generate extra invisible +// faces; the generator does not currently detect whether adjacent +// vheight blocks hide each others sides, so those side faces are +// always generated. For a continuous ground terrain, this means +// that you may generate 5x as many quads as needed. See notes +// on "improvements for shipping products" in the introduction. enum { @@ -989,9 +1035,9 @@ enum // 1/2, 2/2, or 3/2. 0 is the bottom of the block, 1 is halfway // up the block, 2 is the top of the block, and 3 is halfway up the // next block (and actually outside of the block). The value 3 is -// actually legal, and allows you to: +// actually legal for floor vheight (but not ceiling), and allows you to: // -// (A) allows smoother terrain by having slopes that cross blocks, +// (A) have smoother terrain by having slopes that cross blocks, // e.g. (1,1,3,3) is a regular-seeming slope halfway between blocks // (B) make slopes steeper than 45-degrees, e.g. (0,0,3,3) // @@ -1103,6 +1149,7 @@ enum #define STBVOX_MAKE_COLOR(color,t1,t2) ((color)+(t1)*64+(t2)*128) #define STBVOX_MAKE_TEXLERP_VERT3(e,n,w,s,u) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096) #define STBVOX_MAKE_TEXLERP_FACE3(e,n,w,s,u,d) ((e)+(n)*8+(w)*64+(s)*512+(u)*4096+(d)*16384) +#define STBVOX_MAKE_PACKED_COMPACT(rot, vheight, texlerp, def) ((rot)+4*(vheight)+16*(use)+32*(texlerp)) #define STBVOX_MAKE_LIGHTING_EXT(lighting, rot) (((lighting)&~3)+(rot)) #define STBVOX_MAKE_LIGHTING(lighting) (lighting) @@ -2256,10 +2303,10 @@ static unsigned char stbvox_hasface[STBVOX_MAX_GEOM][STBVOX_NUM_ROTATION] = { 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 }, + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight + { 63,63,63,63 }, // vheight }; // this determines which face type above is visible on each side of the geometry @@ -2619,6 +2666,30 @@ void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int fac // first compute texlerp into p1 stbvox_mesh_vertex p1[4] = { 0 }; + #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) && defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up || (f) == STBVOX_FACE_down) + #elif defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ((f) == STBVOX_FACE_up ) + #elif defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + #define STBVOX_USE_PACKED(f) ( (f) == STBVOX_FACE_down) + #endif + + #if defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) || defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) + if (STBVOX_USE_PACKED(face)) { + if (!mm->input.packed_compact || 0==(mm->input.packed_compact[v_off]&16)) + goto set_default; + p1[0] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][0]] >> 5); + p1[1] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][1]] >> 5); + p1[2] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][2]] >> 5); + p1[3] = (mm->input.packed_compact[v_off + mm->cube_vertex_offset[face][3]] >> 5); + 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]); + goto skip; + } + #endif + if (mm->input.block_texlerp) { stbvox_block_type bt = mm->input.blocktype[v_off]; unsigned char val = mm->input.block_texlerp[bt]; @@ -2672,9 +2743,17 @@ void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int fac 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); + #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + set_default: + #endif + p1[0] = p1[1] = p1[2] = p1[3] = stbvox_vertex_encode(0,0,0,0,7); // @TODO make this configurable } + #if defined(STBVOX_CONFIG_UP_TEXLERP_PACKED) || defined(STBVOX_CONFIG_DOWN_TEXLERP_PACKED) + skip: + #endif + + // now compute lighting and store to vertices { stbvox_mesh_vertex *mv[4]; stbvox_get_quad_vertex_pointer(mm, mesh, mv, face_data); @@ -2823,6 +2902,9 @@ static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, in simple_rot = mm->input.lighting[v_off] & 3; #endif + if (mm->input.packed_compact) + simple_rot = mm->input.packed_compact[v_off] & 3; + 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); @@ -2853,9 +2935,6 @@ static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, in 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. @@ -2864,8 +2943,6 @@ static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, in // 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. - - static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_pos pos, int v_off) { int ns_off = mm->y_stride_in_bytes; @@ -2896,15 +2973,12 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po ngeo[4] = mm->input.geometry[v_off + 1]; ngeo[5] = mm->input.geometry[v_off - 1]; - #ifndef STBVOX_CONFIG_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); @@ -2913,27 +2987,41 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po ngeo[i] = mm->input.block_geometry[nbt[i]]; if (mm->input.selector) { #ifndef STBVOX_CONFIG_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; + if (mm->input.packed_compact == NULL) { + 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_CONFIG_ROTATION_IN_LIGHTING - rot = (geo>>4)&3; - geo &= 15; - for (i=0; i < 6; ++i) { - nrot[i] = (ngeo[i]>>4)&3; - ngeo[i] &= 15; + if (mm->input.packed_compact == NULL) { + rot = (geo>>4)&3; + geo &= 15; + for (i=0; i < 6; ++i) { + nrot[i] = (ngeo[i]>>4)&3; + ngeo[i] &= 15; + } } #endif } } - #ifdef STBVOX_CONFIG_ROTATION_IN_LIGHTING + #ifndef STBVOX_CONFIG_ROTATION_IN_LIGHTING + if (mm->input.packed_compact) { + rot = mm->input.packed_compact[rot] & 3; + nrot[0] = mm->input.packed_compact[v_off + ew_off] & 3; + nrot[1] = mm->input.packed_compact[v_off + ns_off] & 3; + nrot[2] = mm->input.packed_compact[v_off - ew_off] & 3; + nrot[3] = mm->input.packed_compact[v_off - ns_off] & 3; + nrot[4] = mm->input.packed_compact[v_off + 1] & 3; + nrot[5] = mm->input.packed_compact[v_off - 1] & 3; + } + #else 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; @@ -3122,6 +3210,11 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po for (i=0; i < 4; ++i) ht[i] = raw[stbvox_rotate_vertex[i][rot]]; + } else if (mm->input.packed_compact) { + ht[0] = (mm->input.packed_compact[v_off ] >> 2) & 3; + ht[1] = (mm->input.packed_compact[v_off+ew_off ] >> 2) & 3; + ht[2] = (mm->input.packed_compact[v_off +ns_off] >> 2) & 3; + ht[3] = (mm->input.packed_compact[v_off+ew_off+ns_off] >> 2) & 3; } else if (mm->input.geometry) { ht[0] = mm->input.geometry[v_off ] >> 6; ht[1] = mm->input.geometry[v_off+ew_off ] >> 6; @@ -3586,7 +3679,7 @@ int main(int argc, char **argv) // - test API for texture rotation on side faces // - API for texture rotation on top & bottom // - better culling of vheight faces with vheight neighbors -// - better culling of non-vheight faces with fheight neighbors +// - better culling of non-vheight faces with vheight neighbors // - gather vertex lighting from slopes correctly // - better support texture edge_clamp: currently if you fall // exactly on 1.0 you get wrapped incorrectly; this is rare, but diff --git a/tests/caveview/cave_mesher.c b/tests/caveview/cave_mesher.c index 52792c5..1fac297 100644 --- a/tests/caveview/cave_mesher.c +++ b/tests/caveview/cave_mesher.c @@ -25,8 +25,9 @@ //#define STBVOX_CONFIG_UNPREMULTIPLY // slower, fixes alpha test makes windows & fancy leaves look better //#define STBVOX_CONFIG_TEX1_EDGE_CLAMP #define STBVOX_CONFIG_DISABLE_TEX2 - +//#define STBVOX_CONFIG_DOWN_TEXLERP_PACKED #define STBVOX_CONFIG_ROTATION_IN_LIGHTING + #define STB_VOXEL_RENDER_IMPLEMENTATION #include "stb_voxel_render.h"