462 changes: 235 additions & 227 deletions src/client/content_mapblock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,27 +74,27 @@ static const auto &quad_indices = quad_indices_02;
const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";

MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output,
scene::IMeshManipulator *mm):
scene::IMeshManipulator *mm):
data(input),
collector(output),
nodedef(data->m_client->ndef()),
meshmanip(mm),
blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE)
blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE),
enable_mesh_cache(g_settings->getBool("enable_mesh_cache") &&
!data->m_smooth_lighting) // Mesh cache is not supported with smooth lighting
{
enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
!data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
}

void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, bool special)
{
if (special)
getSpecialTile(index, &tile, p == data->m_crack_pos_relative);
getSpecialTile(index, &cur_node.tile, cur_node.p == data->m_crack_pos_relative);
else
getTile(index, &tile);
getTile(index, &cur_node.tile);
if (!data->m_smooth_lighting)
color = encode_light(light, f->light_source);
cur_node.color = encode_light(cur_node.light, cur_node.f->light_source);

for (auto &layer : tile.layers) {
for (auto &layer : cur_node.tile.layers) {
layer.material_flags |= set_flags;
layer.material_flags &= ~reset_flags;
}
Expand All @@ -103,19 +103,19 @@ void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, boo
// Returns a tile, ready for use, non-rotated.
void MapblockMeshGenerator::getTile(int index, TileSpec *tile)
{
getNodeTileN(n, p, index, data, *tile);
getNodeTileN(cur_node.n, cur_node.p, index, data, *tile);
}

// Returns a tile, ready for use, rotated according to the node facedir.
void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
{
getNodeTile(n, p, direction, data, *tile);
getNodeTile(cur_node.n, cur_node.p, direction, data, *tile);
}

// Returns a special tile, ready for use, non-rotated.
void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
{
*tile = f->special_tiles[index];
*tile = cur_node.f->special_tiles[index];
TileLayer *top_layer = nullptr;

for (auto &layernum : tile->layers) {
Expand All @@ -124,7 +124,7 @@ void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply
continue;
top_layer = layer;
if (!layer->has_color)
n.getColor(*f, &layer->color);
cur_node.n.getColor(*cur_node.f, &layer->color);
}

if (apply_crack)
Expand All @@ -137,20 +137,20 @@ void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
video::S3DVertex vertices[4];
bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
bool shade_face = !cur_node.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].Pos = coords[j] + cur_node.origin;
vertices[j].Normal = normal2;
if (data->m_smooth_lighting)
vertices[j].Color = blendLightColor(coords[j]);
else
vertices[j].Color = color;
vertices[j].Color = cur_node.color;
if (shade_face)
applyFacesShading(vertices[j].Color, normal2);
vertices[j].TCoords = tcoords[j];
}
collector->append(tile, vertices, 4, quad_indices, 6);
collector->append(cur_node.tile, vertices, 4, quad_indices, 6);
}

static std::array<video::S3DVertex, 24> setupCuboidVertices(const aabb3f &box, const f32 *txc, TileSpec *tiles, int tilecount) {
Expand Down Expand Up @@ -255,16 +255,16 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
void MapblockMeshGenerator::getSmoothLightFrame()
{
for (int k = 0; k < 8; ++k)
frame.sunlight[k] = false;
cur_node.frame.sunlight[k] = false;
for (int k = 0; k < 8; ++k) {
LightPair light(getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data));
frame.lightsDay[k] = light.lightDay;
frame.lightsNight[k] = light.lightNight;
LightPair light(getSmoothLightTransparent(blockpos_nodes + cur_node.p, light_dirs[k], data));
cur_node.frame.lightsDay[k] = light.lightDay;
cur_node.frame.lightsNight[k] = light.lightNight;
// If there is direct sunlight and no ambient occlusion at some corner,
// mark the vertical edge (top and bottom corners) containing it.
if (light.lightDay == 255) {
frame.sunlight[k] = true;
frame.sunlight[k ^ 2] = true;
cur_node.frame.sunlight[k] = true;
cur_node.frame.sunlight[k ^ 2] = true;
}
}
}
Expand All @@ -287,9 +287,9 @@ LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
f32 dy = (k & 2) ? y : 1 - y;
f32 dz = (k & 1) ? z : 1 - z;
// Use direct sunlight (255), if any; use daylight otherwise.
f32 light_boosted = frame.sunlight[k] ? 255 : frame.lightsDay[k];
lightDay += dx * dy * dz * frame.lightsDay[k];
lightNight += dx * dy * dz * frame.lightsNight[k];
f32 light_boosted = cur_node.frame.sunlight[k] ? 255 : cur_node.frame.lightsDay[k];
lightDay += dx * dy * dz * cur_node.frame.lightsDay[k];
lightNight += dx * dy * dz * cur_node.frame.lightsNight[k];
lightBoosted += dx * dy * dz * light_boosted;
}
return LightInfo{lightDay, lightNight, lightBoosted};
Expand All @@ -301,15 +301,16 @@ LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
{
LightInfo light = blendLight(vertex_pos);
return encode_light(light.getPair(), f->light_source);
return encode_light(light.getPair(), cur_node.f->light_source);
}

