Browse Source

various voxel/caveview improvements

Sean Barrett 10 years ago
parent
commit
0f8e471f7b
3 changed files with 132 additions and 80 deletions
  1. 100 43
      stb_voxel_render.h
  2. 17 28
      tests/caveview/cave_mesher.c
  3. 15 9
      tests/caveview/cave_render.c

+ 100 - 43
stb_voxel_render.h

@@ -1,20 +1,21 @@
 // @TODO
 //
-//   - API for texture rotation on side faces (& top&bottom ?)
-//   - edge clamp
+//   - 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
 //   - gather vertex lighting from slopes correctly
-//   - better support texture edge_clamp: explicitly mod texcoords by 1, use textureGrad to avoid
-//     mipmap articats. Need to do compute texcoords in vertex shader, offset towards
-//     center before modding, need 2 bits per vertex to know offset direction (is implicit
-//     for modes without vertex data)
+//   - better support texture edge_clamp: currently if you fall
+//     exactly on 1.0 you get wrapped incorrectly; this is rare, but
+//     can avoid: compute texcoords in vertex shader, offset towards
+//     center before modding, need 2 bits per vertex to know offset direction)
 //   - add 10-byte quad type (loses per-face tex1/tex2 blend mode)
 //   - add 6-byte quad type (loses baked ao, most geometry, flags, texlerp)
 //   - add 4-byte quad type (only texture or only color, no baked light, no recolor)
+//   - fog properties
 //   - enable/disable fog at run-time
 
-// stb_voxel_render.h - 0.01 - Sean Barrett, 2015
+// stb_voxel_render.h - 0.01 - Sean Barrett, 2015 - public domain
 //
 // This library helps render large "voxel" worlds for games,
 // in this case one with blocks that can have textures and that
@@ -381,6 +382,7 @@ enum
 #define STBVOX_MAKE_TEXLERP1(vert,e2,n2,w2,s2,u4,d2) STBVOX_MAKE_TEXLERP(s2, w2, d2, vert)
 #define STBVOX_MAKE_TEXLERP2(vert,e2,n2,w2,s2,u4,d2) ((u2)*16 + (n2)*4 + (s2))
 #define STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d)  ((e)+(n)*2+(w)*4+(s)*8+(u)*16+(d)*32)
+#define STBVOX_MAKE_SIDE_TEXROT(e,n,w,s) ((e)+(n)*4+(w)*16+(s)*64)
 
 #ifdef STBVOX_ROTATION_IN_LIGHTING
 #define STBVOX_MAKE_LIGHTING(lighting, rot)  (((lighting)&~3)+(rot))
@@ -408,6 +410,7 @@ struct stbvox_input_description
    unsigned char *tex2;                    // raw tex2 value to use on all sides of block
    unsigned char *tex2_replace;            // STBVOX_MAKE_TEX2_REPLACE --  tex2:6, face_1:2
    unsigned char *tex2_facemask;           // facemask:6 (use all bits of tex2_replace as texture)
+   unsigned char *side_texrot;             // e:2,n:2,w:2,s:2 texture rotation
    unsigned char *vheight;                 // STBVOX_MAKE_VHEIGHT   -- sw:2, se:2, nw:2, ne:2, doesn't rotate
    unsigned char *texlerp;                 // STBVOX_MAKE_TEXLERP   -- vert:2, ud:2, ew:2, ns:2
    unsigned char *texlerp2;                // STBVOX_MAKE_TEXLERP2 (and use STBVOX_MAKE_TEXLERP1 for 'texlerp' -- e:2, n:2, u:3, unused:1
@@ -440,11 +443,13 @@ struct stbvox_input_description
    unsigned char *block_color;
    unsigned char *block_texlerp;
    unsigned char *block_selector;
+   unsigned char *block_side_texrot;
 
    // indexed by overlay*6+side; in all cases 0 means 'nochange'
    unsigned char (*overlay_tex1)[6];
    unsigned char (*overlay_tex2)[6];
    unsigned char (*overlay_color)[6];
+   unsigned char *overlay_side_texrot;
 
    // indexed by extended_color
    unsigned char *ecolor_color;    // 256
@@ -824,7 +829,7 @@ static char *stbvox_fragment_program =
       "uniform vec4 camera_pos;\n"  // 4th value is used for arbitrary hacking
 
       // probably constant data
-      "uniform vec3 ambient[4];\n"
+      "uniform vec4 ambient[4];\n"
 
       #ifndef STBVOX_ICONFIG_UNTEXTURED
          // generally constant data
