+// 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
+// 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 - 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)
+{
+ 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;
+ }
+ static const u8 light_indices[24] = {
+ 3, 7, 6, 2,
+ 0, 4, 5, 1,
+ 6, 7, 5, 4,
+ 3, 2, 0, 1,
+ 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));
+ }
+ 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);
+}
+
+// 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)
+{
+ 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;
+ }
+ 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)
+{
+ 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);
+ f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
+ f32 lightA = 0.0;
+ f32 lightB = 0.0;
+ for (int k = 0; k < 8; ++k) {
+ f32 dx = (k & 4) ? x : 1 - x;
+ f32 dy = (k & 2) ? y : 1 - y;
+ f32 dz = (k & 1) ? z : 1 - z;
+ lightA += dx * dy * dz * frame.lightsA[k];
+ lightB += dx * dy * dz * frame.lightsB[k];
+ }
+ return
+ core::clamp(core::round32(lightA), 0, 255) |
+ core::clamp(core::round32(lightB), 0, 255) << 8;
+}
+
+// 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,
+ video::SColor tile_color)
+{
+ video::SColor color = blendLight(frame, vertex_pos, tile_color);
+ if (!frame.light_source)
+ applyFacesShading(color, vertex_normal);
+ return color;
+}
+
+static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
+ MeshMakeData *data, MapNode n, int v, int *neighbors)
+{
+ 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;
+ 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] = {
+ 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);
+ }
+}
+
+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)
+{
+ 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 (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 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.
+ */
+static TileSpec getSpecialTile(const ContentFeatures &f,
+ const MapNode &n, u8 i)
+{
+ TileSpec copy = f.special_tiles[i];
+ if (!copy.has_color)
+ n.getColor(f, ©.color);
+ return copy;
+}
+
+/*
+ TODO: Fix alpha blending for special nodes
+ Currently only the last element rendered is blended correct
+*/