video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
const v3f &vertex_normal)
{
LightInfo light = blendLight(vertex_pos);
video::SColor color = encode_light(light.getPair(MYMAX(0.0f, vertex_normal.Y)), f->light_source);
if (!f->light_source)
video::SColor color = encode_light(light.getPair(MYMAX(0.0f, vertex_normal.Y)),
cur_node.f->light_source);
if (!cur_node.f->light_source)
applyFacesShading(color, vertex_normal);
return color;
}
Expand Down Expand Up @@ -342,7 +343,7 @@ static inline int lightDiff(LightPair a, LightPair b)
void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
TileSpec *tiles, int tile_count, u8 mask)
{
bool scale = std::fabs(f->visual_scale - 1.0f) > 1e-3f;
bool scale = std::fabs(cur_node.f->visual_scale - 1.0f) > 1e-3f;
f32 texture_coord_buf[24];
f32 dx1 = box.MinEdge.X;
f32 dy1 = box.MinEdge.Y;
Expand All @@ -355,17 +356,17 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
generateCuboidTextureCoords(box, texture_coord_buf);
txc = texture_coord_buf;
}
box.MinEdge *= f->visual_scale;
box.MaxEdge *= f->visual_scale;
box.MinEdge *= cur_node.f->visual_scale;
box.MaxEdge *= cur_node.f->visual_scale;
}
box.MinEdge += origin;
box.MaxEdge += origin;
box.MinEdge += cur_node.origin;
box.MaxEdge += cur_node.origin;
if (!txc) {
generateCuboidTextureCoords(box, texture_coord_buf);
txc = texture_coord_buf;
}
if (!tiles) {
tiles = &tile;
tiles = &cur_node.tile;
tile_count = 1;
}
if (data->m_smooth_lighting) {
Expand All @@ -383,8 +384,8 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
for (int j = 0; j < 4; j++) {
video::S3DVertex &vertex = vertices[j];
final_lights[j] = lights[light_indices[face][j]].getPair(MYMAX(0.0f, vertex.Normal.Y));
vertex.Color = encode_light(final_lights[j], f->light_source);
if (!f->light_source)
vertex.Color = encode_light(final_lights[j], cur_node.f->light_source);
if (!cur_node.f->light_source)
applyFacesShading(vertex.Color, vertex.Normal);
}
if (lightDiff(final_lights[1], final_lights[3]) < lightDiff(final_lights[0], final_lights[2]))
Expand All @@ -393,8 +394,8 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
});
} else {
drawCuboid(box, tiles, tile_count, txc, mask, [&] (int face, video::S3DVertex vertices[4]) {
video::SColor color = encode_light(light, f->light_source);
if (!f->light_source)
video::SColor color = encode_light(cur_node.light, cur_node.f->light_source);
if (!cur_node.f->light_source)
applyFacesShading(color, vertices[0].Normal);
for (int j = 0; j < 4; j++) {
video::S3DVertex &vertex = vertices[j];
Expand All @@ -418,12 +419,12 @@ void MapblockMeshGenerator::drawSolidNode()
};
TileSpec tiles[6];
u16 lights[6];
content_t n1 = n.getContent();
content_t n1 = cur_node.n.getContent();
for (int face = 0; face < 6; face++) {
v3s16 p2 = blockpos_nodes + p + tile_dirs[face];
v3s16 p2 = blockpos_nodes + cur_node.p + tile_dirs[face];
MapNode neighbor = data->m_vmanip.getNodeNoEx(p2);
content_t n2 = neighbor.getContent();
bool backface_culling = f->drawtype == NDT_NORMAL;
bool backface_culling = cur_node.f->drawtype == NDT_NORMAL;
if (n2 == n1)
continue;
if (n2 == CONTENT_IGNORE)
Expand All @@ -432,8 +433,8 @@ void MapblockMeshGenerator::drawSolidNode()
const ContentFeatures &f2 = nodedef->get(n2);
if (f2.solidness == 2)
continue;
if (f->drawtype == NDT_LIQUID) {
if (f->sameLiquidRender(f2))
if (cur_node.f->drawtype == NDT_LIQUID) {
if (cur_node.f->sameLiquidRender(f2))
continue;
backface_culling = f2.solidness || f2.visual_solidness;
}
Expand All @@ -447,33 +448,34 @@ void MapblockMeshGenerator::drawSolidNode()
layer.material_flags |= MATERIAL_FLAG_TILEABLE_VERTICAL;
}
if (!data->m_smooth_lighting) {
lights[face] = getFaceLight(n, neighbor, nodedef);
lights[face] = getFaceLight(cur_node.n, neighbor, nodedef);
}
}
if (!faces)
return;
u8 mask = faces ^ 0b0011'1111; // k-th bit is set if k-th face is to be *omitted*, as expected by cuboid drawing functions.
origin = intToFloat(p, BS);
cur_node.origin = intToFloat(cur_node.p, BS);
auto box = aabb3f(v3f(-0.5 * BS), v3f(0.5 * BS));
f32 texture_coord_buf[24];
box.MinEdge += origin;
box.MaxEdge += origin;
box.MinEdge += cur_node.origin;
box.MaxEdge += cur_node.origin;
generateCuboidTextureCoords(box, texture_coord_buf);
if (data->m_smooth_lighting) {
LightPair lights[6][4];
for (int face = 0; face < 6; ++face) {
for (int k = 0; k < 4; k++) {
v3s16 corner = light_dirs[light_indices[face][k]];
lights[face][k] = LightPair(getSmoothLightSolid(blockpos_nodes + p, tile_dirs[face], corner, data));
lights[face][k] = LightPair(getSmoothLightSolid(
blockpos_nodes + cur_node.p, tile_dirs[face], corner, data));
}
}

drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) {
auto final_lights = lights[face];
for (int j = 0; j < 4; j++) {
video::S3DVertex &vertex = vertices[j];
vertex.Color = encode_light(final_lights[j], f->light_source);
if (!f->light_source)
vertex.Color = encode_light(final_lights[j], cur_node.f->light_source);
if (!cur_node.f->light_source)
applyFacesShading(vertex.Color, vertex.Normal);
}
if (lightDiff(final_lights[1], final_lights[3]) < lightDiff(final_lights[0], final_lights[2]))
Expand All @@ -482,8 +484,8 @@ void MapblockMeshGenerator::drawSolidNode()
});
} else {
drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) {
video::SColor color = encode_light(lights[face], f->light_source);
if (!f->light_source)
video::SColor color = encode_light(lights[face], cur_node.f->light_source);
if (!cur_node.f->light_source)
applyFacesShading(color, vertices[0].Normal);
for (int j = 0; j < 4; j++) {
video::S3DVertex &vertex = vertices[j];
Expand Down Expand Up @@ -517,7 +519,7 @@ u8 MapblockMeshGenerator::getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 same
(box.MinEdge.Z == -NODE_BOUNDARY ? 32 : 0);

u8 sametype_mask = 0;
if (f->alpha == AlphaMode::ALPHAMODE_OPAQUE) {
if (cur_node.f->alpha == AlphaMode::ALPHAMODE_OPAQUE) {
// In opaque nodeboxes, faces on opposite sides can cancel
// each other out if there is a matching neighbor of the same type
sametype_mask =
Expand All @@ -533,46 +535,49 @@ u8 MapblockMeshGenerator::getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 same

void MapblockMeshGenerator::prepareLiquidNodeDrawing()
{
getSpecialTile(0, &tile_liquid_top);
getSpecialTile(1, &tile_liquid);

MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
c_flowing = f->liquid_alternative_flowing_id;
c_source = f->liquid_alternative_source_id;
top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
if (draw_liquid_bottom) {
getSpecialTile(0, &cur_liquid.tile_top);
getSpecialTile(1, &cur_liquid.tile);

MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p + v3s16(0, 1, 0));
MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p + v3s16(0, -1, 0));
cur_liquid.c_flowing = cur_node.f->liquid_alternative_flowing_id;
cur_liquid.c_source = cur_node.f->liquid_alternative_source_id;
cur_liquid.top_is_same_liquid = (ntop.getContent() == cur_liquid.c_flowing)
|| (ntop.getContent() == cur_liquid.c_source);
cur_liquid.draw_bottom = (nbottom.getContent() != cur_liquid.c_flowing)
&& (nbottom.getContent() != cur_liquid.c_source);
if (cur_liquid.draw_bottom) {
const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
if (f2.solidness > 1)
draw_liquid_bottom = false;
cur_liquid.draw_bottom = false;
}

if (data->m_smooth_lighting)
return; // don't need to pre-compute anything in this case

if (f->light_source != 0) {
if (cur_node.f->light_source != 0) {
// If this liquid emits light and doesn't contain light, draw
// it at what it emits, for an increased effect
u8 e = decode_light(f->light_source);
light = LightPair(std::max(e, light.lightDay), std::max(e, light.lightNight));
u8 e = decode_light(cur_node.f->light_source);
cur_node.light = LightPair(std::max(e, cur_node.light.lightDay),
std::max(e, cur_node.light.lightNight));
} else if (nodedef->getLightingFlags(ntop).has_light) {
// Otherwise, use the light of the node on top if possible
light = LightPair(getInteriorLight(ntop, 0, nodedef));
cur_node.light = LightPair(getInteriorLight(ntop, 0, nodedef));
}

color_liquid_top = encode_light(light, f->light_source);
color = encode_light(light, f->light_source);
cur_liquid.color_top = encode_light(cur_node.light, cur_node.f->light_source);
cur_node.color = encode_light(cur_node.light, cur_node.f->light_source);
}

void MapblockMeshGenerator::getLiquidNeighborhood()
{
u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
u8 range = rangelim(nodedef->get(cur_liquid.c_flowing).liquid_range, 1, 8);

for (int w = -1; w <= 1; w++)
for (int u = -1; u <= 1; u++) {
NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
v3s16 p2 = p + v3s16(u, 0, w);
LiquidData::NeighborData &neighbor = cur_liquid.neighbors[w + 1][u + 1];
v3s16 p2 = cur_node.p + v3s16(u, 0, w);
MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
neighbor.content = n2.getContent();
neighbor.level = -0.5f;
Expand All @@ -582,10 +587,10 @@ void MapblockMeshGenerator::getLiquidNeighborhood()
if (neighbor.content == CONTENT_IGNORE)
continue;

if (neighbor.content == c_source) {
if (neighbor.content == cur_liquid.c_source) {
neighbor.is_same_liquid = true;
neighbor.level = 0.5f;
} else if (neighbor.content == c_flowing) {
} else if (neighbor.content == cur_liquid.c_flowing) {
neighbor.is_same_liquid = true;
u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
Expand All @@ -600,7 +605,7 @@ void MapblockMeshGenerator::getLiquidNeighborhood()
// doesn't exist
p2.Y++;
n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
if (n2.getContent() == c_source || n2.getContent() == c_flowing)
if (n2.getContent() == cur_liquid.c_source || n2.getContent() == cur_liquid.c_flowing)
neighbor.top_is_same_liquid = true;
}
}
Expand All @@ -609,7 +614,7 @@ void MapblockMeshGenerator::calculateCornerLevels()
{
for (int k = 0; k < 2; k++)
for (int i = 0; i < 2; i++)
corner_levels[k][i] = getCornerLevel(i, k);
cur_liquid.corner_levels[k][i] = getCornerLevel(i, k);
}

f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
Expand All @@ -619,19 +624,19 @@ f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
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];
LiquidData::NeighborData &neighbor_data = cur_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.5f;

// Source always has the full height
if (content == c_source)
if (content == cur_liquid.c_source)
return 0.5f;

// Flowing liquid has level information
if (content == c_flowing) {
if (content == cur_liquid.c_flowing) {
sum += neighbor_data.level;
count++;
} else if (content == CONTENT_AIR) {
Expand Down Expand Up @@ -670,13 +675,13 @@ namespace {
void MapblockMeshGenerator::drawLiquidSides()
{
for (const auto &face : liquid_base_faces) {
const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
const LiquidData::NeighborData &neighbor = cur_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 (!top_is_same_liquid)
if (!cur_liquid.top_is_same_liquid)
continue;
if (neighbor.top_is_same_liquid)
continue;
Expand All @@ -697,20 +702,20 @@ void MapblockMeshGenerator::drawLiquidSides()
pos.X = (base.X - 0.5f) * BS;
pos.Z = (base.Z - 0.5f) * BS;
if (vertex.v) {
pos.Y = (neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5f) * BS;
} else if (top_is_same_liquid) {
pos.Y = (neighbor.is_same_liquid ? cur_liquid.corner_levels[base.Z][base.X] : -0.5f) * BS;
} else if (cur_liquid.top_is_same_liquid) {
pos.Y = 0.5f * BS;
} else {
pos.Y = corner_levels[base.Z][base.X] * BS;
v += 0.5f - corner_levels[base.Z][base.X];
pos.Y = cur_liquid.corner_levels[base.Z][base.X] * BS;
v += 0.5f - cur_liquid.corner_levels[base.Z][base.X];
}

if (data->m_smooth_lighting)
color = blendLightColor(pos);
pos += origin;
vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, v);
cur_node.color = blendLightColor(pos);
pos += cur_node.origin;
vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, cur_node.color, vertex.u, v);
};
collector->append(tile_liquid, vertices, 4, quad_indices, 6);
collector->append(cur_liquid.tile, vertices, 4, quad_indices, 6);
}
}

Expand All @@ -722,32 +727,33 @@ void MapblockMeshGenerator::drawLiquidTop()
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),
video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, cur_liquid.color_top, 0, 1),
video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, cur_liquid.color_top, 1, 1),
video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, cur_liquid.color_top, 1, 0),
video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, cur_liquid.color_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] * BS;
vertices[i].Pos.Y += cur_liquid.corner_levels[w][u] * BS;
if (data->m_smooth_lighting)
vertices[i].Color = blendLightColor(vertices[i].Pos);
vertices[i].Pos += origin;
vertices[i].Pos += cur_node.origin;
}