@@ -875,7 +880,9 @@ static char *stbvox_fragment_program =
             "   vec3 texgen_s = texelFetch(texgen, int(texprojid)).xyz;\n"
             "   vec3 texgen_t = texelFetch(texgen, int(texprojid+32u)).xyz;\n"
             "   float tex1_scale = texelFetch(texscale, int(tex1_id & 127u)).x;\n"
+            #ifndef STBVOX_CONFIG_DISABLE_TEX2
             "   float tex2_scale = texelFetch(texscale, int(tex2_id & 127u)).y;\n"
+            #endif
             "   vec4 color = texelFetch(color_table, int(color_id & 63u));\n"
          #endif
          "   vec2 texcoord;\n"
@@ -883,35 +890,55 @@ static char *stbvox_fragment_program =
          "   texcoord.s = dot(texturespace_pos, texgen_s);\n"
          "   texcoord.t = dot(texturespace_pos, texgen_t);\n"
 
-         // @TODO: use 2 bits of facedata.w to enable animation of facedata.x & y?
+         "   vec2  texcoord_1 = tex1_scale * texcoord;\n"
+         #ifndef STBVOX_CONFIG_DISABLE_TEX2
+         "   vec2  texcoord_2 = tex2_scale * texcoord;\n"
+         #endif
+
+         #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP
+         "   texcoord_1 = texcoord_1 - floor(texcoord_1);\n"
+         "   vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n"
+         #else
+         "   vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n"
+         #endif
 
-         "   vec4 tex1 = texture(tex_array[0], vec3(tex1_scale * texcoord, float(tex1_id)));\n"
-         "   vec4 tex2 = texture(tex_array[1], vec3(tex2_scale * texcoord, float(tex2_id)));\n"
+         #ifndef STBVOX_CONFIG_DISABLE_TEX2
+         #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP
+         "   texcoord_2 = texcoord_2 - floor(texcoord_2);\n"
+         "   vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n"
+         #else
+         "   vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n"
+         #endif
+         #endif
 
          "   bool emissive = (int(color.w) & 1) != 0;\n"
 
          // recolor textures
          "   if ((color_id &  64u) != 0u) tex1.xyz *= color.xyz;\n"
-         "   if ((color_id & 128u) != 0u) tex2.xyz *= color.xyz;\n"
+         "   fragment_alpha = tex1.a;\n"
+         #ifndef STBVOX_CONFIG_DISABLE_TEX2
+            "   if ((color_id & 128u) != 0u) tex2.xyz *= color.xyz;\n"
 
-         #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA
-         "   tex2.rgba *= texlerp;\n"
-         #else
-         "   tex2.a *= texlerp;\n"
-         #endif
+            #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA
+            "   tex2.rgba *= texlerp;\n"
+            #else
+            "   tex2.a *= texlerp;\n"
+            #endif
 
-         "   if (texblend_mode)\n"
-         "      albedo = tex2.xyz * rlerp(tex2.a, 2.0*tex1.xyz, vec3(1.0,1.0,1.0));\n"
-         "   else {\n"
-         #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA
-         "      albedo = (1.0-tex2.a)*tex1.xyz + tex2.xyz;\n"
+            "   if (texblend_mode)\n"
+            "      albedo = tex2.xyz * rlerp(tex2.a, 2.0*tex1.xyz, vec3(1.0,1.0,1.0));\n"
+            "   else {\n"
+            #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA
+            "      albedo = (1.0-tex2.a)*tex1.xyz + tex2.xyz;\n"
+            #else
+            "      albedo = rlerp(tex2.a, tex1.xyz, tex2.xyz);\n"
+            #endif
+            "      fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n"
+            "   }\n"
          #else
-         "      albedo = rlerp(tex2.a, tex1.xyz, tex2.xyz);\n"
+            "      albedo = tex1.xyz;\n"
          #endif
-         "      fragment_alpha = tex1.a*(1-tex2.a)+tex2.a;\n"
-         "   }\n"
 
-         "   fragment_alpha = tex1.a;\n"
       #else // UNTEXTURED
          "   vec4 color;"
          "   color.xyz = vec3(facedata.xyz) / 255.0;\n"
@@ -928,7 +955,7 @@ static char *stbvox_fragment_program =
          "   vec3 normal = vnormal;\n"
       #endif
 
