From d4e0c0f9b818120286369b33b1bdb5eb6ece7a83 Mon Sep 17 00:00:00 2001 From: number Zero Date: Mon, 13 Feb 2017 19:31:43 +0300 Subject: [PATCH] Content_mapblock.cpp: Refactor --- src/content_mapblock.cpp | 3158 +++++++++++++----------------------- src/content_mapblock.h | 124 +- src/mapblock_mesh.cpp | 5 +- src/nodedef.h | 18 + src/util/directiontables.h | 56 +- 5 files changed, 1358 insertions(+), 2003 deletions(-) diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 9923647bc..18b4ef6dd 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_mapblock.h" #include "util/numeric.h" #include "util/directiontables.h" -#include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector +#include "mapblock_mesh.h" #include "settings.h" #include "nodedef.h" #include "client/tile.h" @@ -34,12 +34,13 @@ with this program; if not, write to the Free Software Foundation, Inc., // After this distance, it gives up and considers light level constant #define SMOOTH_LIGHTING_OVERSIZE 1.0 -struct LightFrame -{ - f32 lightsA[8]; - f32 lightsB[8]; - u8 light_source; -}; +// Node edge count (for glasslike-framed) +#define FRAMED_EDGE_COUNT 12 + +// Node neighbor count, including edge-connected, but not vertex-connected +// (for glasslike-framed) +// Corresponding offsets are listed in g_27dirs +#define FRAMED_NEIGHBOR_COUNT 18 static const v3s16 light_dirs[8] = { v3s16(-1, -1, -1), @@ -52,189 +53,135 @@ static const v3s16 light_dirs[8] = { v3s16( 1, 1, 1), }; -// Create a cuboid. -// collector - the MeshCollector for the resulting polygons -// box - the position and size of the box -// tiles - the tiles (materials) to use (for all 6 faces) -// tilecount - number of entries in tiles, 1<=tilecount<=6 -// c - colors of the cuboid's six sides -// txc - texture coordinates - this is a list of texture coordinates -// for the opposite corners of each face - therefore, there -// should be (2+2)*6=24 values in the list. Alternatively, -// pass NULL to use the entire texture for each face. The -// order of the faces in the list is up-down-right-left-back- -// front (compatible with ContentFeatures). If you specified -// 0,0,1,1 for each face, that would be the same as -// passing NULL. -// light source - if greater than zero, the box's faces will not be shaded -void makeCuboid(MeshCollector *collector, const aabb3f &box, - TileSpec *tiles, int tilecount, const video::SColor *c, - const f32* txc, const u8 light_source) +// Standard index set to make a quad on 4 vertices +static const u16 quad_indices[] = {0, 1, 2, 2, 3, 0}; + +const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike"; + +MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output) { - assert(tilecount >= 1 && tilecount <= 6); // pre-condition + data = input; + collector = output; - v3f min = box.MinEdge; - v3f max = box.MaxEdge; + nodedef = data->m_client->ndef(); + smgr = data->m_client->getSceneManager(); + meshmanip = smgr->getMeshManipulator(); - if(txc == NULL) { - static const f32 txc_default[24] = { - 0,0,1,1, - 0,0,1,1, - 0,0,1,1, - 0,0,1,1, - 0,0,1,1, - 0,0,1,1 - }; - txc = txc_default; - } - - video::SColor c1 = c[0]; - video::SColor c2 = c[1]; - video::SColor c3 = c[2]; - video::SColor c4 = c[3]; - video::SColor c5 = c[4]; - video::SColor c6 = c[5]; - if (!light_source) { - applyFacesShading(c1, v3f(0, 1, 0)); - applyFacesShading(c2, v3f(0, -1, 0)); - applyFacesShading(c3, v3f(1, 0, 0)); - applyFacesShading(c4, v3f(-1, 0, 0)); - applyFacesShading(c5, v3f(0, 0, 1)); - applyFacesShading(c6, v3f(0, 0, -1)); - } - - video::S3DVertex vertices[24] = - { - // up - video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c1, txc[0],txc[1]), - video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c1, txc[2],txc[1]), - video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c1, txc[2],txc[3]), - video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c1, txc[0],txc[3]), - // down - video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c2, txc[4],txc[5]), - video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c2, txc[6],txc[5]), - video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c2, txc[6],txc[7]), - video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c2, txc[4],txc[7]), - // right - video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c3, txc[ 8],txc[9]), - video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c3, txc[10],txc[9]), - video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c3, txc[10],txc[11]), - video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c3, txc[ 8],txc[11]), - // left - video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c4, txc[12],txc[13]), - video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c4, txc[14],txc[13]), - video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c4, txc[14],txc[15]), - video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c4, txc[12],txc[15]), - // back - video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c5, txc[16],txc[17]), - video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c5, txc[18],txc[17]), - video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c5, txc[18],txc[19]), - video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c5, txc[16],txc[19]), - // front - video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c6, txc[20],txc[21]), - video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c6, txc[22],txc[21]), - video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c6, txc[22],txc[23]), - video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c6, txc[20],txc[23]), - }; - - for(int i = 0; i < 6; i++) - { - switch (tiles[MYMIN(i, tilecount-1)].rotation) - { - case 0: - break; - case 1: //R90 - for (int x = 0; x < 4; x++) - vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); - break; - case 2: //R180 - for (int x = 0; x < 4; x++) - vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0)); - break; - case 3: //R270 - for (int x = 0; x < 4; x++) - vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); - break; - case 4: //FXR90 - for (int x = 0; x < 4; x++){ - vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; - vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); - } - break; - case 5: //FXR270 - for (int x = 0; x < 4; x++){ - vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; - vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); - } - break; - case 6: //FYR90 - for (int x = 0; x < 4; x++){ - vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; - vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); - } - break; - case 7: //FYR270 - for (int x = 0; x < 4; x++){ - vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; - vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); - } - break; - case 8: //FX - for (int x = 0; x < 4; x++){ - vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; - } - break; - case 9: //FY - for (int x = 0; x < 4; x++){ - vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; - } - break; - default: - break; - } - } - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - for (s32 j = 0; j < 24; j += 4) { - int tileindex = MYMIN(j / 4, tilecount - 1); - collector->append(tiles[tileindex], vertices + j, 4, indices, 6); + enable_mesh_cache = g_settings->getBool("enable_mesh_cache") && + !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting + + blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; +} + +void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling) +{ + tile = getNodeTileN(n, p, index, data); + if (!data->m_smooth_lighting) + color = encode_light_and_color(light, tile.color, f->light_source); + if (disable_backface_culling) + tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; + tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; +} + +void MapblockMeshGenerator::useDefaultTile(bool set_color) +{ + tile = getNodeTile(n, p, v3s16(0, 0, 0), data); + if (set_color && !data->m_smooth_lighting) + color = encode_light_and_color(light, tile.color, f->light_source); +} + +TileSpec MapblockMeshGenerator::getTile(const v3s16& direction) +{ + return getNodeTile(n, p, direction, data); +} + +void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal) +{ + static const v2f tcoords[4] = {v2f(0, 1), v2f(1, 1), v2f(1, 0), v2f(0, 0)}; + video::S3DVertex vertices[4]; + bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0)); + v3f normal2(normal.X, normal.Y, normal.Z); + for (int j = 0; j < 4; j++) { + vertices[j].Pos = coords[j] + origin; + vertices[j].Normal = normal2; + if (data->m_smooth_lighting) + vertices[j].Color = blendLight(coords[j], tile.color); + else + vertices[j].Color = color; + if (shade_face) + applyFacesShading(vertices[j].Color, normal2); + vertices[j].TCoords = tcoords[j]; } + collector->append(tile, vertices, 4, quad_indices, 6); } // Create a cuboid. -// collector - the MeshCollector for the resulting polygons -// box - the position and size of the box // tiles - the tiles (materials) to use (for all 6 faces) // tilecount - number of entries in tiles, 1<=tilecount<=6 -// lights - vertex light levels. The order is the same as in light_dirs +// lights - vertex light levels. The order is the same as in light_dirs. +// NULL may be passed if smooth lighting is disabled. // txc - texture coordinates - this is a list of texture coordinates // for the opposite corners of each face - therefore, there -// should be (2+2)*6=24 values in the list. Alternatively, pass -// NULL to use the entire texture for each face. The order of +// should be (2+2)*6=24 values in the list. The order of // the faces in the list is up-down-right-left-back-front -// (compatible with ContentFeatures). If you specified 0,0,1,1 -// for each face, that would be the same as passing NULL. -// light_source - node light emission -static void makeSmoothLightedCuboid(MeshCollector *collector, const aabb3f &box, - TileSpec *tiles, int tilecount, const u16 *lights , const f32 *txc, - const u8 light_source) +// (compatible with ContentFeatures). +void MapblockMeshGenerator::drawCuboid(const aabb3f &box, + TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc) { assert(tilecount >= 1 && tilecount <= 6); // pre-condition v3f min = box.MinEdge; v3f max = box.MaxEdge; - if (txc == NULL) { - static const f32 txc_default[24] = { - 0,0,1,1, - 0,0,1,1, - 0,0,1,1, - 0,0,1,1, - 0,0,1,1, - 0,0,1,1 - }; - txc = txc_default; + video::SColor colors[6]; + if (!data->m_smooth_lighting) { + for (int face = 0; face != 6; ++face) { + int tileindex = MYMIN(face, tilecount - 1); + colors[face] = encode_light_and_color(light, tiles[tileindex].color, f->light_source); + } + if (!f->light_source) { + applyFacesShading(colors[0], v3f(0, 1, 0)); + applyFacesShading(colors[1], v3f(0, -1, 0)); + applyFacesShading(colors[2], v3f(1, 0, 0)); + applyFacesShading(colors[3], v3f(-1, 0, 0)); + applyFacesShading(colors[4], v3f(0, 0, 1)); + applyFacesShading(colors[5], v3f(0, 0, -1)); + } } + + video::S3DVertex vertices[24] = { + // top + video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]), + video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]), + video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]), + video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]), + // bottom + video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]), + video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]), + video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]), + video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]), + // right + video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]), + video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]), + video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]), + video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]), + // left + video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]), + video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]), + video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]), + video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]), + // back + video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]), + video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]), + video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]), + video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]), + // front + video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]), + video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]), + video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]), + video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]), + }; + static const u8 light_indices[24] = { 3, 7, 6, 2, 0, 4, 5, 1, @@ -243,152 +190,83 @@ static void makeSmoothLightedCuboid(MeshCollector *collector, const aabb3f &box, 7, 3, 1, 5, 2, 6, 4, 0 }; - video::S3DVertex vertices[24] = { - // up - video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, video::SColor(), txc[0], txc[1]), - video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, video::SColor(), txc[2], txc[1]), - video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, video::SColor(), txc[2], txc[3]), - video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, video::SColor(), txc[0], txc[3]), - // down - video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, video::SColor(), txc[4], txc[5]), - video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, video::SColor(), txc[6], txc[5]), - video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, video::SColor(), txc[6], txc[7]), - video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, video::SColor(), txc[4], txc[7]), - // right - video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, video::SColor(), txc[ 8], txc[9]), - video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, video::SColor(), txc[10], txc[9]), - video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, video::SColor(), txc[10], txc[11]), - video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, video::SColor(), txc[ 8], txc[11]), - // left - video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, video::SColor(), txc[12], txc[13]), - video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, video::SColor(), txc[14], txc[13]), - video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, video::SColor(), txc[14], txc[15]), - video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, video::SColor(), txc[12], txc[15]), - // back - video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, video::SColor(), txc[16], txc[17]), - video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, video::SColor(), txc[18], txc[17]), - video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, video::SColor(), txc[18], txc[19]), - video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, video::SColor(), txc[16], txc[19]), - // front - video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, video::SColor(), txc[20], txc[21]), - video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, video::SColor(), txc[22], txc[21]), - video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, video::SColor(), txc[22], txc[23]), - video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, video::SColor(), txc[20], txc[23]), - }; - for(int i = 0; i < 6; i++) { - switch (tiles[MYMIN(i, tilecount-1)].rotation) { - case 0: - break; - case 1: //R90 - for (int x = 0; x < 4; x++) - vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); - break; - case 2: //R180 - for (int x = 0; x < 4; x++) - vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0)); - break; - case 3: //R270 - for (int x = 0; x < 4; x++) - vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); - break; - case 4: //FXR90 - for (int x = 0; x < 4; x++) { - vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; - vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); + for (int face = 0; face < 6; face++) { + int tileindex = MYMIN(face, tilecount - 1); + const TileSpec &tile = tiles[tileindex]; + for (int j = 0; j < 4; j++) { + video::S3DVertex &vertex = vertices[face * 4 + j]; + v2f &tcoords = vertex.TCoords; + switch (tile.rotation) { + case 0: + break; + case 1: // R90 + tcoords.rotateBy(90, irr::core::vector2df(0, 0)); + break; + case 2: // R180 + tcoords.rotateBy(180, irr::core::vector2df(0, 0)); + break; + case 3: // R270 + tcoords.rotateBy(270, irr::core::vector2df(0, 0)); + break; + case 4: // FXR90 + tcoords.X = 1.0 - tcoords.X; + tcoords.rotateBy(90, irr::core::vector2df(0, 0)); + break; + case 5: // FXR270 + tcoords.X = 1.0 - tcoords.X; + tcoords.rotateBy(270, irr::core::vector2df(0, 0)); + break; + case 6: // FYR90 + tcoords.Y = 1.0 - tcoords.Y; + tcoords.rotateBy(90, irr::core::vector2df(0, 0)); + break; + case 7: // FYR270 + tcoords.Y = 1.0 - tcoords.Y; + tcoords.rotateBy(270, irr::core::vector2df(0, 0)); + break; + case 8: // FX + tcoords.X = 1.0 - tcoords.X; + break; + case 9: // FY + tcoords.Y = 1.0 - tcoords.Y; + break; + default: + break; } - break; - case 5: //FXR270 - for (int x = 0; x < 4; x++) { - vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; - vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); - } - break; - case 6: //FYR90 - for (int x = 0; x < 4; x++) { - vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; - vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0)); - } - break; - case 7: //FYR270 - for (int x = 0; x < 4; x++) { - vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; - vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0)); - } - break; - case 8: //FX - for (int x = 0; x < 4; x++) { - vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X; - } - break; - case 9: //FY - for (int x = 0; x < 4; x++) { - vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y; - } - break; - default: - break; } } - u16 indices[] = {0,1,2,2,3,0}; - for (s32 j = 0; j < 24; ++j) { - int tileindex = MYMIN(j / 4, tilecount - 1); - vertices[j].Color = encode_light_and_color(lights[light_indices[j]], - tiles[tileindex].color, light_source); - if (!light_source) - applyFacesShading(vertices[j].Color, vertices[j].Normal); - } - // Add to mesh collector - for (s32 k = 0; k < 6; ++k) { - int tileindex = MYMIN(k, tilecount - 1); - collector->append(tiles[tileindex], vertices + 4 * k, 4, indices, 6); - } -} -// Create a cuboid. -// collector - the MeshCollector for the resulting polygons -// box - the position and size of the box -// tiles - the tiles (materials) to use (for all 6 faces) -// tilecount - number of entries in tiles, 1<=tilecount<=6 -// c - color of the cuboid -// txc - texture coordinates - this is a list of texture coordinates -// for the opposite corners of each face - therefore, there -// should be (2+2)*6=24 values in the list. Alternatively, -// pass NULL to use the entire texture for each face. The -// order of the faces in the list is up-down-right-left-back- -// front (compatible with ContentFeatures). If you specified -// 0,0,1,1 for each face, that would be the same as -// passing NULL. -// light source - if greater than zero, the box's faces will not be shaded -void makeCuboid(MeshCollector *collector, const aabb3f &box, TileSpec *tiles, - int tilecount, const video::SColor &c, const f32* txc, - const u8 light_source) -{ - video::SColor color[6]; - for (u8 i = 0; i < 6; i++) - color[i] = c; - makeCuboid(collector, box, tiles, tilecount, color, txc, light_source); + if (data->m_smooth_lighting) { + for (int j = 0; j < 24; ++j) { + int tileindex = MYMIN(j / 4, tilecount - 1); + vertices[j].Color = encode_light_and_color(lights[light_indices[j]], + tiles[tileindex].color, f->light_source); + if (!f->light_source) + applyFacesShading(vertices[j].Color, vertices[j].Normal); + } + } + + // Add to mesh collector + for (int k = 0; k < 6; ++k) { + int tileindex = MYMIN(k, tilecount - 1); + collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6); + } } // Gets the base lighting values for a node -// frame - resulting (opaque) data -// p - node position (absolute) -// data - ... -// light_source - node light emission level -static void getSmoothLightFrame(LightFrame *frame, const v3s16 &p, MeshMakeData *data, u8 light_source) +void MapblockMeshGenerator::getSmoothLightFrame() { for (int k = 0; k < 8; ++k) { - u16 light = getSmoothLight(p, light_dirs[k], data); - frame->lightsA[k] = light & 0xff; - frame->lightsB[k] = light >> 8; + u16 light = getSmoothLight(blockpos_nodes + p, light_dirs[k], data); + frame.lightsA[k] = light & 0xff; + frame.lightsB[k] = light >> 8; } - frame->light_source = light_source; } // Calculates vertex light level -// frame - light values from getSmoothLightFrame() -// vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) -static u16 blendLight(const LightFrame &frame, const core::vector3df& vertex_pos) +// vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) +u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos) { f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE); f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE); @@ -408,47 +286,26 @@ static u16 blendLight(const LightFrame &frame, const core::vector3df& vertex_pos } // Calculates vertex color to be used in mapblock mesh -// frame - light values from getSmoothLightFrame() -// vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) -// tile_color - node's tile color -static video::SColor blendLight(const LightFrame &frame, - const core::vector3df& vertex_pos, video::SColor tile_color) -{ - u16 light = blendLight(frame, vertex_pos); - return encode_light_and_color(light, tile_color, frame.light_source); -} - -static video::SColor blendLight(const LightFrame &frame, - const core::vector3df& vertex_pos, const core::vector3df& vertex_normal, +// vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) +// tile_color - node's tile color +video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos, video::SColor tile_color) { - video::SColor color = blendLight(frame, vertex_pos, tile_color); - if (!frame.light_source) - applyFacesShading(color, vertex_normal); + u16 light = blendLight(vertex_pos); + return encode_light_and_color(light, tile_color, f->light_source); +} + +video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos, + const v3f &vertex_normal, video::SColor tile_color) +{ + video::SColor color = blendLight(vertex_pos, tile_color); + if (!f->light_source) + applyFacesShading(color, vertex_normal); return color; } -static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, - MeshMakeData *data, MapNode n, int v, int *neighbors) +void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords) { - MapNode n2 = data->m_vmanip.getNodeNoEx(p); - if (nodedef->nodeboxConnects(n, n2, v)) - *neighbors |= v; -} - -static void makeAutoLightedCuboid(MeshCollector *collector, MeshMakeData *data, - const v3f &pos, aabb3f box, TileSpec &tile, - /* pre-computed, for non-smooth lighting only */ const video::SColor color, - /* for smooth lighting only */ const LightFrame &frame) -{ - f32 dx1 = box.MinEdge.X; - f32 dy1 = box.MinEdge.Y; - f32 dz1 = box.MinEdge.Z; - f32 dx2 = box.MaxEdge.X; - f32 dy2 = box.MaxEdge.Y; - f32 dz2 = box.MaxEdge.Z; - box.MinEdge += pos; - box.MaxEdge += pos; f32 tx1 = (box.MinEdge.X / BS) + 0.5; f32 ty1 = (box.MinEdge.Y / BS) + 0.5; f32 tz1 = (box.MinEdge.Z / BS) + 0.5; @@ -456,63 +313,52 @@ static void makeAutoLightedCuboid(MeshCollector *collector, MeshMakeData *data, f32 ty2 = (box.MaxEdge.Y / BS) + 0.5; f32 tz2 = (box.MaxEdge.Z / BS) + 0.5; f32 txc[24] = { - tx1, 1-tz2, tx2, 1-tz1, // up - tx1, tz1, tx2, tz2, // down - tz1, 1-ty2, tz2, 1-ty1, // right - 1-tz2, 1-ty2, 1-tz1, 1-ty1, // left - 1-tx2, 1-ty2, 1-tx1, 1-ty1, // back - tx1, 1-ty2, tx2, 1-ty1, // front + tx1, 1 - tz2, tx2, 1 - tz1, // up + tx1, tz1, tx2, tz2, // down + tz1, 1 - ty2, tz2, 1 - ty1, // right + 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left + 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back + tx1, 1 - ty2, tx2, 1 - ty1, // front }; - if (data->m_smooth_lighting) { - u16 lights[8]; - for (int j = 0; j < 8; ++j) { - f32 x = (j & 4) ? dx2 : dx1; - f32 y = (j & 2) ? dy2 : dy1; - f32 z = (j & 1) ? dz2 : dz1; - lights[j] = blendLight(frame, core::vector3df(x, y, z)); - } - makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source); - } else { - makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source); - } + for (int i = 0; i != 24; ++i) + coords[i] = txc[i]; } -static void makeAutoLightedCuboidEx(MeshCollector *collector, MeshMakeData *data, - const v3f &pos, aabb3f box, TileSpec &tile, f32 *txc, - /* pre-computed, for non-smooth lighting only */ const video::SColor color, - /* for smooth lighting only */ const LightFrame &frame) +void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc, + TileSpec *tiles, int tile_count) { + f32 texture_coord_buf[24]; f32 dx1 = box.MinEdge.X; f32 dy1 = box.MinEdge.Y; f32 dz1 = box.MinEdge.Z; f32 dx2 = box.MaxEdge.X; f32 dy2 = box.MaxEdge.Y; f32 dz2 = box.MaxEdge.Z; - box.MinEdge += pos; - box.MaxEdge += pos; + box.MinEdge += origin; + box.MaxEdge += origin; + if (!txc) { + generateCuboidTextureCoords(box, texture_coord_buf); + txc = texture_coord_buf; + } + if (!tiles) { + tiles = &tile; + tile_count = 1; + } if (data->m_smooth_lighting) { u16 lights[8]; for (int j = 0; j < 8; ++j) { - f32 x = (j & 4) ? dx2 : dx1; - f32 y = (j & 2) ? dy2 : dy1; - f32 z = (j & 1) ? dz2 : dz1; - lights[j] = blendLight(frame, core::vector3df(x, y, z)); + v3f d; + d.X = (j & 4) ? dx2 : dx1; + d.Y = (j & 2) ? dy2 : dy1; + d.Z = (j & 1) ? dz2 : dz1; + lights[j] = blendLight(d); } - makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source); + drawCuboid(box, tiles, tile_count, lights, txc); } else { - makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source); + drawCuboid(box, tiles, tile_count, NULL, txc); } } -// For use in mapblock_mesh_generate_special -// X,Y,Z of position must be -1,0,1 -// This expression is a simplification of -// 3 * 3 * (pos.X + 1) + 3 * (pos.Y + 1) + (pos.Z + 1) -static inline int NeighborToIndex(const v3s16 &pos) -{ - return 9 * pos.X + 3 * pos.Y + pos.Z + 13; -} - /*! * Returns the i-th special tile for a map node. */ @@ -525,1633 +371,949 @@ static TileSpec getSpecialTile(const ContentFeatures &f, return copy; } +void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing) +{ + tile_liquid_top = getSpecialTile(*f, n, 0); + tile_liquid = getSpecialTile(*f, n, flowing ? 1 : 0); + + MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z)); + c_flowing = nodedef->getId(f->liquid_alternative_flowing); + c_source = nodedef->getId(f->liquid_alternative_source); + top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source); + + if (data->m_smooth_lighting) + return; // don't need to pre-compute anything in this case + + if (f->light_source != 0) { + // If this liquid emits light and doesn't contain light, draw + // it at what it emits, for an increased effect + light = decode_light(f->light_source); + light = light | (light << 8); + } else if (nodedef->get(ntop).param_type == CPT_LIGHT) { + // Otherwise, use the light of the node on top if possible + light = getInteriorLight(ntop, 0, nodedef); + } + + color_liquid_top = encode_light_and_color(light, tile_liquid_top.color, f->light_source); + color = encode_light_and_color(light, tile_liquid.color, f->light_source); +} + +void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing) +{ + u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8); + + for (int w = -1; w <= 1; w++) + for (int u = -1; u <= 1; u++) { + // Skip getting unneeded data + if (!flowing && u && w) + continue; + + NeighborData &neighbor = liquid_neighbors[w + 1][u + 1]; + v3s16 p2 = p + v3s16(u, 0, w); + MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); + neighbor.content = n2.getContent(); + neighbor.level = -0.5 * BS; + neighbor.is_same_liquid = false; + neighbor.top_is_same_liquid = false; + + if (neighbor.content == CONTENT_IGNORE) + continue; + + if (neighbor.content == c_source) { + neighbor.is_same_liquid = true; + neighbor.level = 0.5 * BS; + } else if (neighbor.content == c_flowing) { + neighbor.is_same_liquid = true; + u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK); + if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range) + liquid_level = 0; + else + liquid_level -= (LIQUID_LEVEL_MAX + 1 - range); + neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS; + } + + // Check node above neighbor. + // NOTE: This doesn't get executed if neighbor + // doesn't exist + p2.Y++; + n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); + if (n2.getContent() == c_source || n2.getContent() == c_flowing) + neighbor.top_is_same_liquid = true; + } +} + +void MapblockMeshGenerator::resetCornerLevels() +{ + for (int k = 0; k < 2; k++) + for (int i = 0; i < 2; i++) + corner_levels[k][i] = 0.5 * BS; +} + +void MapblockMeshGenerator::calculateCornerLevels() +{ + for (int k = 0; k < 2; k++) + for (int i = 0; i < 2; i++) + corner_levels[k][i] = getCornerLevel(i, k); +} + +f32 MapblockMeshGenerator::getCornerLevel(int i, int k) +{ + float sum = 0; + int count = 0; + int air_count = 0; + for (int dk = 0; dk < 2; dk++) + for (int di = 0; di < 2; di++) { + NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di]; + content_t content = neighbor_data.content; + + // If top is liquid, draw starting from top of node + if (neighbor_data.top_is_same_liquid) + return 0.5 * BS; + + // Source always has the full height + if (content == c_source) + return 0.5 * BS; + + // Flowing liquid has level information + if (content == c_flowing) { + sum += neighbor_data.level; + count++; + } else if (content == CONTENT_AIR) { + air_count++; + if (air_count >= 2) + return -0.5 * BS + 0.2; + } + } + if (count > 0) + return sum / count; + return 0; +} + +void MapblockMeshGenerator::drawLiquidSides(bool flowing) +{ + struct LiquidFaceDesc { + v3s16 dir; // XZ + v3s16 p[2]; // XZ only; 1 means +, 0 means - + }; + struct UV { + int u, v; + }; + static const LiquidFaceDesc base_faces[4] = { + {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}}, + {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}}, + {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}}, + {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}}, + }; + static const UV base_vertices[4] = { + {0, 1}, + {1, 1}, + {1, 0}, + {0, 0} + }; + for (int i = 0; i < 4; i++) { + const LiquidFaceDesc &face = base_faces[i]; + const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1]; + + // No face between nodes of the same liquid, unless there is node + // at the top to which it should be connected. Again, unless the face + // there would be inside the liquid + if (neighbor.is_same_liquid) { + if (!flowing) + continue; + if (!top_is_same_liquid) + continue; + if (neighbor.top_is_same_liquid) + continue; + } + + if (!flowing && (neighbor.content == CONTENT_IGNORE)) + continue; + + const ContentFeatures &neighbor_features = nodedef->get(neighbor.content); + // Don't draw face if neighbor is blocking the view + if (neighbor_features.solidness == 2) + continue; + + video::S3DVertex vertices[4]; + for (int j = 0; j < 4; j++) { + const UV &vertex = base_vertices[j]; + const v3s16 &base = face.p[vertex.u]; + v3f pos; + pos.X = (base.X - 0.5) * BS; + pos.Z = (base.Z - 0.5) * BS; + if (vertex.v) + pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS; + else + pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS; + if (data->m_smooth_lighting) + color = blendLight(pos, tile_liquid.color); + pos += origin; + vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v); + }; + collector->append(tile_liquid, vertices, 4, quad_indices, 6); + } +} + +void MapblockMeshGenerator::drawLiquidTop(bool flowing) +{ + // To get backface culling right, the vertices need to go + // clockwise around the front of the face. And we happened to + // calculate corner levels in exact reverse order. + static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}}; + + video::S3DVertex vertices[4] = { + video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1), + video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1), + video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0), + video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0), + }; + + for (int i = 0; i < 4; i++) { + int u = corner_resolve[i][0]; + int w = corner_resolve[i][1]; + vertices[i].Pos.Y += corner_levels[w][u]; + if (data->m_smooth_lighting) + vertices[i].Color = blendLight(vertices[i].Pos, tile_liquid_top.color); + vertices[i].Pos += origin; + } + + if (flowing) { + // Default downwards-flowing texture animation goes from + // -Z towards +Z, thus the direction is +Z. + // Rotate texture to make animation go in flow direction + // Positive if liquid moves towards +Z + f32 dz = (corner_levels[0][0] + corner_levels[0][1]) - + (corner_levels[1][0] + corner_levels[1][1]); + // Positive if liquid moves towards +X + f32 dx = (corner_levels[0][0] + corner_levels[1][0]) - + (corner_levels[0][1] + corner_levels[1][1]); + f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG; + v2f tcoord_center(0.5, 0.5); + v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X); + tcoord_translate.rotateBy(tcoord_angle); + tcoord_translate.X -= floor(tcoord_translate.X); + tcoord_translate.Y -= floor(tcoord_translate.Y); + + for (int i = 0; i < 4; i++) { + vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center); + vertices[i].TCoords += tcoord_translate; + } + + std::swap(vertices[0].TCoords, vertices[2].TCoords); + } + + collector->append(tile_liquid_top, vertices, 4, quad_indices, 6); +} + +void MapblockMeshGenerator::drawLiquidNode(bool flowing) +{ + prepareLiquidNodeDrawing(flowing); + getLiquidNeighborhood(flowing); + if (flowing) + calculateCornerLevels(); + else + resetCornerLevels(); + drawLiquidSides(flowing); + if (!top_is_same_liquid) + drawLiquidTop(flowing); +} + +void MapblockMeshGenerator::drawGlasslikeNode() +{ + useDefaultTile(); + + for (int face = 0; face < 6; face++) { + // Check this neighbor + v3s16 dir = g_6dirs[face]; + v3s16 neighbor_pos = blockpos_nodes + p + dir; + MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos); + // Don't make face if neighbor is of same type + if (neighbor.getContent() == n.getContent()) + continue; + v3f vertices[4] = { + v3f(-BS / 2, -BS / 2, BS / 2), + v3f( BS / 2, -BS / 2, BS / 2), + v3f( BS / 2, BS / 2, BS / 2), + v3f(-BS / 2, BS / 2, BS / 2), + }; + for (int i = 0; i < 4; i++) { + // Rotations in the g_6dirs format + switch (face) { + case 0: vertices[i].rotateXZBy( 0); break; // Z+ + case 1: vertices[i].rotateYZBy(-90); break; // Y+ + case 2: vertices[i].rotateXZBy(-90); break; // X+ + case 3: vertices[i].rotateXZBy(180); break; // Z- + case 4: vertices[i].rotateYZBy( 90); break; // Y- + case 5: vertices[i].rotateXZBy( 90); break; // X- + }; + } + drawQuad(vertices, dir); + } +} + +void MapblockMeshGenerator::drawGlasslikeFramedNode() +{ + TileSpec tiles[6]; + for (int face = 0; face < 6; face++) + tiles[face] = getTile(g_6dirs[face]); + + TileSpec glass_tiles[6]; + if (tiles[0].texture && tiles[3].texture && tiles[4].texture) { + glass_tiles[0] = tiles[4]; + glass_tiles[1] = tiles[0]; + glass_tiles[2] = tiles[4]; + glass_tiles[3] = tiles[4]; + glass_tiles[4] = tiles[3]; + glass_tiles[5] = tiles[4]; + } else { + for (int face = 0; face < 6; face++) + glass_tiles[face] = tiles[4]; + } + + u8 param2 = n.getParam2(); + bool H_merge = !(param2 & 128); + bool V_merge = !(param2 & 64); + param2 &= 63; + + static const float a = BS / 2; + static const float g = a - 0.003; + static const float b = .876 * ( BS / 2 ); + + static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = { + aabb3f( b, b, -a, a, a, a), // y+ + aabb3f(-a, b, -a, -b, a, a), // y+ + aabb3f( b, -a, -a, a, -b, a), // y- + aabb3f(-a, -a, -a, -b, -b, a), // y- + aabb3f( b, -a, b, a, a, a), // x+ + aabb3f( b, -a, -a, a, a, -b), // x+ + aabb3f(-a, -a, b, -b, a, a), // x- + aabb3f(-a, -a, -a, -b, a, -b), // x- + aabb3f(-a, b, b, a, a, a), // z+ + aabb3f(-a, -a, b, a, -b, a), // z+ + aabb3f(-a, -a, -a, a, -b, -b), // z- + aabb3f(-a, b, -a, a, a, -b), // z- + }; + static const aabb3f glass_faces[6] = { + aabb3f(-g, -g, g, g, g, g), // z+ + aabb3f(-g, g, -g, g, g, g), // y+ + aabb3f( g, -g, -g, g, g, g), // x+ + aabb3f(-g, -g, -g, g, g, -g), // z- + aabb3f(-g, -g, -g, g, -g, g), // y- + aabb3f(-g, -g, -g, -g, g, g), // x- + }; + + // tables of neighbour (connect if same type and merge allowed), + // checked with g_26dirs + + // 1 = connect, 0 = face visible + bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + // 1 = check + static const bool check_nb_vertical [FRAMED_NEIGHBOR_COUNT] = {0,1,0,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}; + static const bool check_nb_horizontal [FRAMED_NEIGHBOR_COUNT] = {1,0,1,1,0,1, 0,0,0,0, 1,1,1,1, 0,0,0,0}; + static const bool check_nb_all [FRAMED_NEIGHBOR_COUNT] = {1,1,1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1}; + const bool *check_nb = check_nb_all; + + // neighbours checks for frames visibility + if (H_merge || V_merge) { + if (!H_merge) + check_nb = check_nb_vertical; // vertical-only merge + if (!V_merge) + check_nb = check_nb_horizontal; // horizontal-only merge + content_t current = n.getContent(); + for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) { + if (!check_nb[i]) + continue; + v3s16 n2p = blockpos_nodes + p + g_26dirs[i]; + MapNode n2 = data->m_vmanip.getNodeNoEx(n2p); + content_t n2c = n2.getContent(); + if (n2c == current || n2c == CONTENT_IGNORE) + nb[i] = 1; + } + } + + // edge visibility + + static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = { + {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14}, + {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12}, + {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9}, + }; + + tile = tiles[1]; + for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) { + bool edge_invisible; + if (nb[nb_triplet[edge][2]]) + edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]]; + else + edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]]; + if (edge_invisible) + continue; + drawAutoLightedCuboid(frame_edges[edge]); + } + + for (int face = 0; face < 6; face++) { + if (nb[face]) + continue; + tile = glass_tiles[face]; + drawAutoLightedCuboid(glass_faces[face]); + } + + if (param2 > 0 && f->special_tiles[0].texture) { + // Interior volume level is in range 0 .. 63, + // convert it to -0.5 .. 0.5 + float vlev = (param2 / 63.0) * 2.0 - 1.0; + tile = getSpecialTile(*f, n, 0); + drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b), + -(nb[4] ? g : b), + -(nb[3] ? g : b), + (nb[2] ? g : b), + (nb[1] ? g : b) * vlev, + (nb[0] ? g : b))); + } +} + +void MapblockMeshGenerator::drawAllfacesNode() +{ + static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2); + useDefaultTile(false); + drawAutoLightedCuboid(box); +} + +void MapblockMeshGenerator::drawTorchlikeNode() +{ + u8 wall = n.getWallMounted(nodedef); + u8 tileindex = 0; + switch (wall) { + case DWM_YP: tileindex = 1; break; // ceiling + case DWM_YN: tileindex = 0; break; // floor + default: tileindex = 2; // side (or invalid—should we care?) + } + useTile(tileindex, true); + + float size = BS / 2 * f->visual_scale; + v3f vertices[4] = { + v3f(-size, -size, 0), + v3f( size, -size, 0), + v3f( size, size, 0), + v3f(-size, size, 0), + }; + for (int i = 0; i < 4; i++) { + switch (wall) { + case DWM_YP: vertices[i].rotateXZBy(-45); break; + case DWM_YN: vertices[i].rotateXZBy( 45); break; + case DWM_XP: vertices[i].rotateXZBy( 0); break; + case DWM_XN: vertices[i].rotateXZBy(180); break; + case DWM_ZP: vertices[i].rotateXZBy( 90); break; + case DWM_ZN: vertices[i].rotateXZBy(-90); break; + } + } + drawQuad(vertices); +} + +void MapblockMeshGenerator::drawSignlikeNode() +{ + u8 wall = n.getWallMounted(nodedef); + useTile(0, true); + static const float offset = BS / 16; + float size = BS / 2 * f->visual_scale; + // Wall at X+ of node + v3f vertices[4] = { + v3f(BS / 2 - offset, size, size), + v3f(BS / 2 - offset, size, -size), + v3f(BS / 2 - offset, -size, -size), + v3f(BS / 2 - offset, -size, size), + }; + for (int i = 0; i < 4; i++) { + switch (wall) { + case DWM_YP: vertices[i].rotateXYBy( 90); break; + case DWM_YN: vertices[i].rotateXYBy(-90); break; + case DWM_XP: vertices[i].rotateXZBy( 0); break; + case DWM_XN: vertices[i].rotateXZBy(180); break; + case DWM_ZP: vertices[i].rotateXZBy( 90); break; + case DWM_ZN: vertices[i].rotateXZBy(-90); break; + } + } + drawQuad(vertices); +} + +void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset, + bool offset_top_only) +{ + v3f vertices[4] = { + v3f(-scale, -BS / 2, 0), + v3f( scale, -BS / 2, 0), + v3f( scale, -BS / 2 + scale * 2, 0), + v3f(-scale, -BS / 2 + scale * 2, 0), + }; + if (random_offset_Y) { + PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24); + offset.Y = BS * ((yrng.next() % 16 / 16.0) * 0.125); + } + int offset_first_index = offset_top_only ? 2 : 0; + for (int i = 0; i < 4; i++) { + if (i >= offset_first_index) + vertices[i].Z += quad_offset; + vertices[i].rotateXZBy(rotation + rotate_degree); + vertices[i] += offset; + } + drawQuad(vertices); +} + +void MapblockMeshGenerator::drawPlantlikeNode() +{ + useTile(0, false); + draw_style = PLANT_STYLE_CROSS; + scale = BS / 2 * f->visual_scale; + offset = v3f(0, 0, 0); + rotate_degree = 0; + random_offset_Y = false; + face_num = 0; + + switch (f->param_type_2) { + case CPT2_MESHOPTIONS: + draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE); + if (n.param2 & MO_BIT_SCALE_SQRT2) + scale *= 1.41421; + if (n.param2 & MO_BIT_RANDOM_OFFSET) { + PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16); + offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); + offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); + } + if (n.param2 & MO_BIT_RANDOM_OFFSET_Y) + random_offset_Y = true; + break; + + case CPT2_DEGROTATE: + rotate_degree = n.param2 * 2; + break; + + default: + break; + } + + switch (draw_style) { + case PLANT_STYLE_CROSS: + drawPlantlikeQuad(46); + drawPlantlikeQuad(-44); + break; + + case PLANT_STYLE_CROSS2: + drawPlantlikeQuad(91); + drawPlantlikeQuad(1); + break; + + case PLANT_STYLE_STAR: + drawPlantlikeQuad(121); + drawPlantlikeQuad(241); + drawPlantlikeQuad(1); + break; + + case PLANT_STYLE_HASH: + drawPlantlikeQuad( 1, BS / 4); + drawPlantlikeQuad( 91, BS / 4); + drawPlantlikeQuad(181, BS / 4); + drawPlantlikeQuad(271, BS / 4); + break; + + case PLANT_STYLE_HASH2: + drawPlantlikeQuad( 1, -BS / 2, true); + drawPlantlikeQuad( 91, -BS / 2, true); + drawPlantlikeQuad(181, -BS / 2, true); + drawPlantlikeQuad(271, -BS / 2, true); + break; + } +} + +void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle, + float offset_h, float offset_v) +{ + v3f vertices[4] = { + v3f(-scale, -BS / 2, 0), + v3f( scale, -BS / 2, 0), + v3f( scale, -BS / 2 + scale * 2, 0), + v3f(-scale, -BS / 2 + scale * 2, 0), + }; + for (int i = 0; i < 4; i++) { + vertices[i].rotateYZBy(opening_angle); + vertices[i].Z += offset_h; + vertices[i].rotateXZBy(rotation); + vertices[i].Y += offset_v; + } + drawQuad(vertices); +} + +void MapblockMeshGenerator::drawFirelikeNode() +{ + useTile(0, false); + scale = BS / 2 * f->visual_scale; + + // Check for adjacent nodes + bool neighbors = false; + bool neighbor[6] = {0, 0, 0, 0, 0, 0}; + content_t current = n.getContent(); + for (int i = 0; i < 6; i++) { + v3s16 n2p = blockpos_nodes + p + g_6dirs[i]; + MapNode n2 = data->m_vmanip.getNodeNoEx(n2p); + content_t n2c = n2.getContent(); + if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) { + neighbor[i] = true; + neighbors = true; + } + } + bool drawBasicFire = neighbor[D6D_YN] || !neighbors; + bool drawBottomFire = neighbor[D6D_YP]; + + if (drawBasicFire || neighbor[D6D_ZP]) + drawFirelikeQuad(0, -10, 0.4 * BS); + else if (drawBottomFire) + drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS); + + if (drawBasicFire || neighbor[D6D_XN]) + drawFirelikeQuad(90, -10, 0.4 * BS); + else if (drawBottomFire) + drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS); + + if (drawBasicFire || neighbor[D6D_ZN]) + drawFirelikeQuad(180, -10, 0.4 * BS); + else if (drawBottomFire) + drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS); + + if (drawBasicFire || neighbor[D6D_XP]) + drawFirelikeQuad(270, -10, 0.4 * BS); + else if (drawBottomFire) + drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS); + + if (drawBasicFire) { + drawFirelikeQuad(45, 0, 0.0); + drawFirelikeQuad(-45, 0, 0.0); + } +} + +void MapblockMeshGenerator::drawFencelikeNode() +{ + useDefaultTile(false); + TileSpec tile_nocrack = tile; + tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK; + + // Put wood the right way around in the posts + TileSpec tile_rot = tile; + tile_rot.rotation = 1; + + static const f32 post_rad = BS / 8; + static const f32 bar_rad = BS / 16; + static const f32 bar_len = BS / 2 - post_rad; + + // The post - always present + static const aabb3f post(-post_rad, -BS / 2, -post_rad, + post_rad, BS / 2, post_rad); + static const f32 postuv[24] = { + 0.375, 0.375, 0.625, 0.625, + 0.375, 0.375, 0.625, 0.625, + 0.000, 0.000, 0.250, 1.000, + 0.250, 0.000, 0.500, 1.000, + 0.500, 0.000, 0.750, 1.000, + 0.750, 0.000, 1.000, 1.000, + }; + tile = tile_rot; + drawAutoLightedCuboid(post, postuv); + + tile = tile_nocrack; + + // Now a section of fence, +X, if there's a post there + v3s16 p2 = p; + p2.X++; + MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); + const ContentFeatures *f2 = &nodedef->get(n2); + if (f2->drawtype == NDT_FENCELIKE) { + static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad, + BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad); + static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad, + BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad); + static const f32 xrailuv[24] = { + 0.000, 0.125, 1.000, 0.250, + 0.000, 0.250, 1.000, 0.375, + 0.375, 0.375, 0.500, 0.500, + 0.625, 0.625, 0.750, 0.750, + 0.000, 0.500, 1.000, 0.625, + 0.000, 0.875, 1.000, 1.000, + }; + drawAutoLightedCuboid(bar_x1, xrailuv); + drawAutoLightedCuboid(bar_x2, xrailuv); + } + + // Now a section of fence, +Z, if there's a post there + p2 = p; + p2.Z++; + n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); + f2 = &nodedef->get(n2); + if (f2->drawtype == NDT_FENCELIKE) { + static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len, + bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len); + static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len, + bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len); + static const f32 zrailuv[24] = { + 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch + 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead + 0.0000, 0.5625, 1.0000, 0.6875, + 0.0000, 0.3750, 1.0000, 0.5000, + 0.3750, 0.3750, 0.5000, 0.5000, + 0.6250, 0.6250, 0.7500, 0.7500, + }; + drawAutoLightedCuboid(bar_z1, zrailuv); + drawAutoLightedCuboid(bar_z2, zrailuv); + } +} + +bool MapblockMeshGenerator::isSameRail(v3s16 dir) +{ + MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir); + if (node2.getContent() == n.getContent()) + return true; + const ContentFeatures &def2 = nodedef->get(node2); + return ((def2.drawtype == NDT_RAILLIKE) && + (def2.getGroup(raillike_groupname) == raillike_group)); +} + +void MapblockMeshGenerator::drawRaillikeNode() +{ + static const v3s16 direction[4] = { + v3s16( 0, 0, 1), + v3s16( 0, 0, -1), + v3s16(-1, 0, 0), + v3s16( 1, 0, 0), + }; + static const int slope_angle[4] = {0, 180, 90, -90}; + + enum RailTile { + straight, + curved, + junction, + cross, + }; + struct RailDesc { + int tile_index; + int angle; + }; + static const RailDesc rail_kinds[16] = { + // +x -x -z +z + //------------- + {straight, 0}, // . . . . + {straight, 0}, // . . . +Z + {straight, 0}, // . . -Z . + {straight, 0}, // . . -Z +Z + {straight, 90}, // . -X . . + { curved, 180}, // . -X . +Z + { curved, 270}, // . -X -Z . + {junction, 180}, // . -X -Z +Z + {straight, 90}, // +X . . . + { curved, 90}, // +X . . +Z + { curved, 0}, // +X . -Z . + {junction, 0}, // +X . -Z +Z + {straight, 90}, // +X -X . . + {junction, 90}, // +X -X . +Z + {junction, 270}, // +X -X -Z . + { cross, 0}, // +X -X -Z +Z + }; + + raillike_group = nodedef->get(n).getGroup(raillike_groupname); + + int code = 0; + int angle; + int tile_index; + bool sloped = false; + for (int dir = 0; dir < 4; dir++) { + bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0)); + if (rail_above) { + sloped = true; + angle = slope_angle[dir]; + } + if (rail_above || + isSameRail(direction[dir]) || + isSameRail(direction[dir] + v3s16(0, -1, 0))) + code |= 1 << dir; + } + + if (sloped) { + tile_index = straight; + } else { + tile_index = rail_kinds[code].tile_index; + angle = rail_kinds[code].angle; + } + + useTile(tile_index, true); + + static const float offset = BS / 64; + static const float size = BS / 2; + float y2 = sloped ? size : -size; + v3f vertices[4] = { + v3f(-size, -size + offset, -size), + v3f( size, -size + offset, -size), + v3f( size, y2 + offset, size), + v3f(-size, y2 + offset, size), + }; + if (angle) + for (int i = 0; i < 4; i++) + vertices[i].rotateXZBy(angle); + drawQuad(vertices); +} + +void MapblockMeshGenerator::drawNodeboxNode() +{ + static const v3s16 tile_dirs[6] = { + v3s16(0, 1, 0), + v3s16(0, -1, 0), + v3s16(1, 0, 0), + v3s16(-1, 0, 0), + v3s16(0, 0, 1), + v3s16(0, 0, -1) + }; + + // we have this order for some reason... + static const v3s16 connection_dirs[6] = { + v3s16( 0, 1, 0), // top + v3s16( 0, -1, 0), // bottom + v3s16( 0, 0, -1), // front + v3s16(-1, 0, 0), // left + v3s16( 0, 0, 1), // back + v3s16( 1, 0, 0), // right + }; + + TileSpec tiles[6]; + for (int face = 0; face < 6; face++) { + // Handles facedir rotation for textures + tiles[face] = getTile(tile_dirs[face]); + } + + // locate possible neighboring nodes to connect to + int neighbors_set = 0; + if (f->node_box.type == NODEBOX_CONNECTED) { + for (int dir = 0; dir != 6; dir++) { + int flag = 1 << dir; + v3s16 p2 = blockpos_nodes + p + connection_dirs[dir]; + MapNode n2 = data->m_vmanip.getNodeNoEx(p2); + if (nodedef->nodeboxConnects(n, n2, flag)) + neighbors_set |= flag; + } + } + + std::vector boxes; + n.getNodeBoxes(nodedef, &boxes, neighbors_set); + for (std::vector::iterator i = boxes.begin(); i != boxes.end(); ++i) + drawAutoLightedCuboid(*i, NULL, tiles, 6); +} + +void MapblockMeshGenerator::drawMeshNode() +{ + u8 facedir = 0; + scene::IMesh* mesh; + bool private_mesh; // as a grab/drop pair is not thread-safe + + if (f->param_type_2 == CPT2_FACEDIR || + f->param_type_2 == CPT2_COLORED_FACEDIR) { + facedir = n.getFaceDir(nodedef); + } else if (f->param_type_2 == CPT2_WALLMOUNTED || + f->param_type_2 == CPT2_COLORED_WALLMOUNTED) { + // Convert wallmounted to 6dfacedir. + // When cache enabled, it is already converted. + facedir = n.getWallMounted(nodedef); + if (!enable_mesh_cache) { + static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2}; + facedir = wm_to_6d[facedir]; + } + } + + if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) { + // use cached meshes + private_mesh = false; + mesh = f->mesh_ptr[facedir]; + } else if (f->mesh_ptr[0]) { + // no cache, clone and rotate mesh + private_mesh = true; + mesh = cloneMesh(f->mesh_ptr[0]); + rotateMeshBy6dFacedir(mesh, facedir); + recalculateBoundingBox(mesh); + meshmanip->recalculateNormals(mesh, true, false); + } else + return; + + int mesh_buffer_count = mesh->getMeshBufferCount(); + for (int j = 0; j < mesh_buffer_count; j++) { + useTile(j, false); + scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); + video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); + int vertex_count = buf->getVertexCount(); + + if (data->m_smooth_lighting) { + // Mesh is always private here. So the lighting is applied to each + // vertex right here. + for (int k = 0; k < vertex_count; k++) { + video::S3DVertex &vertex = vertices[k]; + vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color); + vertex.Pos += origin; + } + collector->append(tile, vertices, vertex_count, + buf->getIndices(), buf->getIndexCount()); + } else { + // Don't modify the mesh, it may not be private here. + // Instead, let the collector process colors, etc. + collector->append(tile, vertices, vertex_count, + buf->getIndices(), buf->getIndexCount(), origin, + color, f->light_source); + } + } + if (private_mesh) + mesh->drop(); +} + +// also called when the drawtype is known but should have been pre-converted +void MapblockMeshGenerator::errorUnknownDrawtype() +{ + infostream << "Got drawtype " << f->drawtype << std::endl; + FATAL_ERROR("Unknown drawtype"); +} + +void MapblockMeshGenerator::drawNode() +{ + if (data->m_smooth_lighting) + getSmoothLightFrame(); + else + light = getInteriorLight(n, 1, nodedef); + switch (f->drawtype) { + case NDT_LIQUID: drawLiquidNode(false); break; + case NDT_FLOWINGLIQUID: drawLiquidNode(true); break; + case NDT_GLASSLIKE: drawGlasslikeNode(); break; + case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break; + case NDT_ALLFACES: drawAllfacesNode(); break; + case NDT_TORCHLIKE: drawTorchlikeNode(); break; + case NDT_SIGNLIKE: drawSignlikeNode(); break; + case NDT_PLANTLIKE: drawPlantlikeNode(); break; + case NDT_FIRELIKE: drawFirelikeNode(); break; + case NDT_FENCELIKE: drawFencelikeNode(); break; + case NDT_RAILLIKE: drawRaillikeNode(); break; + case NDT_NODEBOX: drawNodeboxNode(); break; + case NDT_MESH: drawMeshNode(); break; + default: errorUnknownDrawtype(); break; + } +} + /* TODO: Fix alpha blending for special nodes Currently only the last element rendered is blended correct */ -void mapblock_mesh_generate_special(MeshMakeData *data, - MeshCollector &collector) +void MapblockMeshGenerator::generate() { - INodeDefManager *nodedef = data->m_client->ndef(); - scene::ISceneManager* smgr = data->m_client->getSceneManager(); - scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator(); - - // 0ms - //TimeTaker timer("mapblock_mesh_generate_special()"); - - /* - Some settings - */ - bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache") && - !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting - - v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE; - - for(s16 z = 0; z < MAP_BLOCKSIZE; z++) - for(s16 y = 0; y < MAP_BLOCKSIZE; y++) - for(s16 x = 0; x < MAP_BLOCKSIZE; x++) - { - v3s16 p(x,y,z); - - MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p); - const ContentFeatures &f = nodedef->get(n); - - // Only solidness=0 stuff is drawn here - if(f.solidness != 0) + for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++) + for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++) + for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) { + n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p); + f = &nodedef->get(n); + // Solid nodes are drawn by MapBlockMesh + if (f->solidness != 0) continue; - - if (f.drawtype == NDT_AIRLIKE) + if (f->drawtype == NDT_AIRLIKE) continue; - - LightFrame frame; - if (data->m_smooth_lighting) - getSmoothLightFrame(&frame, blockpos_nodes + p, data, f.light_source); - else - frame.light_source = f.light_source; - - switch(f.drawtype) { - default: - infostream << "Got " << f.drawtype << std::endl; - FATAL_ERROR("Unknown drawtype"); - break; - case NDT_LIQUID: - { - /* - Add water sources to mesh if using new style - */ - TileSpec tile_liquid = getSpecialTile(f, n, 0); - TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data); - u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c1 = encode_light_and_color(l, - tile_liquid.color, f.light_source); - video::SColor c2 = encode_light_and_color(l, - tile_liquid_bfculled.color, f.light_source); - - bool top_is_same_liquid = false; - MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); - content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing); - content_t c_source = nodedef->getId(f.liquid_alternative_source); - if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) - top_is_same_liquid = true; - - /* - Generate sides - */ - v3s16 side_dirs[4] = { - v3s16(1,0,0), - v3s16(-1,0,0), - v3s16(0,0,1), - v3s16(0,0,-1), - }; - for(u32 i=0; i<4; i++) - { - v3s16 dir = side_dirs[i]; - - MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir); - content_t neighbor_content = neighbor.getContent(); - const ContentFeatures &n_feat = nodedef->get(neighbor_content); - MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0)); - content_t n_top_c = n_top.getContent(); - - if(neighbor_content == CONTENT_IGNORE) - continue; - - /* - If our topside is liquid and neighbor's topside - is liquid, don't draw side face - */ - if(top_is_same_liquid && (n_top_c == c_flowing || - n_top_c == c_source || n_top_c == CONTENT_IGNORE)) - continue; - - // Don't draw face if neighbor is blocking the view - if(n_feat.solidness == 2) - continue; - - bool neighbor_is_same_liquid = (neighbor_content == c_source - || neighbor_content == c_flowing); - - // Don't draw any faces if neighbor same is liquid and top is - // same liquid - if(neighbor_is_same_liquid && !top_is_same_liquid) - continue; - - // Use backface culled material if neighbor doesn't have a - // solidness of 0 - const TileSpec *current_tile = &tile_liquid; - video::SColor *c = &c1; - if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) { - current_tile = &tile_liquid_bfculled; - c = &c2; - } - - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,0,BS/2,0,0,0, *c, 0,1), - video::S3DVertex(BS/2,0,BS/2,0,0,0, *c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0), - }; - - /* - If our topside is liquid, set upper border of face - at upper border of node - */ - if (top_is_same_liquid) { - vertices[2].Pos.Y = 0.5 * BS; - vertices[3].Pos.Y = 0.5 * BS; - } else { - /* - Otherwise upper position of face is liquid level - */ - vertices[2].Pos.Y = 0.5 * BS; - vertices[3].Pos.Y = 0.5 * BS; - } - /* - If neighbor is liquid, lower border of face is liquid level - */ - if (neighbor_is_same_liquid) { - vertices[0].Pos.Y = 0.5 * BS; - vertices[1].Pos.Y = 0.5 * BS; - } else { - /* - If neighbor is not liquid, lower border of face is - lower border of node - */ - vertices[0].Pos.Y = -0.5 * BS; - vertices[1].Pos.Y = -0.5 * BS; - } - - for (s32 j = 0; j < 4; j++) { - if(dir == v3s16(0,0,1)) - vertices[j].Pos.rotateXZBy(0); - if(dir == v3s16(0,0,-1)) - vertices[j].Pos.rotateXZBy(180); - if(dir == v3s16(-1,0,0)) - vertices[j].Pos.rotateXZBy(90); - if(dir == v3s16(1,0,-0)) - vertices[j].Pos.rotateXZBy(-90); - - // Do this to not cause glitches when two liquids are - // side-by-side - /*if(neighbor_is_same_liquid == false){ - vertices[j].Pos.X *= 0.98; - vertices[j].Pos.Z *= 0.98; - }*/ - - if (data->m_smooth_lighting) - vertices[j].Color = blendLight(frame, vertices[j].Pos, current_tile->color); - vertices[j].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(*current_tile, vertices, 4, indices, 6); - } - - /* - Generate top - */ - if(top_is_same_liquid) - continue; - - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0), - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0), - }; - - for (s32 i = 0; i < 4; i++) { - vertices[i].Pos.Y += 0.5 * BS; - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, tile_liquid.color); - vertices[i].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(tile_liquid, vertices, 4, indices, 6); - break;} - case NDT_FLOWINGLIQUID: - { - /* - Add flowing liquid to mesh - */ - TileSpec tile_liquid = getSpecialTile(f, n, 0); - TileSpec tile_liquid_bfculled = getSpecialTile(f, n, 1); - - bool top_is_same_liquid = false; - MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); - content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing); - content_t c_source = nodedef->getId(f.liquid_alternative_source); - if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) - top_is_same_liquid = true; - - u16 l = 0; - // If this liquid emits light and doesn't contain light, draw - // it at what it emits, for an increased effect - u8 light_source = nodedef->get(n).light_source; - if(light_source != 0){ - l = decode_light(light_source); - l = l | (l<<8); - } - // Use the light of the node on top if possible - else if(nodedef->get(ntop).param_type == CPT_LIGHT) - l = getInteriorLight(ntop, 0, nodedef); - // Otherwise use the light of this node (the liquid) - else - l = getInteriorLight(n, 0, nodedef); - video::SColor c1 = encode_light_and_color(l, - tile_liquid.color, f.light_source); - video::SColor c2 = encode_light_and_color(l, - tile_liquid_bfculled.color, f.light_source); - - u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8); - - // Neighbor liquid levels (key = relative position) - // Includes current node - - struct NeighborData { - f32 level; - content_t content; - u8 flags; - }; - NeighborData neighbor_data_matrix[27]; - - const u8 neighborflag_top_is_same_liquid = 0x01; - v3s16 neighbor_dirs[9] = { - v3s16(0,0,0), - v3s16(0,0,1), - v3s16(0,0,-1), - v3s16(1,0,0), - v3s16(-1,0,0), - v3s16(1,0,1), - v3s16(-1,0,-1), - v3s16(1,0,-1), - v3s16(-1,0,1), - }; - for(u32 i=0; i<9; i++) - { - content_t content = CONTENT_AIR; - float level = -0.5 * BS; - u8 flags = 0; - // Check neighbor - v3s16 p2 = p + neighbor_dirs[i]; - MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - if(n2.getContent() != CONTENT_IGNORE) - { - content = n2.getContent(); - - if(n2.getContent() == c_source) - level = 0.5 * BS; - else if(n2.getContent() == c_flowing){ - u8 liquid_level = (n2.param2&LIQUID_LEVEL_MASK); - if (liquid_level <= LIQUID_LEVEL_MAX+1-range) - liquid_level = 0; - else - liquid_level -= (LIQUID_LEVEL_MAX+1-range); - level = (-0.5 + ((float)liquid_level + 0.5) / (float)range) * BS; - } - - // Check node above neighbor. - // NOTE: This doesn't get executed if neighbor - // doesn't exist - p2.Y += 1; - n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - if(n2.getContent() == c_source || - n2.getContent() == c_flowing) - flags |= neighborflag_top_is_same_liquid; - } - - NeighborData &neighbor_data = - neighbor_data_matrix[NeighborToIndex(neighbor_dirs[i])]; - - neighbor_data.level = level; - neighbor_data.content = content; - neighbor_data.flags = flags; - } - - // Corner heights (average between four liquids) - f32 corner_levels[4]; - - v3s16 halfdirs[4] = { - v3s16(0,0,0), - v3s16(1,0,0), - v3s16(1,0,1), - v3s16(0,0,1), - }; - for(u32 i=0; i<4; i++) - { - v3s16 cornerdir = halfdirs[i]; - float cornerlevel = 0; - u32 valid_count = 0; - u32 air_count = 0; - for(u32 j=0; j<4; j++) - { - v3s16 neighbordir = cornerdir - halfdirs[j]; - - NeighborData &neighbor_data = - neighbor_data_matrix[NeighborToIndex(neighbordir)]; - content_t content = neighbor_data.content; - // If top is liquid, draw starting from top of node - if (neighbor_data.flags & neighborflag_top_is_same_liquid) - { - cornerlevel = 0.5*BS; - valid_count = 1; - break; - } - // Source is always the same height - else if(content == c_source) - { - cornerlevel = 0.5 * BS; - valid_count = 1; - break; - } - // Flowing liquid has level information - else if(content == c_flowing) - { - cornerlevel += neighbor_data.level; - valid_count++; - } - else if(content == CONTENT_AIR) - { - air_count++; - } - } - if(air_count >= 2) - cornerlevel = -0.5*BS+0.2; - else if(valid_count > 0) - cornerlevel /= valid_count; - corner_levels[i] = cornerlevel; - } - - /* - Generate sides - */ - - v3s16 side_dirs[4] = { - v3s16(1,0,0), - v3s16(-1,0,0), - v3s16(0,0,1), - v3s16(0,0,-1), - }; - s16 side_corners[4][2] = { - {1, 2}, - {3, 0}, - {2, 3}, - {0, 1}, - }; - for(u32 i=0; i<4; i++) - { - v3s16 dir = side_dirs[i]; - - NeighborData& neighbor_data = - neighbor_data_matrix[NeighborToIndex(dir)]; - /* - If our topside is liquid and neighbor's topside - is liquid, don't draw side face - */ - if (top_is_same_liquid && - (neighbor_data.flags & neighborflag_top_is_same_liquid)) - continue; - - content_t neighbor_content = neighbor_data.content; - const ContentFeatures &n_feat = nodedef->get(neighbor_content); - - // Don't draw face if neighbor is blocking the view - if(n_feat.solidness == 2) - continue; - - bool neighbor_is_same_liquid = (neighbor_content == c_source - || neighbor_content == c_flowing); - - // Don't draw any faces if neighbor same is liquid and top is - // same liquid - if(neighbor_is_same_liquid == true - && top_is_same_liquid == false) - continue; - - // Use backface culled material if neighbor doesn't have a - // solidness of 0 - const TileSpec *current_tile = &tile_liquid; - video::SColor *c = &c1; - if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) { - current_tile = &tile_liquid_bfculled; - c = &c2; - } - - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0), - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0), - }; - - /* - If our topside is liquid, set upper border of face - at upper border of node - */ - if(top_is_same_liquid) - { - vertices[2].Pos.Y = 0.5*BS; - vertices[3].Pos.Y = 0.5*BS; - } - /* - Otherwise upper position of face is corner levels - */ - else - { - vertices[2].Pos.Y = corner_levels[side_corners[i][0]]; - vertices[3].Pos.Y = corner_levels[side_corners[i][1]]; - } - - /* - If neighbor is liquid, lower border of face is corner - liquid levels - */ - if(neighbor_is_same_liquid) - { - vertices[0].Pos.Y = corner_levels[side_corners[i][1]]; - vertices[1].Pos.Y = corner_levels[side_corners[i][0]]; - } - /* - If neighbor is not liquid, lower border of face is - lower border of node - */ - else - { - vertices[0].Pos.Y = -0.5*BS; - vertices[1].Pos.Y = -0.5*BS; - } - - for(s32 j=0; j<4; j++) - { - if(dir == v3s16(0,0,1)) - vertices[j].Pos.rotateXZBy(0); - if(dir == v3s16(0,0,-1)) - vertices[j].Pos.rotateXZBy(180); - if(dir == v3s16(-1,0,0)) - vertices[j].Pos.rotateXZBy(90); - if(dir == v3s16(1,0,-0)) - vertices[j].Pos.rotateXZBy(-90); - - // Do this to not cause glitches when two liquids are - // side-by-side - /*if(neighbor_is_same_liquid == false){ - vertices[j].Pos.X *= 0.98; - vertices[j].Pos.Z *= 0.98; - }*/ - - if (data->m_smooth_lighting) - vertices[j].Color = blendLight(frame, vertices[j].Pos, current_tile->color); - vertices[j].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(*current_tile, vertices, 4, indices, 6); - } - - /* - Generate top side, if appropriate - */ - - if(top_is_same_liquid == false) - { - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1), - video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1), - video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0), - video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0), - }; - - // To get backface culling right, the vertices need to go - // clockwise around the front of the face. And we happened to - // calculate corner levels in exact reverse order. - s32 corner_resolve[4] = {3,2,1,0}; - - for(s32 i=0; i<4; i++) - { - //vertices[i].Pos.Y += liquid_level; - //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)]; - s32 j = corner_resolve[i]; - vertices[i].Pos.Y += corner_levels[j]; - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, tile_liquid.color); - vertices[i].Pos += intToFloat(p, BS); - } - - // Default downwards-flowing texture animation goes from - // -Z towards +Z, thus the direction is +Z. - // Rotate texture to make animation go in flow direction - // Positive if liquid moves towards +Z - f32 dz = (corner_levels[side_corners[3][0]] + - corner_levels[side_corners[3][1]]) - - (corner_levels[side_corners[2][0]] + - corner_levels[side_corners[2][1]]); - // Positive if liquid moves towards +X - f32 dx = (corner_levels[side_corners[1][0]] + - corner_levels[side_corners[1][1]]) - - (corner_levels[side_corners[0][0]] + - corner_levels[side_corners[0][1]]); - f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG ; - v2f tcoord_center(0.5, 0.5); - v2f tcoord_translate( - blockpos_nodes.Z + z, - blockpos_nodes.X + x); - tcoord_translate.rotateBy(tcoord_angle); - tcoord_translate.X -= floor(tcoord_translate.X); - tcoord_translate.Y -= floor(tcoord_translate.Y); - - for(s32 i=0; i<4; i++) - { - vertices[i].TCoords.rotateBy( - tcoord_angle, - tcoord_center); - vertices[i].TCoords += tcoord_translate; - } - - v2f t = vertices[0].TCoords; - vertices[0].TCoords = vertices[2].TCoords; - vertices[2].TCoords = t; - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(tile_liquid, vertices, 4, indices, 6); - } - break;} - case NDT_GLASSLIKE: - { - TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); - - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = encode_light_and_color(l, tile.color, - f.light_source); - for(u32 j=0; j<6; j++) - { - // Check this neighbor - v3s16 dir = g_6dirs[j]; - v3s16 n2p = blockpos_nodes + p + dir; - MapNode n2 = data->m_vmanip.getNodeNoEx(n2p); - // Don't make face if neighbor is of same type - if(n2.getContent() == n.getContent()) - continue; - video::SColor c2=c; - if(!f.light_source) - applyFacesShading(c2, v3f(dir.X, dir.Y, dir.Z)); - - - // The face at Z+ - video::S3DVertex vertices[4] = { - video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,1), - video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,1), - video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,0), - video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,0), - }; - - // Rotations in the g_6dirs format - if(j == 0) // Z+ - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(0); - else if(j == 1) // Y+ - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateYZBy(-90); - else if(j == 2) // X+ - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(-90); - else if(j == 3) // Z- - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(180); - else if(j == 4) // Y- - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateYZBy(90); - else if(j == 5) // X- - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(90); - - for (u16 i = 0; i < 4; i++) { - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, vertices[i].Normal, tile.color); - vertices[i].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(tile, vertices, 4, indices, 6); - } - break;} - case NDT_GLASSLIKE_FRAMED_OPTIONAL: - // This is always pre-converted to something else - FATAL_ERROR("NDT_GLASSLIKE_FRAMED_OPTIONAL not pre-converted as expected"); - break; - case NDT_GLASSLIKE_FRAMED: - { - static const v3s16 dirs[6] = { - v3s16( 0, 1, 0), - v3s16( 0,-1, 0), - v3s16( 1, 0, 0), - v3s16(-1, 0, 0), - v3s16( 0, 0, 1), - v3s16( 0, 0,-1) - }; - - u16 l = getInteriorLight(n, 1, nodedef); - u8 i; - TileSpec tiles[6]; - for (i = 0; i < 6; i++) - tiles[i] = getNodeTile(n, p, dirs[i], data); - - video::SColor tile0color = encode_light_and_color(l, - tiles[0].color, f.light_source); - - TileSpec glass_tiles[6]; - video::SColor glasscolor[6]; - if (tiles[1].texture && tiles[2].texture && tiles[3].texture) { - glass_tiles[0] = tiles[2]; - glass_tiles[1] = tiles[3]; - glass_tiles[2] = tiles[1]; - glass_tiles[3] = tiles[1]; - glass_tiles[4] = tiles[1]; - glass_tiles[5] = tiles[1]; - } else { - for (i = 0; i < 6; i++) - glass_tiles[i] = tiles[1]; - } - for (i = 0; i < 6; i++) - glasscolor[i] = encode_light_and_color(l, glass_tiles[i].color, - f.light_source); - - u8 param2 = n.getParam2(); - bool H_merge = ! bool(param2 & 128); - bool V_merge = ! bool(param2 & 64); - param2 = param2 & 63; - - v3f pos = intToFloat(p, BS); - static const float a = BS / 2; - static const float g = a - 0.003; - static const float b = .876 * ( BS / 2 ); - - static const aabb3f frame_edges[12] = { - aabb3f( b, b,-a, a, a, a), // y+ - aabb3f(-a, b,-a,-b, a, a), // y+ - aabb3f( b,-a,-a, a,-b, a), // y- - aabb3f(-a,-a,-a,-b,-b, a), // y- - aabb3f( b,-a, b, a, a, a), // x+ - aabb3f( b,-a,-a, a, a,-b), // x+ - aabb3f(-a,-a, b,-b, a, a), // x- - aabb3f(-a,-a,-a,-b, a,-b), // x- - aabb3f(-a, b, b, a, a, a), // z+ - aabb3f(-a,-a, b, a,-b, a), // z+ - aabb3f(-a,-a,-a, a,-b,-b), // z- - aabb3f(-a, b,-a, a, a,-b) // z- - }; - static const aabb3f glass_faces[6] = { - aabb3f(-g, g,-g, g, g, g), // y+ - aabb3f(-g,-g,-g, g,-g, g), // y- - aabb3f( g,-g,-g, g, g, g), // x+ - aabb3f(-g,-g,-g,-g, g, g), // x- - aabb3f(-g,-g, g, g, g, g), // z+ - aabb3f(-g,-g,-g, g, g,-g) // z- - }; - - // table of node visible faces, 0 = invisible - int visible_faces[6] = {0,0,0,0,0,0}; - - // table of neighbours, 1 = same type, checked with g_26dirs - int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; - - // g_26dirs to check when only horizontal merge is allowed - int nb_H_dirs[8] = {0,2,3,5,10,11,12,13}; - - content_t current = n.getContent(); - content_t n2c; - MapNode n2; - v3s16 n2p; - - // neighbours checks for frames visibility - - if (!H_merge && V_merge) { - n2p = blockpos_nodes + p + g_26dirs[1]; - n2 = data->m_vmanip.getNodeNoEx(n2p); - n2c = n2.getContent(); - if (n2c == current || n2c == CONTENT_IGNORE) - nb[1] = 1; - n2p = blockpos_nodes + p + g_26dirs[4]; - n2 = data->m_vmanip.getNodeNoEx(n2p); - n2c = n2.getContent(); - if (n2c == current || n2c == CONTENT_IGNORE) - nb[4] = 1; - } else if (H_merge && !V_merge) { - for(i = 0; i < 8; i++) { - n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]]; - n2 = data->m_vmanip.getNodeNoEx(n2p); - n2c = n2.getContent(); - if (n2c == current || n2c == CONTENT_IGNORE) - nb[nb_H_dirs[i]] = 1; - } - } else if (H_merge && V_merge) { - for(i = 0; i < 18; i++) { - n2p = blockpos_nodes + p + g_26dirs[i]; - n2 = data->m_vmanip.getNodeNoEx(n2p); - n2c = n2.getContent(); - if (n2c == current || n2c == CONTENT_IGNORE) - nb[i] = 1; - } - } - - // faces visibility checks - - if (!V_merge) { - visible_faces[0] = 1; - visible_faces[1] = 1; - } else { - for(i = 0; i < 2; i++) { - n2p = blockpos_nodes + p + dirs[i]; - n2 = data->m_vmanip.getNodeNoEx(n2p); - n2c = n2.getContent(); - if (n2c != current) - visible_faces[i] = 1; - } - } - - if (!H_merge) { - visible_faces[2] = 1; - visible_faces[3] = 1; - visible_faces[4] = 1; - visible_faces[5] = 1; - } else { - for(i = 2; i < 6; i++) { - n2p = blockpos_nodes + p + dirs[i]; - n2 = data->m_vmanip.getNodeNoEx(n2p); - n2c = n2.getContent(); - if (n2c != current) - visible_faces[i] = 1; - } - } - - static const u8 nb_triplet[12*3] = { - 1,2, 7, 1,5, 6, 4,2,15, 4,5,14, - 2,0,11, 2,3,13, 5,0,10, 5,3,12, - 0,1, 8, 0,4,16, 3,4,17, 3,1, 9 - }; - - aabb3f box; - - for(i = 0; i < 12; i++) - { - int edge_invisible; - if (nb[nb_triplet[i*3+2]]) - edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]]; - else - edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]]; - if (edge_invisible) - continue; - box = frame_edges[i]; - makeAutoLightedCuboid(&collector, data, pos, box, tiles[0], tile0color, frame); - } - - for(i = 0; i < 6; i++) - { - if (!visible_faces[i]) - continue; - box = glass_faces[i]; - makeAutoLightedCuboid(&collector, data, pos, box, glass_tiles[i], glasscolor[i], frame); - } - - if (param2 > 0 && f.special_tiles[0].texture) { - // Interior volume level is in range 0 .. 63, - // convert it to -0.5 .. 0.5 - float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0); - TileSpec tile = getSpecialTile(f, n, 0); - video::SColor special_color = encode_light_and_color(l, - tile.color, f.light_source); - float offset = 0.003; - box = aabb3f(visible_faces[3] ? -b : -a + offset, - visible_faces[1] ? -b : -a + offset, - visible_faces[5] ? -b : -a + offset, - visible_faces[2] ? b : a - offset, - visible_faces[0] ? b * vlev : a * vlev - offset, - visible_faces[4] ? b : a - offset); - makeAutoLightedCuboid(&collector, data, pos, box, tile, special_color, frame); - } - break;} - case NDT_ALLFACES: - { - TileSpec tile_leaves = getNodeTile(n, p, - v3s16(0,0,0), data); - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = encode_light_and_color(l, - tile_leaves.color, f.light_source); - - v3f pos = intToFloat(p, BS); - aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2); - makeAutoLightedCuboid(&collector, data, pos, box, tile_leaves, c, frame); - break;} - case NDT_ALLFACES_OPTIONAL: - // This is always pre-converted to something else - FATAL_ERROR("NDT_ALLFACES_OPTIONAL not pre-converted"); - break; - case NDT_TORCHLIKE: - { - v3s16 dir = n.getWallMountedDir(nodedef); - - u8 tileindex = 0; - if(dir == v3s16(0,-1,0)){ - tileindex = 0; // floor - } else if(dir == v3s16(0,1,0)){ - tileindex = 1; // ceiling - // For backwards compatibility - } else if(dir == v3s16(0,0,0)){ - tileindex = 0; // floor - } else { - tileindex = 2; // side - } - - TileSpec tile = getNodeTileN(n, p, tileindex, data); - tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; - tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; - - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = encode_light_and_color(l, tile.color, - f.light_source); - - float s = BS/2*f.visual_scale; - // Wall at X+ of node - video::S3DVertex vertices[4] = - { - video::S3DVertex(-s,-s,0, 0,0,0, c, 0,1), - video::S3DVertex( s,-s,0, 0,0,0, c, 1,1), - video::S3DVertex( s, s,0, 0,0,0, c, 1,0), - video::S3DVertex(-s, s,0, 0,0,0, c, 0,0), - }; - - for (s32 i = 0; i < 4; i++) - { - if(dir == v3s16(1,0,0)) - vertices[i].Pos.rotateXZBy(0); - if(dir == v3s16(-1,0,0)) - vertices[i].Pos.rotateXZBy(180); - if(dir == v3s16(0,0,1)) - vertices[i].Pos.rotateXZBy(90); - if(dir == v3s16(0,0,-1)) - vertices[i].Pos.rotateXZBy(-90); - if(dir == v3s16(0,-1,0)) - vertices[i].Pos.rotateXZBy(45); - if(dir == v3s16(0,1,0)) - vertices[i].Pos.rotateXZBy(-45); - - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); - vertices[i].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(tile, vertices, 4, indices, 6); - break;} - case NDT_SIGNLIKE: - { - TileSpec tile = getNodeTileN(n, p, 0, data); - tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; - tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; - - u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = encode_light_and_color(l, tile.color, - f.light_source); - - float d = (float)BS/16; - float s = BS/2*f.visual_scale; - // Wall at X+ of node - video::S3DVertex vertices[4] = - { - video::S3DVertex(BS/2-d, s, s, 0,0,0, c, 0,0), - video::S3DVertex(BS/2-d, s, -s, 0,0,0, c, 1,0), - video::S3DVertex(BS/2-d, -s, -s, 0,0,0, c, 1,1), - video::S3DVertex(BS/2-d, -s, s, 0,0,0, c, 0,1), - }; - - v3s16 dir = n.getWallMountedDir(nodedef); - - for (s32 i = 0; i < 4; i++) - { - if(dir == v3s16(1,0,0)) - vertices[i].Pos.rotateXZBy(0); - if(dir == v3s16(-1,0,0)) - vertices[i].Pos.rotateXZBy(180); - if(dir == v3s16(0,0,1)) - vertices[i].Pos.rotateXZBy(90); - if(dir == v3s16(0,0,-1)) - vertices[i].Pos.rotateXZBy(-90); - if(dir == v3s16(0,-1,0)) - vertices[i].Pos.rotateXYBy(-90); - if(dir == v3s16(0,1,0)) - vertices[i].Pos.rotateXYBy(90); - - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); - vertices[i].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(tile, vertices, 4, indices, 6); - break;} - case NDT_PLANTLIKE: - { - PseudoRandom rng(x<<8 | z | y<<16); - - TileSpec tile = getNodeTileN(n, p, 0, data); - tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; - - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = encode_light_and_color(l, tile.color, - f.light_source); - - float s = BS / 2 * f.visual_scale; - // add sqrt(2) visual scale - if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x10) != 0)) - s *= 1.41421; - - float random_offset_X = .0; - float random_offset_Z = .0; - if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) { - random_offset_X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); - random_offset_Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); - } - - for (int j = 0; j < 4; j++) { - video::S3DVertex vertices[4] = - { - video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1), - video::S3DVertex( s,-BS/2, 0, 0,0,0, c, 1,1), - video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0), - video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0), - }; - - float rotate_degree = 0; - u8 p2mesh = 0; - if (f.param_type_2 == CPT2_DEGROTATE) - rotate_degree = n.param2 * 2; - if (f.param_type_2 != CPT2_MESHOPTIONS) { - if (j == 0) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(46 + rotate_degree); - } else if (j == 1) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(-44 + rotate_degree); - } - } else { - p2mesh = n.param2 & 0x7; - switch (p2mesh) { - case 0: - // x - if (j == 0) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(46); - } else if (j == 1) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(-44); - } - break; - case 1: - // + - if (j == 0) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(91); - } else if (j == 1) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(1); - } - break; - case 2: - // * - if (j == 0) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(121); - } else if (j == 1) { - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(241); - } else { // (j == 2) - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(1); - } - break; - case 3: - // # - switch (j) { - case 0: - for (u16 i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(1); - vertices[i].Pos.Z += BS / 4; - } - break; - case 1: - for (u16 i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(91); - vertices[i].Pos.X += BS / 4; - } - break; - case 2: - for (u16 i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(181); - vertices[i].Pos.Z -= BS / 4; - } - break; - case 3: - for (u16 i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(271); - vertices[i].Pos.X -= BS / 4; - } - break; - } - break; - case 4: - // outward leaning #-like - switch (j) { - case 0: - for (u16 i = 2; i < 4; i++) - vertices[i].Pos.Z -= BS / 2; - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(1); - break; - case 1: - for (u16 i = 2; i < 4; i++) - vertices[i].Pos.Z -= BS / 2; - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(91); - break; - case 2: - for (u16 i = 2; i < 4; i++) - vertices[i].Pos.Z -= BS / 2; - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(181); - break; - case 3: - for (u16 i = 2; i < 4; i++) - vertices[i].Pos.Z -= BS / 2; - for (u16 i = 0; i < 4; i++) - vertices[i].Pos.rotateXZBy(271); - break; - } - break; - } - } - - for (int i = 0; i < 4; i++) { - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); - vertices[i].Pos += intToFloat(p, BS); - // move to a random spot to avoid moire - if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) { - vertices[i].Pos.X += random_offset_X; - vertices[i].Pos.Z += random_offset_Z; - } - // randomly move each face up/down - if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x20) != 0)) { - PseudoRandom yrng(j | x<<16 | z<<8 | y<<24 ); - vertices[i].Pos.Y -= BS * ((yrng.next() % 16 / 16.0) * 0.125); - } - } - - u16 indices[] = {0, 1, 2, 2, 3, 0}; - // Add to mesh collector - collector.append(tile, vertices, 4, indices, 6); - - // stop adding faces for meshes with less than 4 faces - if (f.param_type_2 == CPT2_MESHOPTIONS) { - if (((p2mesh == 0) || (p2mesh == 1)) && (j == 1)) - break; - else if ((p2mesh == 2) && (j == 2)) - break; - } else if (j == 1) { - break; - } - - } - break;} - case NDT_FIRELIKE: - { - TileSpec tile = getNodeTileN(n, p, 0, data); - tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; - - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = encode_light_and_color(l, tile.color, - f.light_source); - - float s = BS / 2 * f.visual_scale; - - content_t current = n.getContent(); - content_t n2c; - MapNode n2; - v3s16 n2p; - - static const v3s16 dirs[6] = { - v3s16( 0, 1, 0), - v3s16( 0, -1, 0), - v3s16( 1, 0, 0), - v3s16(-1, 0, 0), - v3s16( 0, 0, 1), - v3s16( 0, 0, -1) - }; - - int doDraw[6] = {0, 0, 0, 0, 0, 0}; - - bool drawAllFaces = true; - - // Check for adjacent nodes - for (int i = 0; i < 6; i++) { - n2p = blockpos_nodes + p + dirs[i]; - n2 = data->m_vmanip.getNodeNoEx(n2p); - n2c = n2.getContent(); - if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) { - doDraw[i] = 1; - if (drawAllFaces) - drawAllFaces = false; - - } - } - - for (int j = 0; j < 6; j++) { - - video::S3DVertex vertices[4] = { - video::S3DVertex(-s, -BS / 2, 0, 0, 0, 0, c, 0, 1), - video::S3DVertex( s, -BS / 2, 0, 0, 0, 0, c, 1, 1), - video::S3DVertex( s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 1, 0), - video::S3DVertex(-s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 0, 0), - }; - - // Calculate which faces should be drawn, (top or sides) - if (j == 0 && (drawAllFaces || - (doDraw[3] == 1 || doDraw[1] == 1))) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(90); - vertices[i].Pos.rotateXYBy(-10); - vertices[i].Pos.X -= 4.0; - } - } else if (j == 1 && (drawAllFaces || - (doDraw[5] == 1 || doDraw[1] == 1))) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(180); - vertices[i].Pos.rotateYZBy(10); - vertices[i].Pos.Z -= 4.0; - } - } else if (j == 2 && (drawAllFaces || - (doDraw[2] == 1 || doDraw[1] == 1))) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(270); - vertices[i].Pos.rotateXYBy(10); - vertices[i].Pos.X += 4.0; - } - } else if (j == 3 && (drawAllFaces || - (doDraw[4] == 1 || doDraw[1] == 1))) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateYZBy(-10); - vertices[i].Pos.Z += 4.0; - } - // Center cross-flames - } else if (j == 4 && (drawAllFaces || doDraw[1] == 1)) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(45); - } - } else if (j == 5 && (drawAllFaces || doDraw[1] == 1)) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateXZBy(-45); - } - // Render flames on bottom of node above - } else if (j == 0 && doDraw[0] == 1 && doDraw[1] == 0) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateYZBy(70); - vertices[i].Pos.rotateXZBy(90); - vertices[i].Pos.Y += 4.84; - vertices[i].Pos.X -= 4.7; - } - } else if (j == 1 && doDraw[0] == 1 && doDraw[1] == 0) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateYZBy(70); - vertices[i].Pos.rotateXZBy(180); - vertices[i].Pos.Y += 4.84; - vertices[i].Pos.Z -= 4.7; - } - } else if (j == 2 && doDraw[0] == 1 && doDraw[1] == 0) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateYZBy(70); - vertices[i].Pos.rotateXZBy(270); - vertices[i].Pos.Y += 4.84; - vertices[i].Pos.X += 4.7; - } - } else if (j == 3 && doDraw[0] == 1 && doDraw[1] == 0) { - for (int i = 0; i < 4; i++) { - vertices[i].Pos.rotateYZBy(70); - vertices[i].Pos.Y += 4.84; - vertices[i].Pos.Z += 4.7; - } - } else { - // Skip faces that aren't adjacent to a node - continue; - } - - for (int i = 0; i < 4; i++) { - vertices[i].Pos *= f.visual_scale; - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); - vertices[i].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0, 1, 2, 2, 3, 0}; - // Add to mesh collector - collector.append(tile, vertices, 4, indices, 6); - } - break;} - case NDT_FENCELIKE: - { - TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data); - TileSpec tile_nocrack = tile; - tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK; - - // Put wood the right way around in the posts - TileSpec tile_rot = tile; - tile_rot.rotation = 1; - - u16 l = getInteriorLight(n, 1, nodedef); - video::SColor c = encode_light_and_color(l, tile.color, - f.light_source); - - const f32 post_rad=(f32)BS/8; - const f32 bar_rad=(f32)BS/16; - const f32 bar_len=(f32)(BS/2)-post_rad; - - v3f pos = intToFloat(p, BS); - - // The post - always present - aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad); - f32 postuv[24]={ - 6/16.,6/16.,10/16.,10/16., - 6/16.,6/16.,10/16.,10/16., - 0/16.,0,4/16.,1, - 4/16.,0,8/16.,1, - 8/16.,0,12/16.,1, - 12/16.,0,16/16.,1}; - makeAutoLightedCuboidEx(&collector, data, pos, post, tile_rot, postuv, c, frame); - - // Now a section of fence, +X, if there's a post there - v3s16 p2 = p; - p2.X++; - MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - const ContentFeatures *f2 = &nodedef->get(n2); - if(f2->drawtype == NDT_FENCELIKE) - { - aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad, - bar_len+BS/2,bar_rad+BS/4,bar_rad); - f32 xrailuv[24]={ - 0/16.,2/16.,16/16.,4/16., - 0/16.,4/16.,16/16.,6/16., - 6/16.,6/16.,8/16.,8/16., - 10/16.,10/16.,12/16.,12/16., - 0/16.,8/16.,16/16.,10/16., - 0/16.,14/16.,16/16.,16/16.}; - makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame); - bar.MinEdge.Y -= BS/2; - bar.MaxEdge.Y -= BS/2; - makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame); - } - - // Now a section of fence, +Z, if there's a post there - p2 = p; - p2.Z++; - n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2); - f2 = &nodedef->get(n2); - if(f2->drawtype == NDT_FENCELIKE) - { - aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2, - bar_rad,bar_rad+BS/4,bar_len+BS/2); - f32 zrailuv[24]={ - 3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch - 4/16.,1/16.,6/16.,5/16., // for wood texture instead - 0/16.,9/16.,16/16.,11/16., - 0/16.,6/16.,16/16.,8/16., - 6/16.,6/16.,8/16.,8/16., - 10/16.,10/16.,12/16.,12/16.}; - makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame); - bar.MinEdge.Y -= BS/2; - bar.MaxEdge.Y -= BS/2; - makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame); - } - break;} - case NDT_RAILLIKE: - { - bool is_rail_x[6]; /* (-1,-1,0) X (1,-1,0) (-1,0,0) X (1,0,0) (-1,1,0) X (1,1,0) */ - bool is_rail_z[6]; - - content_t thiscontent = n.getContent(); - std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind - int self_group = ((ItemGroupList) nodedef->get(n).groups)[groupname]; - - u8 index = 0; - for (s8 y0 = -1; y0 <= 1; y0++) { - // Prevent from indexing never used coordinates - for (s8 xz = -1; xz <= 1; xz++) { - if (xz == 0) - continue; - MapNode n_xy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x + xz, y + y0, z)); - MapNode n_zy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y + y0, z + xz)); - const ContentFeatures &def_xy = nodedef->get(n_xy); - const ContentFeatures &def_zy = nodedef->get(n_zy); - - // Check if current node would connect with the rail - is_rail_x[index] = ((def_xy.drawtype == NDT_RAILLIKE - && ((ItemGroupList) def_xy.groups)[groupname] == self_group) - || n_xy.getContent() == thiscontent); - - is_rail_z[index] = ((def_zy.drawtype == NDT_RAILLIKE - && ((ItemGroupList) def_zy.groups)[groupname] == self_group) - || n_zy.getContent() == thiscontent); - index++; - } - } - - bool is_rail_x_all[2]; // [0] = negative x, [1] = positive x coordinate from the current node position - bool is_rail_z_all[2]; - is_rail_x_all[0] = is_rail_x[0] || is_rail_x[2] || is_rail_x[4]; - is_rail_x_all[1] = is_rail_x[1] || is_rail_x[3] || is_rail_x[5]; - is_rail_z_all[0] = is_rail_z[0] || is_rail_z[2] || is_rail_z[4]; - is_rail_z_all[1] = is_rail_z[1] || is_rail_z[3] || is_rail_z[5]; - - // reasonable default, flat straight unrotated rail - bool is_straight = true; - int adjacencies = 0; - int angle = 0; - u8 tileindex = 0; - - // check for sloped rail - if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5]) { - adjacencies = 5; // 5 means sloped - is_straight = true; // sloped is always straight - } else { - // is really straight, rails on both sides - is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]); - adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1]; - } - - switch (adjacencies) { - case 1: - if (is_rail_x_all[0] || is_rail_x_all[1]) - angle = 90; - break; - case 2: - if (!is_straight) - tileindex = 1; // curved - if (is_rail_x_all[0] && is_rail_x_all[1]) - angle = 90; - if (is_rail_z_all[0] && is_rail_z_all[1]) { - if (is_rail_z[4]) - angle = 180; - } - else if (is_rail_x_all[0] && is_rail_z_all[0]) - angle = 270; - else if (is_rail_x_all[0] && is_rail_z_all[1]) - angle = 180; - else if (is_rail_x_all[1] && is_rail_z_all[1]) - angle = 90; - break; - case 3: - // here is where the potential to 'switch' a junction is, but not implemented at present - tileindex = 2; // t-junction - if(!is_rail_x_all[1]) - angle = 180; - if(!is_rail_z_all[0]) - angle = 90; - if(!is_rail_z_all[1]) - angle = 270; - break; - case 4: - tileindex = 3; // crossing - break; - case 5: //sloped - if (is_rail_z[4]) - angle = 180; - if (is_rail_x[4]) - angle = 90; - if (is_rail_x[5]) - angle = -90; - break; - default: - break; - } - - TileSpec tile = getNodeTileN(n, p, tileindex, data); - tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; - tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; - - u16 l = getInteriorLight(n, 0, nodedef); - video::SColor c = encode_light_and_color(l, tile.color, - f.light_source); - - float d = (float)BS/64; - float s = BS/2; - - short g = -1; - if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5]) - g = 1; //Object is at a slope - - video::S3DVertex vertices[4] = - { - video::S3DVertex(-s, -s+d, -s, 0, 0, 0, c, 0, 1), - video::S3DVertex( s, -s+d, -s, 0, 0, 0, c, 1, 1), - video::S3DVertex( s, g*s+d, s, 0, 0, 0, c, 1, 0), - video::S3DVertex(-s, g*s+d, s, 0, 0, 0, c, 0, 0), - }; - - for(s32 i=0; i<4; i++) - { - if(angle != 0) - vertices[i].Pos.rotateXZBy(angle); - if (data->m_smooth_lighting) - vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color); - vertices[i].Pos += intToFloat(p, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - collector.append(tile, vertices, 4, indices, 6); - break;} - case NDT_NODEBOX: - { - static const v3s16 tile_dirs[6] = { - v3s16(0, 1, 0), - v3s16(0, -1, 0), - v3s16(1, 0, 0), - v3s16(-1, 0, 0), - v3s16(0, 0, 1), - v3s16(0, 0, -1) - }; - - TileSpec tiles[6]; - video::SColor colors[6]; - for (int j = 0; j < 6; j++) { - // Handles facedir rotation for textures - tiles[j] = getNodeTile(n, p, tile_dirs[j], data); - } - if (!data->m_smooth_lighting) { - u16 l = getInteriorLight(n, 1, nodedef); - for (int j = 0; j < 6; j++) - colors[j] = encode_light_and_color(l, tiles[j].color, f.light_source); - } - - v3f pos = intToFloat(p, BS); - - int neighbors = 0; - - // locate possible neighboring nodes to connect to - if (f.node_box.type == NODEBOX_CONNECTED) { - v3s16 p2 = p; - - p2.Y++; - getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors); - - p2 = p; - p2.Y--; - getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors); - - p2 = p; - p2.Z--; - getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors); - - p2 = p; - p2.X--; - getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors); - - p2 = p; - p2.Z++; - getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors); - - p2 = p; - p2.X++; - getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors); - } - - std::vector boxes; - n.getNodeBoxes(nodedef, &boxes, neighbors); - for (std::vector::iterator - i = boxes.begin(); - i != boxes.end(); ++i) { - aabb3f box = *i; - - f32 dx1 = box.MinEdge.X; - f32 dy1 = box.MinEdge.Y; - f32 dz1 = box.MinEdge.Z; - f32 dx2 = box.MaxEdge.X; - f32 dy2 = box.MaxEdge.Y; - f32 dz2 = box.MaxEdge.Z; - - box.MinEdge += pos; - box.MaxEdge += pos; - - if (box.MinEdge.X > box.MaxEdge.X) - std::swap(box.MinEdge.X, box.MaxEdge.X); - if (box.MinEdge.Y > box.MaxEdge.Y) - std::swap(box.MinEdge.Y, box.MaxEdge.Y); - if (box.MinEdge.Z > box.MaxEdge.Z) - std::swap(box.MinEdge.Z, box.MaxEdge.Z); - - // - // Compute texture coords - f32 tx1 = (box.MinEdge.X/BS)+0.5; - f32 ty1 = (box.MinEdge.Y/BS)+0.5; - f32 tz1 = (box.MinEdge.Z/BS)+0.5; - f32 tx2 = (box.MaxEdge.X/BS)+0.5; - f32 ty2 = (box.MaxEdge.Y/BS)+0.5; - f32 tz2 = (box.MaxEdge.Z/BS)+0.5; - f32 txc[24] = { - // up - tx1, 1-tz2, tx2, 1-tz1, - // down - tx1, tz1, tx2, tz2, - // right - tz1, 1-ty2, tz2, 1-ty1, - // left - 1-tz2, 1-ty2, 1-tz1, 1-ty1, - // back - 1-tx2, 1-ty2, 1-tx1, 1-ty1, - // front - tx1, 1-ty2, tx2, 1-ty1, - }; - if (data->m_smooth_lighting) { - u16 lights[8]; - for (int j = 0; j < 8; ++j) { - f32 x = (j & 4) ? dx2 : dx1; - f32 y = (j & 2) ? dy2 : dy1; - f32 z = (j & 1) ? dz2 : dz1; - lights[j] = blendLight(frame, core::vector3df(x, y, z)); - } - makeSmoothLightedCuboid(&collector, box, tiles, 6, lights, txc, f.light_source); - } else { - makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source); - } - } - break;} - case NDT_MESH: - { - v3f pos = intToFloat(p, BS); - u16 l = getInteriorLight(n, 1, nodedef); - u8 facedir = 0; - if (f.param_type_2 == CPT2_FACEDIR || - f.param_type_2 == CPT2_COLORED_FACEDIR) { - facedir = n.getFaceDir(nodedef); - } else if (f.param_type_2 == CPT2_WALLMOUNTED || - f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { - //convert wallmounted to 6dfacedir. - //when cache enabled, it is already converted - facedir = n.getWallMounted(nodedef); - if (!enable_mesh_cache) { - static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2}; - facedir = wm_to_6d[facedir]; - } - } - - if (!data->m_smooth_lighting && f.mesh_ptr[facedir]) { - // use cached meshes - for (u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) { - const TileSpec &tile = getNodeTileN(n, p, j, data); - scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j); - collector.append(tile, (video::S3DVertex *) - buf->getVertices(), buf->getVertexCount(), - buf->getIndices(), buf->getIndexCount(), pos, - encode_light_and_color(l, tile.color, f.light_source), - f.light_source); - } - } else if (f.mesh_ptr[0]) { - // no cache, clone and rotate mesh - scene::IMesh* mesh = cloneMesh(f.mesh_ptr[0]); - rotateMeshBy6dFacedir(mesh, facedir); - recalculateBoundingBox(mesh); - meshmanip->recalculateNormals(mesh, true, false); - for (u16 j = 0; j < mesh->getMeshBufferCount(); j++) { - const TileSpec &tile = getNodeTileN(n, p, j, data); - scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); - u32 vertex_count = buf->getVertexCount(); - if (data->m_smooth_lighting) { - for (u16 m = 0; m < vertex_count; ++m) { - video::S3DVertex &vertex = vertices[m]; - vertex.Color = blendLight(frame, vertex.Pos, vertex.Normal, tile.color); - vertex.Pos += pos; - } - collector.append(tile, vertices, vertex_count, - buf->getIndices(), buf->getIndexCount()); - } else { - collector.append(tile, vertices, vertex_count, - buf->getIndices(), buf->getIndexCount(), pos, - encode_light_and_color(l, tile.color, f.light_source), - f.light_source); - } - } - mesh->drop(); - } - break;} - } + origin = intToFloat(p, BS); + drawNode(); } } - diff --git a/src/content_mapblock.h b/src/content_mapblock.h index bb1e129da..c8425024f 100644 --- a/src/content_mapblock.h +++ b/src/content_mapblock.h @@ -19,11 +19,129 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef CONTENT_MAPBLOCK_HEADER #define CONTENT_MAPBLOCK_HEADER +#include "util/numeric.h" +#include "nodedef.h" +#include struct MeshMakeData; struct MeshCollector; -void mapblock_mesh_generate_special(MeshMakeData *data, - MeshCollector &collector); + +struct LightFrame +{ + f32 lightsA[8]; + f32 lightsB[8]; +}; + +class MapblockMeshGenerator +{ +public: + MeshMakeData *data; + MeshCollector *collector; + + INodeDefManager *nodedef; + scene::ISceneManager *smgr; + scene::IMeshManipulator *meshmanip; + +// options + bool enable_mesh_cache; + +// current node + v3s16 blockpos_nodes; + v3s16 p; + v3f origin; + MapNode n; + const ContentFeatures *f; + u16 light; + LightFrame frame; + video::SColor color; + TileSpec tile; + float scale; + +// lighting + void getSmoothLightFrame(); + u16 blendLight(const v3f &vertex_pos); + video::SColor blendLight(const v3f &vertex_pos, video::SColor tile_color); + video::SColor blendLight(const v3f &vertex_pos, const v3f &vertex_normal, video::SColor tile_color); + + void useTile(int index, bool disable_backface_culling); + void useDefaultTile(bool set_color = true); + TileSpec getTile(const v3s16 &direction); + +// face drawing + void drawQuad(v3f *vertices, const v3s16 &normal = v3s16(0, 0, 0)); + +// cuboid drawing! + void drawCuboid(const aabb3f &box, TileSpec *tiles, int tilecount, + const u16 *lights , const f32 *txc); + void generateCuboidTextureCoords(aabb3f const &box, f32 *coords); + void drawAutoLightedCuboid(aabb3f box, const f32 *txc = NULL, + TileSpec *tiles = NULL, int tile_count = 0); + +// liquid-specific + bool top_is_same_liquid; + TileSpec tile_liquid; + TileSpec tile_liquid_top; + content_t c_flowing; + content_t c_source; + video::SColor color_liquid_top; + struct NeighborData { + f32 level; + content_t content; + bool is_same_liquid; + bool top_is_same_liquid; + }; + NeighborData liquid_neighbors[3][3]; + f32 corner_levels[2][2]; + + void prepareLiquidNodeDrawing(bool flowing); + void getLiquidNeighborhood(bool flowing); + void resetCornerLevels(); + void calculateCornerLevels(); + f32 getCornerLevel(int i, int k); + void drawLiquidSides(bool flowing); + void drawLiquidTop(bool flowing); + +// raillike-specific + // name of the group that enables connecting to raillike nodes of different kind + static const std::string raillike_groupname; + int raillike_group; + bool isSameRail(v3s16 dir); + +// plantlike-specific + PlantlikeStyle draw_style; + v3f offset; + int rotate_degree; + bool random_offset_Y; + int face_num; + + void drawPlantlikeQuad(float rotation, float quad_offset = 0, + bool offset_top_only = false); + +// firelike-specific + void drawFirelikeQuad(float rotation, float opening_angle, + float offset_h, float offset_v = 0.0); + +// drawtypes + void drawLiquidNode(bool flowing); + void drawGlasslikeNode(); + void drawGlasslikeFramedNode(); + void drawAllfacesNode(); + void drawTorchlikeNode(); + void drawSignlikeNode(); + void drawPlantlikeNode(); + void drawFirelikeNode(); + void drawFencelikeNode(); + void drawRaillikeNode(); + void drawNodeboxNode(); + void drawMeshNode(); + +// common + void errorUnknownDrawtype(); + void drawNode(); + +public: + MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output); + void generate(); +}; #endif - diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index 2520b5459..f76033ea8 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -1108,7 +1108,10 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset): - whatever */ - mapblock_mesh_generate_special(data, collector); + { + MapblockMeshGenerator generator(data, &collector); + generator.generate(); + } /* Convert MeshCollector to SMesh diff --git a/src/nodedef.h b/src/nodedef.h index 6275b41ce..4a9908ecc 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -166,6 +166,19 @@ enum NodeDrawType NDT_MESH, // Uses static meshes }; +// Mesh options for NDT_PLANTLIKE with CPT2_MESHOPTIONS +static const u8 MO_MASK_STYLE = 0x07; +static const u8 MO_BIT_RANDOM_OFFSET = 0x08; +static const u8 MO_BIT_SCALE_SQRT2 = 0x10; +static const u8 MO_BIT_RANDOM_OFFSET_Y = 0x20; +enum PlantlikeStyle { + PLANT_STYLE_CROSS, + PLANT_STYLE_CROSS2, + PLANT_STYLE_STAR, + PLANT_STYLE_HASH, + PLANT_STYLE_HASH2, +}; + /* Stand-alone definition of a TileSpec (basically a server-side TileSpec) */ @@ -364,6 +377,11 @@ struct ContentFeatures return (liquid_alternative_flowing == f.liquid_alternative_flowing); } + int getGroup(const std::string &group) const + { + return itemgroup_get(groups, group); + } + #ifndef SERVER void fillTileAttribs(ITextureSource *tsrc, TileSpec *tile, TileDef *tiledef, u32 shader_id, bool use_normal_texture, bool backface_culling, diff --git a/src/util/directiontables.h b/src/util/directiontables.h index 0dd3aab09..3cfe0fb3e 100644 --- a/src/util/directiontables.h +++ b/src/util/directiontables.h @@ -30,5 +30,59 @@ extern const v3s16 g_26dirs[26]; // 26th is (0,0,0) extern const v3s16 g_27dirs[27]; -#endif +/// Direction in the 6D format. g_27dirs contains corresponding vectors. +/// Here P means Positive, N stands for Negative. +enum Direction6D { +// 0 + D6D_ZP, + D6D_YP, + D6D_XP, + D6D_ZN, + D6D_YN, + D6D_XN, +// 6 + D6D_XN_YP, + D6D_XP_YP, + D6D_YP_ZP, + D6D_YP_ZN, + D6D_XN_ZP, + D6D_XP_ZP, + D6D_XN_ZN, + D6D_XP_ZN, + D6D_XN_YN, + D6D_XP_YN, + D6D_YN_ZP, + D6D_YN_ZN, +// 18 + D6D_XN_YP_ZP, + D6D_XP_YP_ZP, + D6D_XN_YP_ZN, + D6D_XP_YP_ZN, + D6D_XN_YN_ZP, + D6D_XP_YN_ZP, + D6D_XN_YN_ZN, + D6D_XP_YN_ZN, +// 26 + D6D, +// aliases + D6D_BACK = D6D_ZP, + D6D_TOP = D6D_YP, + D6D_RIGHT = D6D_XP, + D6D_FRONT = D6D_ZN, + D6D_BOTTOM = D6D_YN, + D6D_LEFT = D6D_XN, +}; + +/// Direction in the wallmounted format. +/// P is Positive, N is Negative. +enum DirectionWallmounted { + DWM_YP, + DWM_YN, + DWM_XP, + DWM_XN, + DWM_ZP, + DWM_ZN, +}; + +#endif