소스 검색

added input.packed_compact for storing most of the major 2/3-bit values in one byte

Sean Barrett 10 년 전
부모
커밋
9534eb2e08
2개의 변경된 파일124개의 추가작업 그리고 30개의 파일을 삭제
  1. 122 29
      stb_voxel_render.h
  2. 2 1
      tests/caveview/cave_mesher.c

+ 122 - 29
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

+ 2 - 1
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"