// 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]);
f32 dz = (cur_liquid.corner_levels[0][0] + cur_liquid.corner_levels[0][1]) -
(cur_liquid.corner_levels[1][0] + cur_liquid.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 dx = (cur_liquid.corner_levels[0][0] + cur_liquid.corner_levels[1][0]) -
(cur_liquid.corner_levels[0][1] + cur_liquid.corner_levels[1][1]);
v2f tcoord_center(0.5, 0.5);
v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
v2f tcoord_translate(blockpos_nodes.Z + cur_node.p.Z,
blockpos_nodes.X + cur_node.p.X);
v2f dir = v2f(dx, dz).normalize();
if (dir == v2f{0.0f, 0.0f}) // if corners are symmetrical
dir = v2f{1.0f, 0.0f};
Expand All @@ -773,25 +779,25 @@ void MapblockMeshGenerator::drawLiquidTop()

std::swap(vertices[0].TCoords, vertices[2].TCoords);

collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
collector->append(cur_liquid.tile_top, vertices, 4, quad_indices, 6);
}

void MapblockMeshGenerator::drawLiquidBottom()
{
video::S3DVertex vertices[4] = {
video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, cur_liquid.color_top, 0, 0),
video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, cur_liquid.color_top, 1, 0),
video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, cur_liquid.color_top, 1, 1),
video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, cur_liquid.color_top, 0, 1),
};