-      "   vec3 ambient_color = dot(normal, ambient[0]) * ambient[1] + ambient[2];\n"
+      "   vec3 ambient_color = dot(normal, ambient[0].xyz) * ambient[1].xyz + ambient[2].xyz;\n"
 
       "   ambient_color = clamp(ambient_color, 0.0, 1.0);"
       "   ambient_color *= amb_occ;\n"
@@ -972,14 +999,15 @@ static char *stbvox_fragment_program =
       "\n"
       "vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha)\n"
       "{\n"
-      "   float f = sqrt(dot(relative_pos,relative_pos))/1320.0;\n"
+      "   float f = dot(relative_pos,relative_pos)*ambient[3].w;\n"
+      //"   f = rlerp(f, -2,1);\n"
       "   f = clamp(f, 0.0, 1.0);\n" 
       "   f = 3.0*f*f - 2.0*f*f*f;\n" // smoothstep
-      "   f = f*f;\n"  // fade in more smoothly
+      //"   f = f*f;\n"  // fade in more smoothly
       #ifdef STBVOX_CONFIG_PREMULTIPLIED_ALPHA
-      "   return rlerp(f, color.xyz, ambient[3]*fragment_alpha);\n"
+      "   return rlerp(f, color.xyz, ambient[3].xyz*fragment_alpha);\n"
       #else
-      "   return rlerp(f, color.xyz, ambient[3]);\n"
+      "   return rlerp(f, color.xyz, ambient[3].xyz);\n"
       #endif
       "}\n"
    #endif
@@ -1042,10 +1070,22 @@ static char *stbvox_fragment_program_alpha_only =
       "   texcoord.s = dot(texturespace_pos, texgen_s);\n"
       "   texcoord.t = dot(texturespace_pos, texgen_t);\n"
 
-      // @TODO: use 2 bits of facedata.w to enable animation of facedata.x & y?
+      "   vec2  texcoord_1 = tex1_scale * texcoord;\n"
+      "   vec2  texcoord_2 = tex2_scale * texcoord;\n"
 
-      "   vec4 tex1 = texture(tex_array[0], vec3(tex1_scale * texcoord, float(tex1_id)));\n"
-      "   vec4 tex2 = texture(tex_array[1], vec3(tex2_scale * texcoord, float(tex2_id)));\n"
+      #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP
+      "   texcoord_1 = texcoord_1 - floor(texcoord_1);\n"
+      "   vec4 tex1 = textureGrad(tex_array[0], vec3(texcoord_1, float(tex1_id)), dFdx(tex1_scale*texcoord), dFdy(tex1_scale*texcoord));\n"
+      #else
+      "   vec4 tex1 = texture(tex_array[0], vec3(texcoord_1, float(tex1_id)));\n"
+      #endif
+
+      #ifdef STBVOX_CONFIG_TEX2_EDGE_CLAMP
+      "   texcoord_2 = texcoord_2 - floor(texcoord_2);\n"
+      "   vec4 tex2 = textureGrad(tex_array[0], vec3(texcoord_2, float(tex2_id)), dFdx(tex2_scale*texcoord), dFdy(tex2_scale*texcoord));\n"
+      #else
+      "   vec4 tex2 = texture(tex_array[1], vec3(texcoord_2, float(tex2_id)));\n"
+      #endif
 
       "   tex2.a *= texlerp;\n"
 
@@ -1094,8 +1134,8 @@ static stbvox_uniform_info stbvox_uniforms[] =
    { STBVOX_UNIFORM_TYPE_vec4     , 16,  64, "color_table"  , stbvox_default_palette[0]  , STBVOX_TEXBUF },
    { STBVOX_UNIFORM_TYPE_vec3     , 12,  32, "normal_table" , stbvox_default_normals[0]   },
    { STBVOX_UNIFORM_TYPE_vec3     , 12,  64, "texgen"       , stbvox_default_texgen[0][0], STBVOX_TEXBUF },
-   { STBVOX_UNIFORM_TYPE_vec3     , 12,   4, "ambient"      , 0                           },
-   { STBVOX_UNIFORM_TYPE_vec4     , 12,   1, "camera_pos"   , stbvox_dummy_transform[0]   },
+   { STBVOX_UNIFORM_TYPE_vec4     , 16,   4, "ambient"      , 0                           },
+   { STBVOX_UNIFORM_TYPE_vec4     , 16,   1, "camera_pos"   , stbvox_dummy_transform[0]   },
 };
 
 STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform)
@@ -1141,6 +1181,7 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro
    stbvox_mesh_face face_data = { 0 };
    stbvox_block_type bt = mm->input.blocktype[v_off];
    unsigned char bt_face = STBVOX_ROTATE(face, rot.block);
