diff --git a/docs/stb_voxel_render_interview.md b/docs/stb_voxel_render_interview.md new file mode 100644 index 0000000000000000000000000000000000000000..7448efcfd1b5c363e9c19ef770ae1532d78dbc51 --- /dev/null +++ b/docs/stb_voxel_render_interview.md @@ -0,0 +1,141 @@ +# An interview with STB about stb_voxel_render.h + +**Q:** +I suppose you really like Minecraft? + +**A:** +Not really. I mean, I do own it and play it some, and +I do watch YouTube videos of other people playing it +once in a while, but I'm not saying it's that great. + +But I do love voxels. I've been playing with voxel rendering +since the mid-late 90's when we were still doing software +rendering and thinking maybe polygons weren't the answer. +Once GPUs came along that kind of died off, at least until +Minecraft brought it back to attention. + +**Q:** +Do you expect people will make a lot of Minecraft clones +with this? + +**A:** +I hope not! + +For one thing, it's a terrible idea for the +developer. Remember before Minecraft was on the Xbox 360, +there were a ton of "indie" clones (some maybe making +decent money even), but then the real Minecraft came out +and just crushed them (as far as I know). It's just not +something you really want to compete with. + +The reason I made this library is because I'd like +to see more games with Minecraft's *art style*, not +necessary its *gameplay*. + +I can understand the urge to clone the gameplay. When +you have a world made of voxels/blocks, there are a +few things that become incredibly easy to do that would +otherwise be very hard (at least for an indie) to do in 3D. +One thing is that procedural generation becomes much easier. +Another is that destructible environments are easy. Another +is that you have a world where your average user can build +stuff that they find satisfactory. + +Minecraft is at a sort of local maximum, a sweet spot, where +it leverages all of those easy-to-dos. And so I'm sure it's +hard to look at the space of 'games using voxels' and move +away from that local maximum, to give up some of that. +But I think that's what people should do. + +**Q:** +So what else can people do with stb_voxel_render? + +**A:** +All of those benefits I mentioned above are still valid even +if you stay away from the sweet spot. You can make a 3D roguelike +without player-creation/destruction that uses procedural generation. +You could make a shooter with pre-designed maps but destructible +environments. + +And I'm sure there are other possible benefits to using voxels/blocks. +Hopefully this will make it easier for people to explore the space. + +Also, the library has a pretty wide range of features to allow +people to come up with some distinctive looks. For example, +the art style of Continue?9876543210. I'm terrible at art, +so this isn't really my thing, but I tried to put in flexible +technology that could be used multiple ways. + +One thing I did intentionally was try to make it possible to +make nicer looking ground terrain, using the half-height +slopes and "weird slopes". There are Minecraft mods with +drivable cars and they just go up these blocky slopes and, +like, what? So I wanted you to be able to make smoother +terrain, either just for the look, or for vehicles etc. +Also, you can cross-fade between two ground textures for +that classic bad dirt/grass transition that has shipped +in plenty of professional games. Of course, you could +just use a separate non-voxel ground renderer for all of +this. But this way, you can seamlessly integrate everything +else with it. E.g. in your authoring tool (or procedural +generation) you can make smooth ground and then cut a +sharp-edged hole in it for a building's basement or whatever. + +**Q:** +What one thing would you really like to see somebody do? + +**A:** +Before Unity, 3D has seemed deeply problematic in the indie +space. Software like GameMaker has tried to support 3D but +it seems like little of note has been done with it. + +Minecraft has shown that people can build worlds with that +toolset far more easily than we've ever seen from those +other tools. Obviously people have done great things with +Unity, but those people are much closer to professional +developers; typically they still need real 3D modelling +and all of that stuff. + +So what I'd really like to see is someone build some kind +of voxel-game-construction-set. Start with stb_voxel_render, +maybe expose all the flexibility of stb_voxel_render (so +people + +**Q:** +Why'd you make this library? + +**A:** +Mainly as a way of releasing this technology I've been working +on since 2011 and seemed unlikely to ever ship myself. In 2011 +I was playing the voxel shooter Ace of Spades. One of the maps +that we played on was a partial port of Broville (which is the +first Minecraft map in stb_voxel_render release trailer). I'd +made a bunch of procedural level generators for the game, and +I started trying to make a city generator inspired by Broville. + +But I realized it would be a lot of work, and of very little +value (most of my maps didn't get much play because people +preferred to play on maps where they could charge straight +at the enemies and shoot them as fast as possible). So I +wrote my own voxel engine and started working on a procedural +city game. But I got bogged down after I finally got the road +generator working and never got anywhere with building +generation or gameplay. + +stb_voxel_render is actually a complete rewrite from scratch, +but it's based a lot on what I learned from that previous work. + +**Q:** +About the release video... how long did that take to edit? + +**A:** +About seven or eight hours. I had the first version done in +maybe six or sevent hours, but then I realized I'd left out +one clip, and when I went back to add it I also gussied up +a couple other moments in the video. + +**Q:** +Ok, that's it. Thanks, me. + +**A:** +Thanks *me!* diff --git a/stb_voxel_render.h b/stb_voxel_render.h index 8c63f6de8045565539236b4a4fae49cf1a833a58..498aa64a0b67a1e378f0734d6d2cac440ed8d0c9 100644 --- a/stb_voxel_render.h +++ b/stb_voxel_render.h @@ -1,10 +1,14 @@ -// stb_voxel_render.h - v0.75 - Sean Barrett, 2015 - public domain +// stb_voxel_render.h - v0.76 - 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 // can also be a few shapes other than cubes. // -// Video introduction: https://www.youtube.com/watch?v=2vnTtiLrV1w +// Video introduction: +// http://www.youtube.com/watch?v=2vnTtiLrV1w +// +// Minecraft-viewer sample app: +// http://github.com/nothings/stb/tree/master/tests/caveview // // It works by creating triangle meshes. The library includes // @@ -16,9 +20,12 @@ // the 3D graphics API. (At the moment, it's not actually portable // since the shaders are GLSL only, but patches are welcome.) // -// Currently the preferred vertex format is 20 bytes per quad. -// There are plans to allow more compact formats with a slight -// reduction in features. +// You have to do all the caching and tracking of vertex buffers +// yourself. However, you could also try making a game with +// a small enough world that it's fully loaded rather than +// streaming. Currently the preferred vertex format is 20 bytes +// per quad. There are plans to allow much more compact formats +// with a slight reduction in shader features. // // // USAGE @@ -152,6 +159,20 @@ // zmc engine 96-byte quads 2011/10 // zmc engine 32-byte quads 2013/12 // stb_voxel_render 20-byte quads 2015/01 +// +// +// CONTRIBUTORS +// +// Features Porting Bugfixes & Warnings +// Sean Barrett github:r-leyh Jesus Fernandez +// Miguel Lechon +// +// VERSION HISTORY +// +// 0.76 typos, signed/unsigned shader issue, more documentation +// 0.75 initial release +// +// #ifndef INCLUDE_STB_VOXEL_RENDER_H #define INCLUDE_STB_VOXEL_RENDER_H @@ -171,11 +192,106 @@ typedef struct stbvox_input_description stbvox_input_description; extern "C" { #endif +////////////////////////////////////////////////////////////////////////////// +// +// CONFIGURATION MACROS +// +// #define STBVOX_CONFIG_MODE +// Configures the overall behavior of stb_voxel_render. This +// can affect the shaders, the uniform info, and other things. +// (If you need more than one mode in the same app, you can +// use STBVOX_STATIC_IMPLEMENTATION to create multiple versions +// in separate files, and then wrap them.) +// +// Mode value Meaning +// 0 Textured blocks, 32-byte quads +// 1 Textured blocks, 20-byte quads +// 20 Untextured blocks, 32-byte quads +// 21 Untextured blocks, 20-byte quads +// +// +// #define STBVOX_CONFIG_PRECISION_Z +// Defines the number of bits of fractional position for Z. +// Only 0 or 1 are valid. If 0, then a single mesh has +// twice the legal Z range; e.g. in modes 0,1,20,21, +// Z in the mesh can extend to 511 instead of 255. +// However, half-height blocks cannot be used. +// +// +// All of the following just #ifdef tested so need no values. +// +// STBVOX_CONFIG_BLOCKTYPE_SHORT +// use unsigned 16-bit values for 'blocktype' in the input instead of 8-bit values +// +// STBVOX_CONFIG_OPENGL_MODELVIEW +// use the gl_ModelView matrix rather than the explicit uniform +// +// STBVOX_CONFIG_HLSL +// NOT IMPLEMENTED! Define HLSL shaders instead of GLSL shaders +// +// STBVOX_CONFIG_PREFER_TEXBUFFER +// Stores many of the uniform arrays in texture buffers intead, +// so they can be larger and may be more efficient on some hardware. +// +// STBVOX_CONFIG_LIGHTING_SIMPLE +// Creates a simple lighting engine with a single point light source +// in addition to the default half-lambert ambient light. +// +// STBVOX_CONFIG_LIGHTING +// Declares a lighting function hook; you must append a lighting function +// to the shader before compiling it: +// vec3 compute_lighting(vec3 pos, vec3 norm, vec3 albedo, vec3 ambient); +// 'ambient' is the half-lambert ambient light with vertex ao applied +// +// STBVOX_CONFIG_FOG_SMOOTHSTEP +// Defines a simple unrealistic fog system designed to maximize +// unobscured view distance while not looking to weird when things +// emerge from the fog. Configured using an extra array element +// in the STBVOX_UNIFORM_ambient uniform. +// +// STBVOX_CONFIG_FOG +// Defines a fog function hook; you must append a fog function to +// the shader before compiling it: +// vec3 compute_fog(vec3 color, vec3 relative_pos, float fragment_alpha); +// "color" is the incoming pre-fogged color, fragment_alpha is the alpha value, +// and relative_pos is the vector from the point to the camera in worldspace +// +// STBVOX_CONFIG_DISABLE_TEX2 +// This disables all processing of texture 2 in the shader in case +// you don't use it. Eventually this will be replaced with a mode +// that omits the unused data entirely. +// +// STBVOX_CONFIG_TEX1_EDGE_CLAMP +// STBVOX_CONFIG_TEX2_EDGE_CLAMP +// If you want to edge clamp the textures, instead of letting them wrap, +// set this flag. By default stb_voxel_render relies on texture wrapping +// to simplify texture coordinate generation. This flag forces it to do +// it correctly, although there can still be minor artifacts. +// +// STBVOX_CONFIG_ROTATION_IN_LIGHTING +// Changes the meaning of the 'lighting' mesher input variable to also +// store the rotation; see later discussion. +// +// STBVOX_CONFIG_PREMULTIPLIED_ALPHA +// Adjusts the shader calculations on the assumption that tex1.rgba, +// tex2.rgba, and color.rgba all use premultiplied values, and that +// the output of the fragment shader should be premultiplied. +// +// STBVOX_CONFIG_UNPREMULTIPLY +// Only meaningful if STBVOX_CONFIG_PREMULTIPLIED_ALPHA is defined. +// Changes the behavior described above so that the inputs are +// still premultiplied alpha, but the output of the fragment +// shader is not premultiplied alpha. This is needed when allowing +// non-unit alpha values but not doing alpha-blending (for example +// when alpha testing). +// + + ////////////////////////////////////////////////////////////////////////////// // // MESHING // -// A mesh represents a (typically) small chunk of a larger level. +// A mesh represents a (typically) small chunk of a larger world. // Meshes encode coordinates using small integers, so those // coordinates must be relative to some base location. // All of the coordinates in the functions below use @@ -218,8 +334,10 @@ STBVXDEC int stbvox_get_buffer_count(stbvox_mesh_maker *mm); // Returns the number of buffers needed per mesh as described above. STBVXDEC int stbvox_get_buffer_size_per_quad(stbvox_mesh_maker *mm, int slot); -// Returns how much of a given buffer will get used per quad. -// This allows you to choose correct relative sizes for each buffer. +// Returns how much of a given buffer will get used per quad. This +// allows you to choose correct relative sizes for each buffer, although +// the values are fixed based on the configuration you've selected at +// compile time, and the details are described in stbvox_set_buffer. STBVXDEC void stbvox_set_default_mesh(stbvox_mesh_maker *mm, int mesh); // Selects which mesh the mesher will output to (see previous function) @@ -359,7 +477,8 @@ typedef struct stbvox_uniform_info stbvox_uniform_info; STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform); // Gets the information about a uniform necessary for you to // set up each uniform with a minimal amount of explicit code. -// See the sample code for examples. +// See the sample code after the structure definition for stbvox_uniform_info, +// further down in this header section. // // "uniform" is from the list immediately following. For many // of these, default values are provided which you can set. @@ -367,8 +486,6 @@ STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform); // APIs you can set most of the state only once. Only // STBVOX_UNIFORM_transform needs to change per draw call. // -// The info from this function allows you to cal -// // STBVOX_UNIFORM_texscale // 64- or 128-long vec4 array. (128 only if STBVOX_CONFIG_PREFER_TEXBUFFER) // x: scale factor to apply to texture #1. must be a power of two. 1.0 means 'face-sized' @@ -378,7 +495,9 @@ STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform); // // Texscale is indexed by the bottom 6 or 7 bits of the texture id; thus for // example the texture at index 0 in the array and the texture in index 128 of -// the array must be scaled the same. +// the array must be scaled the same. This means that if you only have 64 or 128 +// unique textures, they all get distinct values anyway; otherwise you have +// to group them in pairs or sets of four. // // STBVOX_UNIFORM_ambient // 4-long vec4 array: @@ -390,19 +509,19 @@ STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform); // ambient[3].a - reciprocal of squared distance of farthest fog point (viewing distance) - // +----- has a default value - // | +-- always use the default value -enum // V V -{ // ------------------------------------------------ - STBVOX_UNIFORM_face_data, // n the sampler with the face texture buffer - STBVOX_UNIFORM_transform, // n the transform data from stbvox_get_transform - STBVOX_UNIFORM_tex_array, // n an array of two texture samplers containing the two texture arrays - STBVOX_UNIFORM_texscale, // Y a table of texture properties, see above - STBVOX_UNIFORM_color_table, // Y 64 vec4 RGBA values; a default palette is provided - STBVOX_UNIFORM_normals, // Y Y table of normals, internal-only - STBVOX_UNIFORM_texgen, // Y Y table of texgen vectors, internal-only - STBVOX_UNIFORM_ambient, // n lighting & fog info, see above - STBVOX_UNIFORM_camera_pos, // Y camera position in global voxel space (for lighting & fog) + // +----- has a default value + // | +-- you should always use the default value +enum // V V +{ // ------------------------------------------------ + STBVOX_UNIFORM_face_data, // n the sampler with the face texture buffer + STBVOX_UNIFORM_transform, // n the transform data from stbvox_get_transform + STBVOX_UNIFORM_tex_array, // n an array of two texture samplers containing the two texture arrays + STBVOX_UNIFORM_texscale, // Y a table of texture properties, see above + STBVOX_UNIFORM_color_table, // Y 64 vec4 RGBA values; a default palette is provided; if A > 1.0, fullbright + STBVOX_UNIFORM_normals, // Y Y table of normals, internal-only + STBVOX_UNIFORM_texgen, // Y Y table of texgen vectors, internal-only + STBVOX_UNIFORM_ambient, // n lighting & fog info, see above + STBVOX_UNIFORM_camera_pos, // Y camera position in global voxel space (for lighting & fog) STBVOX_UNIFORM_count, }; @@ -426,6 +545,59 @@ struct stbvox_uniform_info int use_tex_buffer; // if true, then the uniform is a sampler but the data can come from default_value }; +////////////////////////////////////////////////////////////////////////////// +// +// Uniform sample code +// + +#if 0 +// Run this once per frame before drawing all the meshes. +// You still need to set the 'transform' uniform for every mesh, etc. +void setup_uniforms(GLuint shader, float camera_pos[4], GLuint tex1, GLuint tex2) +{ + int i; + glUseProgram(shader); // so uniform binding works + for (i=0; i < STBVOX_UNIFORM_count; ++i) { + stbvox_uniform_info sui; + if (stbvox_get_uniform_info(&sui, i)) { + GLint loc = glGetUniformLocation(shader, sui.name); + if (loc != 0) { + switch (i) { + case STBVOX_UNIFORM_camera_pos: // only needed for fog + glUniform4fv(loc, sui.array_length, camera_pos); + break; + + case STBVOX_UNIFORM_tex_array: { + GLuint tex_unit[2] = { 0, 1 }; // your choice of samplers + glUniform1iv(loc, 2, tex_unit); + + glActiveTexture(GL_TEXTURE0 + tex_unit[0]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex1); + glActiveTexture(GL_TEXTURE0 + tex_unit[1]); glBindTexture(GL_TEXTURE_2D_ARRAY, tex2); + glActiveTexture(GL_TEXTURE0); // reset to default + break; + } + + case STBVOX_UNIFORM_face_data: + glUniform1i(loc, SAMPLER_YOU_WILL_BIND_PER_MESH_FACE_DATA_TO); + break; + + case STBVOX_UNIFORM_ambient: // you definitely want to override this + case STBVOX_UNIFORM_color_table: // you might want to override this + case STBVOX_UNIFORM_texscale: // you may want to override this + glUniform4fv(loc, sui.array_length, sui.default_value); + break; + + case STBVOX_UNIFORM_normals: // you never want to override this + case STBVOX_UNIFORM_texgen: // you never want to override this + glUniform3fv(loc, sui.array_length, sui.default_value); + break; + } + } + } + } +} +#endif + #ifdef __cplusplus } #endif @@ -475,43 +647,68 @@ enum STBVOX_FACE_count, }; -// 24-bit color -typedef struct -{ - unsigned char r,g,b; -} stbvox_rgb; - #ifdef STBVOX_CONFIG_BLOCKTYPE_SHORT typedef unsigned short stbvox_block_type; #else typedef unsigned char stbvox_block_type; #endif +// 24-bit color +typedef struct +{ + unsigned char r,g,b; +} stbvox_rgb; + #define STBVOX_COLOR_TEX1_ENABLE 64 #define STBVOX_COLOR_TEX2_ENABLE 128 -// This is the data structure you fill out +// This is the data structure you fill out. Most of the arrays can be +// NULL, except when one is required to get the value to index another. struct stbvox_input_description { unsigned char lighting_at_vertices; // The default is lighting values (i.e. ambient occlusion) are at block - // center, and the vertex light is gathered from the adjacent block - // centers the vertex faces. This makes smooth lighting consistent - // on adjacent faces with the same orientation. + // center, and the vertex light is gathered from those adjacent block + // centers that the vertex is facing. This makes smooth lighting + // consistent across adjacent faces with the same orientation. // // Setting this flag to non-zero gives you explicit control - // of light at each vertex; now the lighting/ao will be shared by - // all vertices at the same point, even if they have different normals. + // of light at each vertex, but now the lighting/ao will be + // shared by all vertices at the same point, even if they + // have different normals. - // these are 3D maps you use to define your voxel world, using x_stride and y_stride + // these are mostly 3D maps you use to define your voxel world, using x_stride and y_stride // note that for cache efficiency, you want to use the block_foo palettes as much as possible instead stbvox_rgb *rgb; + // Indexed by 3D coordinate. // 24-bit voxel color for STBVOX_CONFIG_MODE = 20 or 21 only - stbvox_block_type *blocktype; // index into palettes listed below - // This is a core "block type" value, which is used to index into - // other arrays. + unsigned char *lighting; + // Indexed by 3D coordinate. The lighting value / ambient occlusion + // value that is used to define the vertex lighting values. + // The raw lighting values are defined at the center of blocks + // (or at vertex if 'lighting_at_vertices' is true). + // + // If the macro STBVOX_ROTATION_IN_LIGHTING is defined, + // then an additional 2-bit block rotation value is stored + // in this field as well. + // + // Encode with STBVOX_MAKE_LIGHTING(lighting,rot)--here + // 'lighting' should still be 8 bits, as the macro will + // discard the bottom bits automatically. + // + // (Rationale: rotation needs to + // be independent of blocktype, but is only 2 bits so + // doesn't want to be its own array. Lighting is the one + // thing that was likely to already be in use and that + // I could easily steal 2 bits from.) + + stbvox_block_type *blocktype; + // Indexed by 3D coordinate. This is a core "block type" value, which is used + // to index into other arrays; essentially a "palette". This is much more + // memory-efficient and performance-friendly than storing the values explicitly, + // but only makes sense if the values are always synchronized. // // If a voxel's blocktype is 0, it is assumed to be empty (STBVOX_GEOM_empty), // and no other blocktypes should be STBVOX_GEOM_empty. (Only if you do not @@ -520,6 +717,13 @@ struct stbvox_input_description // Normally it is an unsigned byte, but you can override it to be // a short if you have too many blocktypes. + unsigned char *geometry; + // Indexed by 3D coordinate. Contains the geometry type for the block. + // Also contains a 2-bit rotation for how the whole block is rotated. + // Also includes a 2-bit vheight value when using shared vheight values. + // See the separate vheight documentation. + // Encode with STBVOX_MAKE_GEOMETRY(geom, rot, vheight) + unsigned char *block_geometry; // Array indexed by blocktype containing the geometry for this block, plus // a 2-bit "simple rotation". Note rotation has limited use since it's not @@ -534,6 +738,12 @@ struct stbvox_input_description // Array indexed by blocktype and face containing the texture id for texture #1. // The N/E/S/W face choices can be rotated by one of the rotation selectors; // The top & bottom face textures will rotate to match. + // Note that it only makes sense to use one of block_tex1 or block_tex1_face; + // this pattern repeats throughout and this notice is not repeated. + + unsigned char *tex2; + // Indexed by 3D coordinate. Contains the texture id for texture #2 + // to use on all faces of the block. unsigned char *block_tex2; // Array indexed by blocktype containing the texture id for texture #2. @@ -543,6 +753,11 @@ struct stbvox_input_description // The N/E/S/W face choices can be rotated by one of the rotation selectors; // The top & bottom face textures will rotate to match. + unsigned char *color; + // Indexed by 3D coordinate. Contains the color for all faces of the block. + // The core color value is 0..63. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + unsigned char *block_color; // Array indexed by blocktype containing the color value to apply to the faces. // The core color value is 0..63. @@ -565,69 +780,166 @@ struct stbvox_input_description unsigned char *block_vheight; // Array indexed by blocktype containing the vheight values for the // top or bottom face of this block. These will rotate properly if the - // block is rotated. + // block is rotated. See discussion of vheight. // Encode with STBVOX_MAKE_VHEIGHT(sw_height, se_height, nw_height, ne_height) + unsigned char *selector; + // Array indexed by 3D coordinates indicating which output mesh to select. + unsigned char *block_selector; // Array indexed by blocktype indicating which output mesh to select. + unsigned char *side_texrot; + // Array indexed by 3D coordinates encoding 2-bit texture rotations for the + // faces on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + unsigned char *block_side_texrot; - // Array indexed by blocktype encoding 2-bin texture rotations for the faces + // Array indexed by blocktype encoding 2-bit texture rotations for the faces // on the E/N/W/S sides of the block. // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) + unsigned char *overlay; // index into palettes listed below + // Indexed by 3D coordinate. If 0, there is no overlay. If non-zero, + // it indexes into to the below arrays and overrides the values + // defined by the blocktype. -////////////////////////////////////////////////////////////////////////////// -// X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X// -//X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X // -// X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X// -//X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X // -// X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X// -//X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X // -// X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X// -//X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X // -////////////////////////////////////////////////////////////////////////////// + unsigned char (*overlay_tex1)[6]; + // Array indexed by overlay value and face, containing an override value + // for the texture id for texture #1. If 0, the value defined by blocktype + // is used. + + unsigned char (*overlay_tex2)[6]; + // Array indexed by overlay value and face, containing an override value + // for the texture id for texture #2. If 0, the value defined by blocktype + // is used. - // Note the detailed documentation runs out here, I still have to finish this - // and document all the #define STBVOX_CONFIGs as well + unsigned char (*overlay_color)[6]; + // Array indexed by overlay value and face, containing an override value + // for the face color. If 0, the value defined by blocktype is used. + unsigned char *overlay_side_texrot; + // Array indexed by overlay value, encoding 2-bit texture rotations for the faces + // on the E/N/W/S sides of the block. + // Encode with STBVOX_MAKE_SIDE_TEXROT(rot_e, rot_n, rot_w, rot_s) - unsigned char *overlay; // index into palettes listed below - unsigned char *selector; // raw selector (chooses which mesh to write to) - unsigned char *geometry; // STBVOX_MAKE_GEOMETRY -- geom:4, rot:2, vheight:2 - unsigned char *rotate; // STBVOX_MAKE_MATROT -- block:2, overlay:2, tex2:2, color:2 - 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 - unsigned char *texlerp_simple; // STBVOX_MAKE_TEXLERP_SIMPLE -- baselerp:2, vert_lerp:3, face_to_use_vert_lerp:3 - unsigned short *texlerp_vert3; // e:3,n:3,w:3,s:3,u:3 (down comes from 'texlerp') - unsigned short *texlerp_face3; // e:3,n:3,w:3,s:3,u:2,d:2 - unsigned char *lighting; // lighting:8 - unsigned char *color; // color for entire block - unsigned char *extended_color; // index into ecolor palettes - unsigned char *color2, *color2_facemask;// additional override colors with face bitmask - unsigned char *color3, *color3_facemask;// additional override colors with face bitmask + unsigned char *rotate; + // Indexed by 3D coordinate. Allows independent rotation of several + // parts of the voxel, where by rotation I mean swapping textures + // and colors between E/N/S/W faces. + // Block: rotates anything indexed by blocktype + // Overlay: rotates anything indexed by overlay + // EColor: rotates faces defined in ecolor_facemask + // Encode with STBVOX_MAKE_MATROT(block,overlay,ecolor) + + unsigned char *tex2_for_tex1; + // Array indexed by tex1 containing the texture id for texture #2. + // You can use this if the two are always/almost-always strictly + // correlated (e.g. if tex2 is a detail texture for tex1), as it + // will be more efficient (touching fewer cache lines) than using + // e.g. block_tex2_face. + + unsigned char *tex2_replace; + // Indexed by 3D coordinate. Specifies the texture id for texture #2 + // to use on a single face of the voxel, which must be E/N/W/S (not U/D). + // The texture id is limited to 6 bits unless tex2_facemask is also + // defined (see below). + // Encode with STBVOX_MAKE_TEX2_REPLACE(tex2, face) + + unsigned char *tex2_facemask; + // Indexed by 3D coordinate. Specifies which of the six faces should + // have their tex2 replaced by the value of tex2_replace. In this + // case, all 8 bits of tex2_replace are used as the texture id. + // Encode with STBVOX_MAKE_FACE_MASK(east,north,west,south,up,down) + + unsigned char *extended_color; + // Indexed by 3D coordinate. Specifies a value that indexes into + // the ecolor arrays below (both of which must be defined). + + unsigned char *ecolor_color; + // Indexed by extended_color value, specifies an optional override + // for the color value on some faces. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - // indexed by tex1, used to determine tex2 if not otherwise specified - unsigned char *tex2_for_tex1; // 256 + unsigned char *ecolor_facemask; + // Indexed by extended_color value, this specifies which faces the + // color in ecolor_color should be applied to. The faces can be + // independently rotated by the ecolor value of 'rotate', if it exists. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) - // @TODO: when specializing, build a single struct with all of the - // below values, so it's AoS instead of SoA for better cache efficiency + unsigned char *color2; + // Indexed by 3D coordinates, specifies an alternative color to apply + // to some of the faces of the block. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) - // 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; + unsigned char *color2_facemask; + // Indexed by 3D coordinates, specifies which faces should use the + // color defined in color2. No rotation value is applied. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *color3; + // Indexed by 3D coordinates, specifies an alternative color to apply + // to some of the faces of the block. + // Encode with STBVOX_MAKE_COLOR(color_number, tex1_enable, tex2_enable) + + unsigned char *color3_facemask; + // Indexed by 3D coordinates, specifies which faces should use the + // color defined in color3. No rotation value is applied. + // Encode with STBVOX_MAKE_FACE_MASK(e,n,w,s,u,d) + + unsigned char *texlerp_simple; + // Indexed by 3D coordinates, this is the smallest texlerp encoding + // that can do useful work. It consits of three values: baselerp, + // vertlerp, and face_vertlerp. Baselerp defines the value + // to use on all of the faces but one, from the STBVOX_TEXLERP_BASE + // values. face_vertlerp is one of the 6 face values (or STBVOX_FACE_NONE) + // which specifies the face should use the vertlerp values. + // Vertlerp defines a lerp value at every vertex of the mesh. + // Thus, one face can have per-vertex texlerp values, and those + // values are encoded in the space so that they will be shared + // by adjacent faces that also use vertlerp, allowing continuity + // (this is used for the "texture crossfade" bit of the release video). + // Encode with STBVOX_MAKE_TEXLERP_SIMPLE(baselerp, vertlerp, face_vertlerp) + + // The following texlerp encodings are experimental and maybe not + // that useful. + + unsigned char *texlerp; + // Indexed by 3D coordinates, this defines four values: + // vertlerp is a lerp value at every vertex of the mesh (using STBVOX_TEXLERP_BASE values). + // ud is the value to use on up and down faces, from STBVOX_TEXLERP_FACE values + // ew is the value to use on east and west faces, from STBVOX_TEXLERP_FACE values + // ns is the value to use on north and south faces, from STBVOX_TEXLERP_FACE values + // If any of ud, ew, or ns is STBVOX_TEXLERP_FACE_use_vert, then the + // vertlerp values for the vertices are gathered and used for those faces. + // Encode with STBVOX_MAKE_TEXLERP(vertlerp,ud,ew,sw) + + unsigned short *texlerp_vert3; + // Indexed by 3D coordinates, this works with texlerp and + // provides a unique texlerp value for every direction at + // every vertex. The same rules of whether faces share values + // applies. The STBVOX_TEXLERP_FACE vertlerp value defined in + // texlerp is only used for the down direction. The values at + // each vertex in other directions are defined in this array, + // and each uses the STBVOX_TEXLERP3 values (i.e. full precision + // 3-bit texlerp values). + // Encode with STBVOX_MAKE_VERT3(vertlerp_e,vertlerp_n,vertlerp_w,vertlerp_s,vertlerp_u) + + unsigned short *texlerp_face3; // e:3,n:3,w:3,s:3,u:2,d:2 + // Indexed by 3D coordinates, this provides a compact way to + // fully specify the texlerp value indepenendly for every face, + // but doesn't allow per-vertex variation. E/N/W/S values are + // encoded using STBVOX_TEXLERP3 values, whereas up and down + // use STBVOX_TEXLERP_SIMPLE values. + // Encode with STBVOX_MAKE_FACE3(face_e,face_n,face_w,face_s,face_u,face_d) - // indexed by extended_color - unsigned char *ecolor_color; // 256 - unsigned char *ecolor_facemask; // 256 + unsigned char *vheight; // STBVOX_MAKE_VHEIGHT -- sw:2, se:2, nw:2, ne:2, doesn't rotate + // Indexed by 3D coordinates, this defines the four + // vheight values to use if the geometry is STBVOX_GEOM_vheight*. + // See the vheight discussion. }; +// @OPTIMIZE when specializing, build a single struct with all of the +// 3D-indexed so it's AoS instead of SoA for better cache efficiency enum @@ -639,10 +951,10 @@ enum enum { - STBVOX_TEXLERP_0, - STBVOX_TEXLERP_half, - STBVOX_TEXLERP_1, - STBVOX_TEXLERP_use_vert, + STBVOX_TEXLERP_FACE_0, + STBVOX_TEXLERP_FACE_half, + STBVOX_TEXLERP_FACE_1, + STBVOX_TEXLERP_FACE_use_vert, }; enum @@ -677,7 +989,7 @@ enum #define STBVOX_MAKE_GEOMETRY(geom, rotate, vheight) ((geom) + (rotate)*16 + (vheight)*64) #define STBVOX_MAKE_VHEIGHT(v_sw, v_se, v_nw, v_ne) ((v_sw) + (v_se)*4 + (v_nw)*16 + (v_ne)*64) -#define STBVOX_MAKE_MATROT(block, overlay, tex2, color) ((block) + (overlay)*4 + (tex2)*16 + (color)*64) +#define STBVOX_MAKE_MATROT(block, overlay, color) ((block) + (overlay)*4 + (color)*64) #define STBVOX_MAKE_TEX2_REPLACE(tex2, tex2_replace_face) ((tex2) + ((tex2_replace_face) & 3)*64) #define STBVOX_MAKE_TEXLERP(ns2, ew2, ud2, vert) ((ew2) + (ns2)*4 + (ud2)*16 + (vert)*64) #define STBVOX_MAKE_TEXLERP_SIMPLE(baselerp,vert,face) ((vert)*32 + (face)*4 + (baselerp)) @@ -686,6 +998,8 @@ enum #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) #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) #ifdef STBVOX_ROTATION_IN_LIGHTING #define STBVOX_MAKE_LIGHTING(lighting, rot) (((lighting)&~3)+(rot)) @@ -947,7 +1261,7 @@ static float stbvox_default_ambient[4][4] = { 0,0,1 ,0 }, // reversed lighting direction { 0.5,0.5,0.5,0 }, // directional color { 0.5,0.5,0.5,0 }, // constant color - { 0.5,0.5,0.5,1.0f/1000.0f }, // fog data for simple_fog + { 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]; @@ -1036,7 +1350,7 @@ static char *stbvox_vertex_program = " amb_occ = float( (attr_vertex >> 23u) & 63u ) / 63.0;\n" // a[23..28] " texlerp = float( (attr_vertex >> 29u) ) / 7.0;\n" // a[29..31] - " vnormal = normal_table[(facedata.w>>2) & 31u];\n" + " vnormal = normal_table[(facedata.w>>2u) & 31u];\n" " voxelspace_pos = offset * transform[0];\n" // mesh-to-object scale " vec3 position = voxelspace_pos + transform[1];\n" // mesh-to-object translate @@ -1428,11 +1742,10 @@ STBVXDEC int stbvox_get_uniform_info(stbvox_uniform_info *info, int uniform) typedef struct { - unsigned char block; - unsigned char overlay; - unsigned char facerot:4; - unsigned char ecolor:4; - unsigned char tex2; + unsigned char block:2; + unsigned char overlay:2; + unsigned char facerot:2; + unsigned char ecolor:2; } stbvox_rotate; typedef struct @@ -1505,24 +1818,26 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro if (mm->input.overlay) { int over_face = STBVOX_ROTATE(face, rot.overlay); unsigned char over = mm->input.overlay[v_off]; - if (mm->input.overlay_tex1) { - unsigned char rep1 = mm->input.overlay_tex1[over][over_face]; - if (rep1) - face_data.tex1 = rep1; - } - if (mm->input.overlay_tex2) { - unsigned char rep2 = mm->input.overlay_tex1[over][over_face]; - if (rep2) - face_data.tex2 = rep2; - } - if (mm->input.overlay_color) { - unsigned char rep3 = mm->input.overlay_color[over][over_face]; - if (rep3) - face_data.color = rep3; - } + if (over) { + if (mm->input.overlay_tex1) { + unsigned char rep1 = mm->input.overlay_tex1[over][over_face]; + if (rep1) + face_data.tex1 = rep1; + } + if (mm->input.overlay_tex2) { + unsigned char rep2 = mm->input.overlay_tex1[over][over_face]; + if (rep2) + face_data.tex2 = rep2; + } + if (mm->input.overlay_color) { + unsigned char rep3 = mm->input.overlay_color[over][over_face]; + if (rep3) + face_data.color = rep3; + } - if (face <= STBVOX_FACE_south) - facerot = mm->input.overlay_side_texrot[over] >> (2*over_face); + if (mm->input.overlay_side_texrot && face <= STBVOX_FACE_south) + facerot = mm->input.overlay_side_texrot[over] >> (2*over_face); + } } if (mm->input.tex2_for_tex1) @@ -1530,8 +1845,7 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro if (mm->input.tex2) face_data.tex2 = mm->input.tex2[v_off]; if (mm->input.tex2_replace) { - int tex2_face = STBVOX_ROTATE(face, rot.tex2); - if (mm->input.tex2_facemask[v_off] & (1 << tex2_face)) + if (mm->input.tex2_facemask[v_off] & (1 << face)) face_data.tex2 = mm->input.tex2_replace[v_off]; } @@ -1555,11 +1869,11 @@ stbvox_mesh_face stbvox_compute_mesh_face_value(stbvox_mesh_maker *mm, stbvox_ro } static unsigned char stbvox_face_lerp[6] = { 0,2,0,2,4,4 }; -static unsigned char stbvox_vert3_lerp[6] = { 0,3,6,9,12,12 }; +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_face3_updown[8] = { 0,2,4,7,0,2,4,7 }; 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]; @@ -1615,7 +1929,7 @@ 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,val); } else if (mm->input.texlerp_face3) { unsigned char val = (mm->input.texlerp_face3[v_off] >> stbvox_face3_lerp[face]) & 7; - if (face >= 4) + 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) { @@ -1636,7 +1950,7 @@ void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int fac } } else if (mm->input.texlerp) { unsigned char facelerp = (mm->input.texlerp[v_off] >> stbvox_face_lerp[face]) & 3; - if (facelerp == STBVOX_TEXLERP_use_vert) { + 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; @@ -1644,10 +1958,10 @@ void stbvox_make_mesh_for_face(stbvox_mesh_maker *mm, stbvox_rotate rot, int fac 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_face_lerp[mm->input.texlerp[mm->cube_vertex_offset[face][0]]>>6]; - p1[1] = stbvox_vert_lerp_for_face_lerp[mm->input.texlerp[mm->cube_vertex_offset[face][1]]>>6]; - p1[2] = stbvox_vert_lerp_for_face_lerp[mm->input.texlerp[mm->cube_vertex_offset[face][2]]>>6]; - p1[3] = stbvox_vert_lerp_for_face_lerp[mm->input.texlerp[mm->cube_vertex_offset[face][3]]>>6]; + 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]); @@ -1785,7 +2099,7 @@ static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, in 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,0 }; + stbvox_rotate rot = { 0,0,0,0 }; unsigned char simple_rot = 0; unsigned char mesh = mm->default_mesh; @@ -1816,10 +2130,10 @@ static void stbvox_make_mesh_for_block(stbvox_mesh_maker *mm, stbvox_pos pos, in unsigned char val = mm->input.rotate[v_off]; rot.block = (val >> 0) & 3; rot.overlay = (val >> 2) & 3; - rot.tex2 = (val >> 4) & 3; + //rot.tex2 = (val >> 4) & 3; rot.ecolor = (val >> 6) & 3; } else { - rot.block = rot.overlay = rot.tex2 = rot.ecolor = simple_rot; + rot.block = rot.overlay = rot.ecolor = simple_rot; } rot.facerot = 0; @@ -2044,7 +2358,7 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po // 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,0 }; + 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 @@ -2094,10 +2408,10 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po unsigned char val = mm->input.rotate[v_off]; rotate.block = (val >> 0) & 3; rotate.overlay = (val >> 2) & 3; - rotate.tex2 = (val >> 4) & 3; + //rotate.tex2 = (val >> 4) & 3; rotate.ecolor = (val >> 6) & 3; } else { - rotate.block = rotate.overlay = rotate.tex2 = rotate.ecolor = simple_rot; + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; } rotate.facerot = 0; @@ -2119,7 +2433,7 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po stbvox_mesh_vertex vmesh[6][4]; stbvox_mesh_vertex cube[8]; stbvox_mesh_vertex basevert; - stbvox_rotate rotate = { 0,0,0,0,0 }; + stbvox_rotate rotate = { 0,0,0,0 }; unsigned char simple_rot = rot; unsigned char ht[4]; int extreme; @@ -2237,10 +2551,10 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po unsigned char val = mm->input.rotate[v_off]; rotate.block = (val >> 0) & 3; rotate.overlay = (val >> 2) & 3; - rotate.tex2 = (val >> 4) & 3; + //rotate.tex2 = (val >> 4) & 3; rotate.ecolor = (val >> 6) & 3; } else if (mm->input.selector) { - rotate.block = rotate.overlay = rotate.tex2 = rotate.ecolor = simple_rot; + rotate.block = rotate.overlay = rotate.ecolor = simple_rot; } if ((visible_faces & (1 << STBVOX_FACE_north)) || (extreme && (ht[2] == 3 || ht[3] == 3))) @@ -2257,7 +2571,7 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po // 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,0 }; + stbvox_rotate rot = { 0,0,0,0 }; unsigned char mesh = mm->default_mesh; if (mm->input.selector) { mesh = mm->input.selector[v_off]; @@ -2275,10 +2589,10 @@ static void stbvox_make_mesh_for_block_with_geo(stbvox_mesh_maker *mm, stbvox_po unsigned char val = mm->input.rotate[v_off]; rot.block = (val >> 0) & 3; rot.overlay = (val >> 2) & 3; - rot.tex2 = (val >> 4) & 3; + //rot.tex2 = (val >> 4) & 3; rot.ecolor = (val >> 6) & 3; } else if (mm->input.selector) { - rot.block = rot.overlay = rot.tex2 = rot.ecolor = simple_rot; + rot.block = rot.overlay = rot.ecolor = simple_rot; } rot.facerot = 0;