for (int i = 0; i < 4; i++) {
if (data->m_smooth_lighting)
vertices[i].Color = blendLightColor(vertices[i].Pos);
vertices[i].Pos += origin;
vertices[i].Pos += cur_node.origin;
}

collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
collector->append(cur_liquid.tile_top, vertices, 4, quad_indices, 6);
}

void MapblockMeshGenerator::drawLiquidNode()
Expand All @@ -800,9 +806,9 @@ void MapblockMeshGenerator::drawLiquidNode()
getLiquidNeighborhood();
calculateCornerLevels();
drawLiquidSides();
if (!top_is_same_liquid)
if (!cur_liquid.top_is_same_liquid)
drawLiquidTop();
if (draw_liquid_bottom)
if (cur_liquid.draw_bottom)
drawLiquidBottom();
}

Expand All @@ -813,10 +819,10 @@ void MapblockMeshGenerator::drawGlasslikeNode()
for (int face = 0; face < 6; face++) {
// Check this neighbor
v3s16 dir = g_6dirs[face];
v3s16 neighbor_pos = blockpos_nodes + p + dir;
v3s16 neighbor_pos = blockpos_nodes + cur_node.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())
if (neighbor.getContent() == cur_node.n.getContent())
continue;
// Face at Z-
v3f vertices[4] = {
Expand Down Expand Up @@ -853,14 +859,15 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
getTile(g_6dirs[face], &tiles[face]);

if (!data->m_smooth_lighting)
color = encode_light(light, f->light_source);
cur_node.color = encode_light(cur_node.light, cur_node.f->light_source);

TileSpec glass_tiles[6];
for (auto &glass_tile : glass_tiles)
glass_tile = tiles[4];

// Only respect H/V merge bits when paramtype2 = "glasslikeliquidlevel" (liquid tank)
u8 param2 = (f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL) ? n.getParam2() : 0;
u8 param2 = (cur_node.f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL) ?
cur_node.n.getParam2() : 0;
bool H_merge = !(param2 & 128);
bool V_merge = !(param2 & 64);
param2 &= 63;
Expand Down Expand Up @@ -905,11 +912,11 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
check_nb = check_nb_vertical; // vertical-only merge
if (!V_merge)
check_nb = check_nb_horizontal; // horizontal-only merge
content_t current = n.getContent();
content_t current = cur_node.n.getContent();
for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
if (!check_nb[i])
continue;
v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
v3s16 n2p = blockpos_nodes + cur_node.p + g_26dirs[i];
MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
content_t n2c = n2.getContent();
if (n2c == current)
Expand All @@ -925,7 +932,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
{0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
};

tile = tiles[1];
cur_node.tile = tiles[1];
for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
bool edge_invisible;
if (nb[nb_triplet[edge][2]])
Expand All @@ -941,7 +948,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
if (nb[face])
continue;

tile = glass_tiles[face];
cur_node.tile = glass_tiles[face];
// Face at Z-
v3f vertices[4] = {
v3f(-a, a, -g),
Expand Down Expand Up @@ -972,12 +979,12 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()

// Optionally render internal liquid level defined by param2
// Liquid is textured with 1 tile defined in nodedef 'special_tiles'
if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
f->special_tiles[0].layers[0].texture) {
if (param2 > 0 && cur_node.f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
cur_node.f->special_tiles[0].layers[0].texture) {
// Internal liquid level has param2 range 0 .. 63,
// convert it to -0.5 .. 0.5
float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
getSpecialTile(0, &tile);
getSpecialTile(0, &cur_node.tile);
drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
-(nb[4] ? g : b),
-(nb[3] ? g : b),
Expand All @@ -996,7 +1003,7 @@ void MapblockMeshGenerator::drawAllfacesNode()

void MapblockMeshGenerator::drawTorchlikeNode()
{
u8 wall = n.getWallMounted(nodedef);
u8 wall = cur_node.n.getWallMounted(nodedef);
u8 tileindex = 0;
switch (wall) {
case DWM_YP: tileindex = 1; break; // ceiling
Expand All @@ -1005,7 +1012,7 @@ void MapblockMeshGenerator::drawTorchlikeNode()
}
useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);

float size = BS / 2 * f->visual_scale;
float size = BS / 2 * cur_node.f->visual_scale;
v3f vertices[4] = {
v3f(-size, size, 0),
v3f( size, size, 0),
Expand Down Expand Up @@ -1044,10 +1051,10 @@ void MapblockMeshGenerator::drawTorchlikeNode()

void MapblockMeshGenerator::drawSignlikeNode()
{
u8 wall = n.getWallMounted(nodedef);
u8 wall = cur_node.n.getWallMounted(nodedef);
useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
static const float offset = BS / 16;
float size = BS / 2 * f->visual_scale;
float size = BS / 2 * cur_node.f->visual_scale;
// Wall at X+ of node
v3f vertices[4] = {
v3f(BS / 2 - offset, size, size),
Expand Down Expand Up @@ -1078,26 +1085,30 @@ void MapblockMeshGenerator::drawSignlikeNode()
void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
bool offset_top_only)
{
const f32 scale = cur_node.scale;
v3f vertices[4] = {
v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
v3f(-scale, -BS / 2 + 2.0 * scale * cur_plant.plant_height, 0),
v3f( scale, -BS / 2 + 2.0 * scale * cur_plant.plant_height, 0),
v3f( scale, -BS / 2, 0),
v3f(-scale, -BS / 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);
if (cur_plant.random_offset_Y) {
PseudoRandom yrng(cur_plant.face_num++
| cur_node.p.X << 16
| cur_node.p.Z << 8
| cur_node.p.Y << 24);
cur_plant.offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
}
int offset_count = offset_top_only ? 2 : 4;
for (int i = 0; i < offset_count; i++)
vertices[i].Z += quad_offset;

for (v3f &vertex : vertices) {
vertex.rotateXZBy(rotation + rotate_degree);
vertex += offset;
vertex.rotateXZBy(rotation + cur_plant.rotate_degree);
vertex += cur_plant.offset;
}

u8 wall = n.getWallMounted(nodedef);
u8 wall = cur_node.n.getWallMounted(nodedef);
if (wall != DWM_YN) {
for (v3f &vertex : vertices) {
switch (wall) {
Expand All @@ -1124,63 +1135,63 @@ void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
}
}

drawQuad(vertices, v3s16(0, 0, 0), plant_height);
drawQuad(vertices, v3s16(0, 0, 0), cur_plant.plant_height);
}

void MapblockMeshGenerator::drawPlantlike(bool is_rooted)
{
draw_style = PLANT_STYLE_CROSS;
scale = BS / 2 * f->visual_scale;
offset = v3f(0, 0, 0);
rotate_degree = 0.0f;
random_offset_Y = false;
face_num = 0;
plant_height = 1.0;

switch (f->param_type_2) {
cur_plant.draw_style = PLANT_STYLE_CROSS;
cur_node.scale = BS / 2 * cur_node.f->visual_scale;
cur_plant.offset = v3f(0, 0, 0);
cur_plant.rotate_degree = 0.0f;
cur_plant.random_offset_Y = false;
cur_plant.face_num = 0;
cur_plant.plant_height = 1.0;

switch (cur_node.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);
cur_plant.draw_style = PlantlikeStyle(cur_node.n.param2 & MO_MASK_STYLE);
if (cur_node.n.param2 & MO_BIT_SCALE_SQRT2)
cur_node.scale *= 1.41421;
if (cur_node.n.param2 & MO_BIT_RANDOM_OFFSET) {
PseudoRandom rng(cur_node.p.X << 8 | cur_node.p.Z | cur_node.p.Y << 16);
cur_plant.offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
cur_plant.offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
}
if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
random_offset_Y = true;
if (cur_node.n.param2 & MO_BIT_RANDOM_OFFSET_Y)
cur_plant.random_offset_Y = true;
break;

case CPT2_DEGROTATE:
case CPT2_COLORED_DEGROTATE:
rotate_degree = 1.5f * n.getDegRotate(nodedef);
cur_plant.rotate_degree = 1.5f * cur_node.n.getDegRotate(nodedef);
break;

case CPT2_LEVELED:
plant_height = n.param2 / 16.0;
cur_plant.plant_height = cur_node.n.param2 / 16.0;
break;

default:
break;
}

if (is_rooted) {
u8 wall = n.getWallMounted(nodedef);
u8 wall = cur_node.n.getWallMounted(nodedef);
switch (wall) {
case DWM_YP:
offset.Y += BS*2;
cur_plant.offset.Y += BS*2;
break;
case DWM_XN:
case DWM_XP:
case DWM_ZN:
case DWM_ZP:
offset.X += -BS;
offset.Y += BS;
cur_plant.offset.X += -BS;
cur_plant.offset.Y += BS;
break;
}
}

switch (draw_style) {
switch (cur_plant.draw_style) {
case PLANT_STYLE_CROSS:
drawPlantlikeQuad(46);
drawPlantlikeQuad(-44);
Expand Down Expand Up @@ -1223,21 +1234,22 @@ void MapblockMeshGenerator::drawPlantlikeRootedNode()
{
drawSolidNode();
useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
origin += v3f(0.0, BS, 0.0);
p.Y++;
cur_node.origin += v3f(0.0, BS, 0.0);
cur_node.p.Y++;
if (data->m_smooth_lighting) {
getSmoothLightFrame();
} else {
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
light = LightPair(getInteriorLight(ntop, 0, nodedef));
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p);
cur_node.light = LightPair(getInteriorLight(ntop, 0, nodedef));
}
drawPlantlike(true);
p.Y--;
cur_node.p.Y--;
}

void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
float offset_h, float offset_v)
{
const f32 scale = cur_node.scale;
v3f vertices[4] = {
v3f(-scale, -BS / 2 + scale * 2, 0),
v3f( scale, -BS / 2 + scale * 2, 0),
Expand All @@ -1257,14 +1269,14 @@ void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle
void MapblockMeshGenerator::drawFirelikeNode()
{
useTile();
scale = BS / 2 * f->visual_scale;
cur_node.scale = BS / 2 * cur_node.f->visual_scale;

// Check for adjacent nodes
bool neighbors = false;
bool neighbor[6] = {0, 0, 0, 0, 0, 0};
content_t current = n.getContent();
content_t current = cur_node.n.getContent();
for (int i = 0; i < 6; i++) {
v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
v3s16 n2p = blockpos_nodes + cur_node.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) {
Expand Down Expand Up @@ -1304,13 +1316,13 @@ void MapblockMeshGenerator::drawFirelikeNode()
void MapblockMeshGenerator::drawFencelikeNode()
{
useTile(0, 0, 0);
TileSpec tile_nocrack = tile;
TileSpec tile_nocrack = cur_node.tile;

for (auto &layer : tile_nocrack.layers)
layer.material_flags &= ~MATERIAL_FLAG_CRACK;

// Put wood the right way around in the posts
TileSpec tile_rot = tile;
TileSpec tile_rot = cur_node.tile;
tile_rot.rotation = TileRotation::R90;

static const f32 post_rad = BS / 8;
Expand All @@ -1328,13 +1340,13 @@ void MapblockMeshGenerator::drawFencelikeNode()
0.500, 0.000, 0.750, 1.000,
0.750, 0.000, 1.000, 1.000,
};
tile = tile_rot;
cur_node.tile = tile_rot;
drawAutoLightedCuboid(post, postuv);

tile = tile_nocrack;
cur_node.tile = tile_nocrack;

// Now a section of fence, +X, if there's a post there
v3s16 p2 = p;
v3s16 p2 = cur_node.p;
p2.X++;
MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
const ContentFeatures *f2 = &nodedef->get(n2);
Expand All @@ -1356,7 +1368,7 @@ void MapblockMeshGenerator::drawFencelikeNode()
}

// Now a section of fence, +Z, if there's a post there
p2 = p;
p2 = cur_node.p;
p2.Z++;
n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
f2 = &nodedef->get(n2);
Expand All @@ -1380,12 +1392,12 @@ void MapblockMeshGenerator::drawFencelikeNode()

bool MapblockMeshGenerator::isSameRail(v3s16 dir)
{
MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
if (node2.getContent() == n.getContent())
MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p + dir);
if (node2.getContent() == cur_node.n.getContent())
return true;
const ContentFeatures &def2 = nodedef->get(node2);
return ((def2.drawtype == NDT_RAILLIKE) &&
(def2.getGroup(raillike_groupname) == raillike_group));
(def2.getGroup(raillike_groupname) == cur_rail.raillike_group));
}

namespace {
Expand Down Expand Up @@ -1431,7 +1443,7 @@ namespace {

void MapblockMeshGenerator::drawRaillikeNode()
{
raillike_group = nodedef->get(n).getGroup(raillike_groupname);
cur_rail.raillike_group = cur_node.f->getGroup(raillike_groupname);

int code = 0;
int angle;
Expand Down Expand Up @@ -1503,44 +1515,44 @@ void MapblockMeshGenerator::drawNodeboxNode()
}

bool param2_is_rotation =
f->param_type_2 == CPT2_COLORED_FACEDIR ||
f->param_type_2 == CPT2_COLORED_WALLMOUNTED ||
f->param_type_2 == CPT2_FACEDIR ||
f->param_type_2 == CPT2_WALLMOUNTED;
cur_node.f->param_type_2 == CPT2_COLORED_FACEDIR ||
cur_node.f->param_type_2 == CPT2_COLORED_WALLMOUNTED ||
cur_node.f->param_type_2 == CPT2_FACEDIR ||
cur_node.f->param_type_2 == CPT2_WALLMOUNTED;

bool param2_is_level =
f->param_type_2 == CPT2_LEVELED;
cur_node.f->param_type_2 == CPT2_LEVELED;

// locate possible neighboring nodes to connect to
u8 neighbors_set = 0;
u8 solid_neighbors = 0;
u8 sametype_neighbors = 0;
for (int dir = 0; dir != 6; dir++) {
u8 flag = 1 << dir;
v3s16 p2 = blockpos_nodes + p + nodebox_tile_dirs[dir];
v3s16 p2 = blockpos_nodes + cur_node.p + nodebox_tile_dirs[dir];
MapNode n2 = data->m_vmanip.getNodeNoEx(p2);

// mark neighbors that are the same node type
// and have the same rotation or higher level stored as param2
if (n2.param0 == n.param0 &&
(!param2_is_rotation || n.param2 == n2.param2) &&
(!param2_is_level || n.param2 <= n2.param2))
if (n2.param0 == cur_node.n.param0 &&
(!param2_is_rotation || cur_node.n.param2 == n2.param2) &&
(!param2_is_level || cur_node.n.param2 <= n2.param2))
sametype_neighbors |= flag;

// mark neighbors that are simple solid blocks
if (nodedef->get(n2).drawtype == NDT_NORMAL)
solid_neighbors |= flag;

if (f->node_box.type == NODEBOX_CONNECTED) {
p2 = blockpos_nodes + p + nodebox_connection_dirs[dir];
if (cur_node.f->node_box.type == NODEBOX_CONNECTED) {
p2 = blockpos_nodes + cur_node.p + nodebox_connection_dirs[dir];
n2 = data->m_vmanip.getNodeNoEx(p2);
if (nodedef->nodeboxConnects(n, n2, flag))
if (nodedef->nodeboxConnects(cur_node.n, n2, flag))
neighbors_set |= flag;
}
}

std::vector<aabb3f> boxes;
n.getNodeBoxes(nodedef, &boxes, neighbors_set);
cur_node.n.getNodeBoxes(nodedef, &boxes, neighbors_set);

bool isTransparent = false;

Expand Down Expand Up @@ -1607,31 +1619,31 @@ void MapblockMeshGenerator::drawMeshNode()
bool private_mesh; // as a grab/drop pair is not thread-safe
int degrotate = 0;

if (f->param_type_2 == CPT2_FACEDIR ||
f->param_type_2 == CPT2_COLORED_FACEDIR ||
f->param_type_2 == CPT2_4DIR ||
f->param_type_2 == CPT2_COLORED_4DIR) {
facedir = n.getFaceDir(nodedef);
} else if (f->param_type_2 == CPT2_WALLMOUNTED ||
f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
if (cur_node.f->param_type_2 == CPT2_FACEDIR ||
cur_node.f->param_type_2 == CPT2_COLORED_FACEDIR ||
cur_node.f->param_type_2 == CPT2_4DIR ||
cur_node.f->param_type_2 == CPT2_COLORED_4DIR) {
facedir = cur_node.n.getFaceDir(nodedef);
} else if (cur_node.f->param_type_2 == CPT2_WALLMOUNTED ||
cur_node.f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
// Convert wallmounted to 6dfacedir.
// When cache enabled, it is already converted.
facedir = n.getWallMounted(nodedef);
facedir = cur_node.n.getWallMounted(nodedef);
if (!enable_mesh_cache)
facedir = wallmounted_to_facedir[facedir];
} else if (f->param_type_2 == CPT2_DEGROTATE ||
f->param_type_2 == CPT2_COLORED_DEGROTATE) {
degrotate = n.getDegRotate(nodedef);
} else if (cur_node.f->param_type_2 == CPT2_DEGROTATE ||
cur_node.f->param_type_2 == CPT2_COLORED_DEGROTATE) {
degrotate = cur_node.n.getDegRotate(nodedef);
}

if (!data->m_smooth_lighting && f->mesh_ptr[facedir] && !degrotate) {
if (!data->m_smooth_lighting && cur_node.f->mesh_ptr[facedir] && !degrotate) {
// use cached meshes
private_mesh = false;
mesh = f->mesh_ptr[facedir];
} else if (f->mesh_ptr[0]) {
mesh = cur_node.f->mesh_ptr[facedir];
} else if (cur_node.f->mesh_ptr[0]) {
// no cache, clone and rotate mesh
private_mesh = true;
mesh = cloneMesh(f->mesh_ptr[0]);
mesh = cloneMesh(cur_node.f->mesh_ptr[0]);
if (facedir)
rotateMeshBy6dFacedir(mesh, facedir);
else if (degrotate)
Expand All @@ -1654,16 +1666,16 @@ void MapblockMeshGenerator::drawMeshNode()
for (int k = 0; k < vertex_count; k++) {
video::S3DVertex &vertex = vertices[k];
vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
vertex.Pos += origin;
vertex.Pos += cur_node.origin;
}
collector->append(tile, vertices, vertex_count,
collector->append(cur_node.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);
collector->append(cur_node.tile, vertices, vertex_count,
buf->getIndices(), buf->getIndexCount(), cur_node.origin,
cur_node.color, cur_node.f->light_source);
}
}
if (private_mesh)
Expand All @@ -1673,13 +1685,13 @@ void MapblockMeshGenerator::drawMeshNode()
// also called when the drawtype is known but should have been pre-converted
void MapblockMeshGenerator::errorUnknownDrawtype()
{
infostream << "Got drawtype " << f->drawtype << std::endl;
infostream << "Got drawtype " << cur_node.f->drawtype << std::endl;
FATAL_ERROR("Unknown drawtype");
}

void MapblockMeshGenerator::drawNode()
{
switch (f->drawtype) {
switch (cur_node.f->drawtype) {
case NDT_AIRLIKE: // Not drawn at all
return;
case NDT_LIQUID:
Expand All @@ -1689,12 +1701,12 @@ void MapblockMeshGenerator::drawNode()
default:
break;
}
origin = intToFloat(p, BS);
cur_node.origin = intToFloat(cur_node.p, BS);
if (data->m_smooth_lighting)
getSmoothLightFrame();
else
light = LightPair(getInteriorLight(n, 0, nodedef));
switch (f->drawtype) {
cur_node.light = LightPair(getInteriorLight(cur_node.n, 0, nodedef));
switch (cur_node.f->drawtype) {
case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
case NDT_GLASSLIKE: drawGlasslikeNode(); break;
case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
Expand All @@ -1712,25 +1724,21 @@ void MapblockMeshGenerator::drawNode()
}
}

/*
TODO: Fix alpha blending for special nodes
Currently only the last element rendered is blended correct
*/
void MapblockMeshGenerator::generate()
{
for (p.Z = 0; p.Z < data->side_length; p.Z++)
for (p.Y = 0; p.Y < data->side_length; p.Y++)
for (p.X = 0; p.X < data->side_length; p.X++) {
n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
f = &nodedef->get(n);
for (cur_node.p.Z = 0; cur_node.p.Z < data->side_length; cur_node.p.Z++)
for (cur_node.p.Y = 0; cur_node.p.Y < data->side_length; cur_node.p.Y++)
for (cur_node.p.X = 0; cur_node.p.X < data->side_length; cur_node.p.X++) {
cur_node.n = data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p);
cur_node.f = &nodedef->get(cur_node.n);
drawNode();
}
}

void MapblockMeshGenerator::renderSingle(content_t node, u8 param2)
{
p = {0, 0, 0};
n = MapNode(node, 0xff, param2);
f = &nodedef->get(n);
cur_node.p = {0, 0, 0};
cur_node.n = MapNode(node, 0xff, param2);
cur_node.f = &nodedef->get(cur_node.n);
drawNode();
}
95 changes: 54 additions & 41 deletions src/client/content_mapblock.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,26 +61,35 @@ struct LightFrame {
class MapblockMeshGenerator
{
public:
MeshMakeData *data;
MeshCollector *collector;
MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output,
scene::IMeshManipulator *mm);
void generate();
void renderSingle(content_t node, u8 param2 = 0x00);

const NodeDefManager *nodedef;
scene::IMeshManipulator *meshmanip;
private:
MeshMakeData *const data;
MeshCollector *const collector;

const NodeDefManager *const nodedef;
scene::IMeshManipulator *const meshmanip;

const v3s16 blockpos_nodes;

// options
bool enable_mesh_cache;
const bool enable_mesh_cache;

// current node
v3s16 blockpos_nodes;
v3s16 p;
v3f origin;
MapNode n;
const ContentFeatures *f;
LightPair light;
LightFrame frame;
video::SColor color;
TileSpec tile;
float scale;
struct {
v3s16 p;
v3f origin;
MapNode n;
const ContentFeatures *f;
LightPair light;
LightFrame frame;
video::SColor color;
TileSpec tile;
f32 scale;
} cur_node;

// lighting
void getSmoothLightFrame();
Expand All @@ -106,21 +115,25 @@ class MapblockMeshGenerator
u8 getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const;

// liquid-specific
bool top_is_same_liquid;
bool draw_liquid_bottom;
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;
struct LiquidData {
struct NeighborData {
f32 level;
content_t content;
bool is_same_liquid;
bool top_is_same_liquid;
};

bool top_is_same_liquid;
bool draw_bottom;
TileSpec tile;
TileSpec tile_top;
content_t c_flowing;
content_t c_source;
video::SColor color_top;
NeighborData neighbors[3][3];
f32 corner_levels[2][2];
};
NeighborData liquid_neighbors[3][3];
f32 corner_levels[2][2];
LiquidData cur_liquid;

void prepareLiquidNodeDrawing();
void getLiquidNeighborhood();
Expand All @@ -133,16 +146,22 @@ class MapblockMeshGenerator
// raillike-specific
// name of the group that enables connecting to raillike nodes of different kind
static const std::string raillike_groupname;
int raillike_group;
struct RaillikeData {
int raillike_group;
};
RaillikeData cur_rail;
bool isSameRail(v3s16 dir);

// plantlike-specific
PlantlikeStyle draw_style;
v3f offset;
float rotate_degree;
bool random_offset_Y;
int face_num;
float plant_height;
struct PlantlikeData {
PlantlikeStyle draw_style;
v3f offset;
float rotate_degree;
bool random_offset_Y;
int face_num;
float plant_height;
};
PlantlikeData cur_plant;

void drawPlantlikeQuad(float rotation, float quad_offset = 0,
bool offset_top_only = false);
Expand Down Expand Up @@ -171,10 +190,4 @@ class MapblockMeshGenerator
// common
void errorUnknownDrawtype();
void drawNode();

public:
MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output,
scene::IMeshManipulator *mm);
void generate();
void renderSingle(content_t node, u8 param2 = 0x00);
};