+   int facerot = rot.facerot;
 
    if (mm->input.color)
       face_data.color = mm->input.color[v_off];
@@ -1167,6 +1208,13 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro
          face_data.color = mcol;
    }
 
+   if (face <= STBVOX_FACE_south) {
+      if (mm->input.side_texrot)
+         facerot = mm->input.side_texrot[v_off] >> (2 * face);
+      else if (mm->input.block_side_texrot)
+         facerot = mm->input.block_side_texrot[v_off] >> (2 * bt_face);
+   }
+
    if (mm->input.overlay) {
       int over_face = STBVOX_ROTATE(face, rot.overlay);
       unsigned char over = mm->input.overlay[v_off];
@@ -1185,7 +1233,11 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro
          if (rep3)
             face_data.color = rep3;
       }
+
+      if (face <= STBVOX_FACE_south)
+         facerot = mm->input.overlay_side_texrot[over] >> (2*over_face);
    }
+
    if (mm->input.tex2_for_tex1)
       face_data.tex2 = mm->input.tex2_for_tex1[face_data.tex1];
    if (mm->input.tex2)
@@ -1195,19 +1247,22 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro
       if (mm->input.tex2_facemask[v_off] & (1 << tex2_face))
          face_data.tex2 = mm->input.tex2_replace[v_off];
    }
+
    color_face = STBVOX_ROTATE(face, rot.ecolor);
    if (mm->input.extended_color) {
       unsigned char ec = mm->input.extended_color[v_off];
       if (mm->input.ecolor_facemask[ec] & (1 << color_face))
          face_data.color = mm->input.ecolor_color[ec];
    }
+
    if (mm->input.color2) {
       if (mm->input.color2_facemask[v_off] & (1 << color_face))
          face_data.color = mm->input.color2[v_off];
       if (mm->input.color3 && (mm->input.color3_facemask[v_off] & (1 << color_face)))
          face_data.color = mm->input.color3[v_off];
    }
-   face_data.face_info = (normal<<2) + rot.facerot;
+
+   face_data.face_info = (normal<<2) + facerot;
    return face_data;
 }
 
@@ -1309,8 +1364,9 @@ void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int fac
          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);
+               *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];
@@ -1330,7 +1386,8 @@ void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int fac
                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);
+                          + 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
@@ -2143,9 +2200,9 @@ void stbvox_get_transform(stbvox_mesh_maker *mm, float transform[3][3])
    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 & 63); // @TODO depends on max texture scale
-   transform[2][1] = (float) (mm->pos_y & 63);
-   transform[2][2] = (float) (mm->pos_z & 63);
+   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])

+ 17 - 28
tests/caveview/cave_mesher.c

@@ -21,8 +21,10 @@
 #define STBVOX_CONFIG_PREFER_TEXBUFFER
 //#define STBVOX_CONFIG_LIGHTING_SIMPLE
 #define STBVOX_CONFIG_FOG_SMOOTHSTEP
-//#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA  // use this even though it doesn't really work for alpha test without next #define
-//#define STBVOX_CONFIG_UNPREMULTIPLY  // slower, makes windows & fancy leaves look better
+//#define STBVOX_CONFIG_PREMULTIPLIED_ALPHA  // this doesn't work properly alpha test without next #define
+//#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_ROTATION_IN_LIGHTING
 #define STB_VOXEL_RENDER_IMPLEMENTATION
@@ -300,7 +302,6 @@ unsigned char minecraft_info[256][7] =
 
 unsigned char minecraft_tex1_for_blocktype[256][6];
 unsigned char effective_blocktype[256];
-unsigned char effective_block_add[256];
 unsigned char minecraft_color_for_blocktype[256][6];
 unsigned char minecraft_geom_for_blocktype[256];
 
@@ -703,26 +704,21 @@ unsigned char mc_rot[4] = { 1,3,2,0 };
 // in lighting
 void build_stair_rotations(int blocktype, unsigned char *map)
 {
-   int i,j,k;
-   for (j=0; j < 2; ++j) {
-      int geom = j ? STBVOX_GEOM_ceil_slope_north_is_bottom : STBVOX_GEOM_floor_slope_north_is_top;
-      //int geom = STBVOX_GEOM_solid;
-      for (i=0; i < 4; ++i) {
-         if (i == 0 && j == 0) {
-            map[j*4+i+8] = map[j*4+i] = blocktype;
-            minecraft_geom_for_blocktype[blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(geom, 0, 0);
-         } else {
-            map[j*4+i+8] = map[j*4+i] = next_blocktype;
+   int i;
 
-            for (k=0; k < 6; ++k) {
-               minecraft_color_for_blocktype[next_blocktype][k] = minecraft_color_for_blocktype[blocktype][k];
-               minecraft_tex1_for_blocktype [next_blocktype][k] = minecraft_tex1_for_blocktype [blocktype][k];
-            }
-            minecraft_geom_for_blocktype[next_blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(geom, 0, 0);
-            --next_blocktype;
-         }
-      }
+   // use the existing block type for floor stairs; allocate a new type for ceil stairs
+   for (i=0; i < 6; ++i) {
+      minecraft_color_for_blocktype[next_blocktype][i] = minecraft_color_for_blocktype[blocktype][i];
+      minecraft_tex1_for_blocktype [next_blocktype][i] = minecraft_tex1_for_blocktype [blocktype][i];
    }
+   minecraft_geom_for_blocktype[next_blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_ceil_slope_north_is_bottom, 0, 0);
+   minecraft_geom_for_blocktype[     blocktype] = (unsigned char) STBVOX_MAKE_GEOMETRY(STBVOX_GEOM_floor_slope_north_is_top, 0, 0);
+
+   for (i=0; i < 4; ++i) {
+      map[0+i+8] = map[0+i] =      blocktype;
+      map[4+i+8] = map[4+i] = next_blocktype;
+   }
+   --next_blocktype;
 }
 
 void build_wool_variations(int bt, unsigned char *map)
@@ -847,11 +843,4 @@ void mesh_init(void)
    // set the remap flags for these so they write the rotation values
    remap_in_place(54, 9);
    remap_in_place(146, 10);
-
-   for (i=0; i < 256; ++i) {
-      if (remap[i])
-         effective_block_add[i] = 0;
-      else
-         effective_block_add[i] = effective_blocktype[i];
-   }
 }

+ 15 - 9
tests/caveview/cave_render.c

@@ -18,6 +18,7 @@
 #include <math.h>
 #include <assert.h>
 
+//#define STBVOX_CONFIG_TEX1_EDGE_CLAMP
 
 
 // currently no dynamic way to set mesh cache size or view distance
@@ -182,6 +183,7 @@ static void upload_mesh_data(raw_mesh *rm)
 
 GLint uniform_loc[16];
 float table3[128][3];
+float table4[64][4];
 GLint tablei[2];
 
 float step=0;
@@ -238,9 +240,9 @@ void setup_uniforms(float pos[3])
 
                // ambient direction is sky-colored upwards
                // "ambient" lighting is from above
-               table3[0][0] =  0.3f;
-               table3[0][1] = -0.5f;
-               table3[0][2] =  0.9f;
+               table4[0][0] =  0.3f;
+               table4[0][1] = -0.5f;
+               table4[0][2] =  0.9f;
 
                amb[1][0] = 0.3f; amb[1][1] = 0.3f; amb[1][2] = 0.3f; // dark-grey
                amb[2][0] = 1.0; amb[2][1] = 1.0; amb[2][2] = 1.0; // white
@@ -252,16 +254,16 @@ void setup_uniforms(float pos[3])
                //     amb[1] + (amb[2] - amb[1]) * dot/2 + (amb[2]-amb[1])/2
 
                for (j=0; j < 3; ++j) {
-                  table3[1][j] = (amb[2][j] - amb[1][j])/2 * bright;
-                  table3[2][j] = (amb[1][j] + amb[2][j])/2 * bright;
+                  table4[1][j] = (amb[2][j] - amb[1][j])/2 * bright;
+                  table4[2][j] = (amb[1][j] + amb[2][j])/2 * bright;
                }
 
                // fog color
-               table3[3][0] = 0.6f, table3[3][1] = 0.7f, table3[3][2] = 0.9f;
-               // fog distance
-               //table3[3][3] = 1200;
+               table4[3][0] = 0.6f, table4[3][1] = 0.7f, table4[3][2] = 0.9f;
+               table4[3][3] = 1.0f / 1320.0f;
+               table4[3][3] *= table4[3][3];
 
-               data = table3;
+               data = table4;
                break;
             }
          }
@@ -407,6 +409,10 @@ void render_init(void)
    glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16);
+   #ifdef STBVOX_CONFIG_TEX1_EDGE_CLAMP
+   glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+   glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+   #endif
 
    glGenerateMipmapEXT(GL_TEXTURE_2D_ARRAY_EXT);