3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_mapblock.h"
21 #include "util/numeric.h"
22 #include "util/directiontables.h"
23 #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
26 #include "client/tile.h"
28 #include <IMeshManipulator.h>
33 // Distance of light extrapolation (for oversized nodes)
34 // After this distance, it gives up and considers light level constant
35 #define SMOOTH_LIGHTING_OVERSIZE 1.0
44 static const v3s16 light_dirs[8] = {
56 // collector - the MeshCollector for the resulting polygons
57 // box - the position and size of the box
58 // tiles - the tiles (materials) to use (for all 6 faces)
59 // tilecount - number of entries in tiles, 1<=tilecount<=6
60 // c - colors of the cuboid's six sides
61 // txc - texture coordinates - this is a list of texture coordinates
62 // for the opposite corners of each face - therefore, there
63 // should be (2+2)*6=24 values in the list. Alternatively,
64 // pass NULL to use the entire texture for each face. The
65 // order of the faces in the list is up-down-right-left-back-
66 // front (compatible with ContentFeatures). If you specified
67 // 0,0,1,1 for each face, that would be the same as
69 // light source - if greater than zero, the box's faces will not be shaded
70 void makeCuboid(MeshCollector *collector, const aabb3f &box,
71 TileSpec *tiles, int tilecount, const video::SColor *c,
72 const f32* txc, const u8 light_source)
74 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
76 v3f min = box.MinEdge;
77 v3f max = box.MaxEdge;
80 static const f32 txc_default[24] = {
91 video::SColor c1 = c[0];
92 video::SColor c2 = c[1];
93 video::SColor c3 = c[2];
94 video::SColor c4 = c[3];
95 video::SColor c5 = c[4];
96 video::SColor c6 = c[5];
98 applyFacesShading(c1, v3f(0, 1, 0));
99 applyFacesShading(c2, v3f(0, -1, 0));
100 applyFacesShading(c3, v3f(1, 0, 0));
101 applyFacesShading(c4, v3f(-1, 0, 0));
102 applyFacesShading(c5, v3f(0, 0, 1));
103 applyFacesShading(c6, v3f(0, 0, -1));
106 video::S3DVertex vertices[24] =
109 video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c1, txc[0],txc[1]),
110 video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c1, txc[2],txc[1]),
111 video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c1, txc[2],txc[3]),
112 video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c1, txc[0],txc[3]),
114 video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c2, txc[4],txc[5]),
115 video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c2, txc[6],txc[5]),
116 video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c2, txc[6],txc[7]),
117 video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c2, txc[4],txc[7]),
119 video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c3, txc[ 8],txc[9]),
120 video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c3, txc[10],txc[9]),
121 video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c3, txc[10],txc[11]),
122 video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c3, txc[ 8],txc[11]),
124 video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c4, txc[12],txc[13]),
125 video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c4, txc[14],txc[13]),
126 video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c4, txc[14],txc[15]),
127 video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c4, txc[12],txc[15]),
129 video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c5, txc[16],txc[17]),
130 video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c5, txc[18],txc[17]),
131 video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c5, txc[18],txc[19]),
132 video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c5, txc[16],txc[19]),
134 video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c6, txc[20],txc[21]),
135 video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c6, txc[22],txc[21]),
136 video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c6, txc[22],txc[23]),
137 video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c6, txc[20],txc[23]),
140 for(int i = 0; i < 6; i++)
142 switch (tiles[MYMIN(i, tilecount-1)].rotation)
147 for (int x = 0; x < 4; x++)
148 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
151 for (int x = 0; x < 4; x++)
152 vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
155 for (int x = 0; x < 4; x++)
156 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
159 for (int x = 0; x < 4; x++){
160 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
161 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
165 for (int x = 0; x < 4; x++){
166 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
167 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
171 for (int x = 0; x < 4; x++){
172 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
173 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
177 for (int x = 0; x < 4; x++){
178 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
179 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
183 for (int x = 0; x < 4; x++){
184 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
188 for (int x = 0; x < 4; x++){
189 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
196 u16 indices[] = {0,1,2,2,3,0};
197 // Add to mesh collector
198 for (s32 j = 0; j < 24; j += 4) {
199 int tileindex = MYMIN(j / 4, tilecount - 1);
200 collector->append(tiles[tileindex], vertices + j, 4, indices, 6);
205 // collector - the MeshCollector for the resulting polygons
206 // box - the position and size of the box
207 // tiles - the tiles (materials) to use (for all 6 faces)
208 // tilecount - number of entries in tiles, 1<=tilecount<=6
209 // lights - vertex light levels. The order is the same as in light_dirs
210 // txc - texture coordinates - this is a list of texture coordinates
211 // for the opposite corners of each face - therefore, there
212 // should be (2+2)*6=24 values in the list. Alternatively, pass
213 // NULL to use the entire texture for each face. The order of
214 // the faces in the list is up-down-right-left-back-front
215 // (compatible with ContentFeatures). If you specified 0,0,1,1
216 // for each face, that would be the same as passing NULL.
217 // light_source - node light emission
218 static void makeSmoothLightedCuboid(MeshCollector *collector, const aabb3f &box,
219 TileSpec *tiles, int tilecount, const u16 *lights , const f32 *txc,
220 const u8 light_source)
222 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
224 v3f min = box.MinEdge;
225 v3f max = box.MaxEdge;
228 static const f32 txc_default[24] = {
238 static const u8 light_indices[24] = {
246 video::S3DVertex vertices[24] = {
248 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, video::SColor(), txc[0], txc[1]),
249 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, video::SColor(), txc[2], txc[1]),
250 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, video::SColor(), txc[2], txc[3]),
251 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, video::SColor(), txc[0], txc[3]),
253 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, video::SColor(), txc[4], txc[5]),
254 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, video::SColor(), txc[6], txc[5]),
255 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, video::SColor(), txc[6], txc[7]),
256 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, video::SColor(), txc[4], txc[7]),
258 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, video::SColor(), txc[ 8], txc[9]),
259 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, video::SColor(), txc[10], txc[9]),
260 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, video::SColor(), txc[10], txc[11]),
261 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, video::SColor(), txc[ 8], txc[11]),
263 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, video::SColor(), txc[12], txc[13]),
264 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, video::SColor(), txc[14], txc[13]),
265 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, video::SColor(), txc[14], txc[15]),
266 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, video::SColor(), txc[12], txc[15]),
268 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, video::SColor(), txc[16], txc[17]),
269 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, video::SColor(), txc[18], txc[17]),
270 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, video::SColor(), txc[18], txc[19]),
271 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, video::SColor(), txc[16], txc[19]),
273 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, video::SColor(), txc[20], txc[21]),
274 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, video::SColor(), txc[22], txc[21]),
275 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, video::SColor(), txc[22], txc[23]),
276 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, video::SColor(), txc[20], txc[23]),
279 for(int i = 0; i < 6; i++) {
280 switch (tiles[MYMIN(i, tilecount-1)].rotation) {
284 for (int x = 0; x < 4; x++)
285 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
288 for (int x = 0; x < 4; x++)
289 vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
292 for (int x = 0; x < 4; x++)
293 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
296 for (int x = 0; x < 4; x++) {
297 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
298 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
302 for (int x = 0; x < 4; x++) {
303 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
304 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
308 for (int x = 0; x < 4; x++) {
309 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
310 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
314 for (int x = 0; x < 4; x++) {
315 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
316 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
320 for (int x = 0; x < 4; x++) {
321 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
325 for (int x = 0; x < 4; x++) {
326 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
333 u16 indices[] = {0,1,2,2,3,0};
334 for (s32 j = 0; j < 24; ++j) {
335 int tileindex = MYMIN(j / 4, tilecount - 1);
336 vertices[j].Color = encode_light_and_color(lights[light_indices[j]],
337 tiles[tileindex].color, light_source);
339 applyFacesShading(vertices[j].Color, vertices[j].Normal);
341 // Add to mesh collector
342 for (s32 k = 0; k < 6; ++k) {
343 int tileindex = MYMIN(k, tilecount - 1);
344 collector->append(tiles[tileindex], vertices + 4 * k, 4, indices, 6);
349 // collector - the MeshCollector for the resulting polygons
350 // box - the position and size of the box
351 // tiles - the tiles (materials) to use (for all 6 faces)
352 // tilecount - number of entries in tiles, 1<=tilecount<=6
353 // c - color of the cuboid
354 // txc - texture coordinates - this is a list of texture coordinates
355 // for the opposite corners of each face - therefore, there
356 // should be (2+2)*6=24 values in the list. Alternatively,
357 // pass NULL to use the entire texture for each face. The
358 // order of the faces in the list is up-down-right-left-back-
359 // front (compatible with ContentFeatures). If you specified
360 // 0,0,1,1 for each face, that would be the same as
362 // light source - if greater than zero, the box's faces will not be shaded
363 void makeCuboid(MeshCollector *collector, const aabb3f &box, TileSpec *tiles,
364 int tilecount, const video::SColor &c, const f32* txc,
365 const u8 light_source)
367 video::SColor color[6];
368 for (u8 i = 0; i < 6; i++)
370 makeCuboid(collector, box, tiles, tilecount, color, txc, light_source);
373 // Gets the base lighting values for a node
374 // frame - resulting (opaque) data
375 // p - node position (absolute)
377 // light_source - node light emission level
378 static void getSmoothLightFrame(LightFrame *frame, const v3s16 &p, MeshMakeData *data, u8 light_source)
380 for (int k = 0; k < 8; ++k) {
381 u16 light = getSmoothLight(p, light_dirs[k], data);
382 frame->lightsA[k] = light & 0xff;
383 frame->lightsB[k] = light >> 8;
385 frame->light_source = light_source;
388 // Calculates vertex light level
389 // frame - light values from getSmoothLightFrame()
390 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
391 static u16 blendLight(const LightFrame &frame, const core::vector3df& vertex_pos)
393 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
394 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
395 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
398 for (int k = 0; k < 8; ++k) {
399 f32 dx = (k & 4) ? x : 1 - x;
400 f32 dy = (k & 2) ? y : 1 - y;
401 f32 dz = (k & 1) ? z : 1 - z;
402 lightA += dx * dy * dz * frame.lightsA[k];
403 lightB += dx * dy * dz * frame.lightsB[k];
406 core::clamp(core::round32(lightA), 0, 255) |
407 core::clamp(core::round32(lightB), 0, 255) << 8;
410 // Calculates vertex color to be used in mapblock mesh
411 // frame - light values from getSmoothLightFrame()
412 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
413 // tile_color - node's tile color
414 static video::SColor blendLight(const LightFrame &frame,
415 const core::vector3df& vertex_pos, video::SColor tile_color)
417 u16 light = blendLight(frame, vertex_pos);
418 return encode_light_and_color(light, tile_color, frame.light_source);
421 static video::SColor blendLight(const LightFrame &frame,
422 const core::vector3df& vertex_pos, const core::vector3df& vertex_normal,
423 video::SColor tile_color)
425 video::SColor color = blendLight(frame, vertex_pos, tile_color);
426 if (!frame.light_source)
427 applyFacesShading(color, vertex_normal);
431 static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
432 MeshMakeData *data, MapNode n, int v, int *neighbors)
434 MapNode n2 = data->m_vmanip.getNodeNoEx(p);
435 if (nodedef->nodeboxConnects(n, n2, v))
439 static void makeAutoLightedCuboid(MeshCollector *collector, MeshMakeData *data,
440 const v3f &pos, aabb3f box, TileSpec &tile,
441 /* pre-computed, for non-smooth lighting only */ const video::SColor color,
442 /* for smooth lighting only */ const LightFrame &frame)
444 f32 dx1 = box.MinEdge.X;
445 f32 dy1 = box.MinEdge.Y;
446 f32 dz1 = box.MinEdge.Z;
447 f32 dx2 = box.MaxEdge.X;
448 f32 dy2 = box.MaxEdge.Y;
449 f32 dz2 = box.MaxEdge.Z;
452 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
453 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
454 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
455 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
456 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
457 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
459 tx1, 1-tz2, tx2, 1-tz1, // up
460 tx1, tz1, tx2, tz2, // down
461 tz1, 1-ty2, tz2, 1-ty1, // right
462 1-tz2, 1-ty2, 1-tz1, 1-ty1, // left
463 1-tx2, 1-ty2, 1-tx1, 1-ty1, // back
464 tx1, 1-ty2, tx2, 1-ty1, // front
466 if (data->m_smooth_lighting) {
468 for (int j = 0; j < 8; ++j) {
469 f32 x = (j & 4) ? dx2 : dx1;
470 f32 y = (j & 2) ? dy2 : dy1;
471 f32 z = (j & 1) ? dz2 : dz1;
472 lights[j] = blendLight(frame, core::vector3df(x, y, z));
474 makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source);
476 makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source);
480 static void makeAutoLightedCuboidEx(MeshCollector *collector, MeshMakeData *data,
481 const v3f &pos, aabb3f box, TileSpec &tile, f32 *txc,
482 /* pre-computed, for non-smooth lighting only */ const video::SColor color,
483 /* for smooth lighting only */ const LightFrame &frame)
485 f32 dx1 = box.MinEdge.X;
486 f32 dy1 = box.MinEdge.Y;
487 f32 dz1 = box.MinEdge.Z;
488 f32 dx2 = box.MaxEdge.X;
489 f32 dy2 = box.MaxEdge.Y;
490 f32 dz2 = box.MaxEdge.Z;
493 if (data->m_smooth_lighting) {
495 for (int j = 0; j < 8; ++j) {
496 f32 x = (j & 4) ? dx2 : dx1;
497 f32 y = (j & 2) ? dy2 : dy1;
498 f32 z = (j & 1) ? dz2 : dz1;
499 lights[j] = blendLight(frame, core::vector3df(x, y, z));
501 makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source);
503 makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source);
507 // For use in mapblock_mesh_generate_special
508 // X,Y,Z of position must be -1,0,1
509 // This expression is a simplification of
510 // 3 * 3 * (pos.X + 1) + 3 * (pos.Y + 1) + (pos.Z + 1)
511 static inline int NeighborToIndex(const v3s16 &pos)
513 return 9 * pos.X + 3 * pos.Y + pos.Z + 13;
517 * Returns the i-th special tile for a map node.
519 static TileSpec getSpecialTile(const ContentFeatures &f,
520 const MapNode &n, u8 i)
522 TileSpec copy = f.special_tiles[i];
524 n.getColor(f, ©.color);
529 TODO: Fix alpha blending for special nodes
530 Currently only the last element rendered is blended correct
532 void mapblock_mesh_generate_special(MeshMakeData *data,
533 MeshCollector &collector)
535 INodeDefManager *nodedef = data->m_client->ndef();
536 scene::ISceneManager* smgr = data->m_client->getSceneManager();
537 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
540 //TimeTaker timer("mapblock_mesh_generate_special()");
545 bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
546 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
548 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
550 for(s16 z = 0; z < MAP_BLOCKSIZE; z++)
551 for(s16 y = 0; y < MAP_BLOCKSIZE; y++)
552 for(s16 x = 0; x < MAP_BLOCKSIZE; x++)
556 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
557 const ContentFeatures &f = nodedef->get(n);
559 // Only solidness=0 stuff is drawn here
563 if (f.drawtype == NDT_AIRLIKE)
567 if (data->m_smooth_lighting)
568 getSmoothLightFrame(&frame, blockpos_nodes + p, data, f.light_source);
570 frame.light_source = f.light_source;
574 infostream << "Got " << f.drawtype << std::endl;
575 FATAL_ERROR("Unknown drawtype");
580 Add water sources to mesh if using new style
582 TileSpec tile_liquid = getSpecialTile(f, n, 0);
583 TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
584 u16 l = getInteriorLight(n, 0, nodedef);
585 video::SColor c1 = encode_light_and_color(l,
586 tile_liquid.color, f.light_source);
587 video::SColor c2 = encode_light_and_color(l,
588 tile_liquid_bfculled.color, f.light_source);
590 bool top_is_same_liquid = false;
591 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
592 content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
593 content_t c_source = nodedef->getId(f.liquid_alternative_source);
594 if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
595 top_is_same_liquid = true;
600 v3s16 side_dirs[4] = {
606 for(u32 i=0; i<4; i++)
608 v3s16 dir = side_dirs[i];
610 MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
611 content_t neighbor_content = neighbor.getContent();
612 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
613 MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
614 content_t n_top_c = n_top.getContent();
616 if(neighbor_content == CONTENT_IGNORE)
620 If our topside is liquid and neighbor's topside
621 is liquid, don't draw side face
623 if(top_is_same_liquid && (n_top_c == c_flowing ||
624 n_top_c == c_source || n_top_c == CONTENT_IGNORE))
627 // Don't draw face if neighbor is blocking the view
628 if(n_feat.solidness == 2)
631 bool neighbor_is_same_liquid = (neighbor_content == c_source
632 || neighbor_content == c_flowing);
634 // Don't draw any faces if neighbor same is liquid and top is
636 if(neighbor_is_same_liquid && !top_is_same_liquid)
639 // Use backface culled material if neighbor doesn't have a
641 const TileSpec *current_tile = &tile_liquid;
642 video::SColor *c = &c1;
643 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) {
644 current_tile = &tile_liquid_bfculled;
648 video::S3DVertex vertices[4] =
650 video::S3DVertex(-BS/2,0,BS/2,0,0,0, *c, 0,1),
651 video::S3DVertex(BS/2,0,BS/2,0,0,0, *c, 1,1),
652 video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0),
653 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0),
657 If our topside is liquid, set upper border of face
658 at upper border of node
660 if (top_is_same_liquid) {
661 vertices[2].Pos.Y = 0.5 * BS;
662 vertices[3].Pos.Y = 0.5 * BS;
665 Otherwise upper position of face is liquid level
667 vertices[2].Pos.Y = 0.5 * BS;
668 vertices[3].Pos.Y = 0.5 * BS;
671 If neighbor is liquid, lower border of face is liquid level
673 if (neighbor_is_same_liquid) {
674 vertices[0].Pos.Y = 0.5 * BS;
675 vertices[1].Pos.Y = 0.5 * BS;
678 If neighbor is not liquid, lower border of face is
681 vertices[0].Pos.Y = -0.5 * BS;
682 vertices[1].Pos.Y = -0.5 * BS;
685 for (s32 j = 0; j < 4; j++) {
686 if(dir == v3s16(0,0,1))
687 vertices[j].Pos.rotateXZBy(0);
688 if(dir == v3s16(0,0,-1))
689 vertices[j].Pos.rotateXZBy(180);
690 if(dir == v3s16(-1,0,0))
691 vertices[j].Pos.rotateXZBy(90);
692 if(dir == v3s16(1,0,-0))
693 vertices[j].Pos.rotateXZBy(-90);
695 // Do this to not cause glitches when two liquids are
697 /*if(neighbor_is_same_liquid == false){
698 vertices[j].Pos.X *= 0.98;
699 vertices[j].Pos.Z *= 0.98;
702 if (data->m_smooth_lighting)
703 vertices[j].Color = blendLight(frame, vertices[j].Pos, current_tile->color);
704 vertices[j].Pos += intToFloat(p, BS);
707 u16 indices[] = {0,1,2,2,3,0};
708 // Add to mesh collector
709 collector.append(*current_tile, vertices, 4, indices, 6);
715 if(top_is_same_liquid)
718 video::S3DVertex vertices[4] =
720 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1),
721 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1),
722 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0),
723 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0),
726 for (s32 i = 0; i < 4; i++) {
727 vertices[i].Pos.Y += 0.5 * BS;
728 if (data->m_smooth_lighting)
729 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile_liquid.color);
730 vertices[i].Pos += intToFloat(p, BS);
733 u16 indices[] = {0,1,2,2,3,0};
734 // Add to mesh collector
735 collector.append(tile_liquid, vertices, 4, indices, 6);
737 case NDT_FLOWINGLIQUID:
740 Add flowing liquid to mesh
742 TileSpec tile_liquid = getSpecialTile(f, n, 0);
743 TileSpec tile_liquid_bfculled = getSpecialTile(f, n, 1);
745 bool top_is_same_liquid = false;
746 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
747 content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
748 content_t c_source = nodedef->getId(f.liquid_alternative_source);
749 if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
750 top_is_same_liquid = true;
753 // If this liquid emits light and doesn't contain light, draw
754 // it at what it emits, for an increased effect
755 u8 light_source = nodedef->get(n).light_source;
756 if(light_source != 0){
757 l = decode_light(light_source);
760 // Use the light of the node on top if possible
761 else if(nodedef->get(ntop).param_type == CPT_LIGHT)
762 l = getInteriorLight(ntop, 0, nodedef);
763 // Otherwise use the light of this node (the liquid)
765 l = getInteriorLight(n, 0, nodedef);
766 video::SColor c1 = encode_light_and_color(l,
767 tile_liquid.color, f.light_source);
768 video::SColor c2 = encode_light_and_color(l,
769 tile_liquid_bfculled.color, f.light_source);
771 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
773 // Neighbor liquid levels (key = relative position)
774 // Includes current node
776 struct NeighborData {
781 NeighborData neighbor_data_matrix[27];
783 const u8 neighborflag_top_is_same_liquid = 0x01;
784 v3s16 neighbor_dirs[9] = {
795 for(u32 i=0; i<9; i++)
797 content_t content = CONTENT_AIR;
798 float level = -0.5 * BS;
801 v3s16 p2 = p + neighbor_dirs[i];
802 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
803 if(n2.getContent() != CONTENT_IGNORE)
805 content = n2.getContent();
807 if(n2.getContent() == c_source)
809 else if(n2.getContent() == c_flowing){
810 u8 liquid_level = (n2.param2&LIQUID_LEVEL_MASK);
811 if (liquid_level <= LIQUID_LEVEL_MAX+1-range)
814 liquid_level -= (LIQUID_LEVEL_MAX+1-range);
815 level = (-0.5 + ((float)liquid_level + 0.5) / (float)range) * BS;
818 // Check node above neighbor.
819 // NOTE: This doesn't get executed if neighbor
822 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
823 if(n2.getContent() == c_source ||
824 n2.getContent() == c_flowing)
825 flags |= neighborflag_top_is_same_liquid;
828 NeighborData &neighbor_data =
829 neighbor_data_matrix[NeighborToIndex(neighbor_dirs[i])];
831 neighbor_data.level = level;
832 neighbor_data.content = content;
833 neighbor_data.flags = flags;
836 // Corner heights (average between four liquids)
837 f32 corner_levels[4];
839 v3s16 halfdirs[4] = {
845 for(u32 i=0; i<4; i++)
847 v3s16 cornerdir = halfdirs[i];
848 float cornerlevel = 0;
851 for(u32 j=0; j<4; j++)
853 v3s16 neighbordir = cornerdir - halfdirs[j];
855 NeighborData &neighbor_data =
856 neighbor_data_matrix[NeighborToIndex(neighbordir)];
857 content_t content = neighbor_data.content;
858 // If top is liquid, draw starting from top of node
859 if (neighbor_data.flags & neighborflag_top_is_same_liquid)
861 cornerlevel = 0.5*BS;
865 // Source is always the same height
866 else if(content == c_source)
868 cornerlevel = 0.5 * BS;
872 // Flowing liquid has level information
873 else if(content == c_flowing)
875 cornerlevel += neighbor_data.level;
878 else if(content == CONTENT_AIR)
884 cornerlevel = -0.5*BS+0.2;
885 else if(valid_count > 0)
886 cornerlevel /= valid_count;
887 corner_levels[i] = cornerlevel;
894 v3s16 side_dirs[4] = {
900 s16 side_corners[4][2] = {
906 for(u32 i=0; i<4; i++)
908 v3s16 dir = side_dirs[i];
910 NeighborData& neighbor_data =
911 neighbor_data_matrix[NeighborToIndex(dir)];
913 If our topside is liquid and neighbor's topside
914 is liquid, don't draw side face
916 if (top_is_same_liquid &&
917 (neighbor_data.flags & neighborflag_top_is_same_liquid))
920 content_t neighbor_content = neighbor_data.content;
921 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
923 // Don't draw face if neighbor is blocking the view
924 if(n_feat.solidness == 2)
927 bool neighbor_is_same_liquid = (neighbor_content == c_source
928 || neighbor_content == c_flowing);
930 // Don't draw any faces if neighbor same is liquid and top is
932 if(neighbor_is_same_liquid == true
933 && top_is_same_liquid == false)
936 // Use backface culled material if neighbor doesn't have a
938 const TileSpec *current_tile = &tile_liquid;
939 video::SColor *c = &c1;
940 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) {
941 current_tile = &tile_liquid_bfculled;
945 video::S3DVertex vertices[4] =
947 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,1),
948 video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,1),
949 video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0),
950 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0),
954 If our topside is liquid, set upper border of face
955 at upper border of node
957 if(top_is_same_liquid)
959 vertices[2].Pos.Y = 0.5*BS;
960 vertices[3].Pos.Y = 0.5*BS;
963 Otherwise upper position of face is corner levels
967 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
968 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
972 If neighbor is liquid, lower border of face is corner
975 if(neighbor_is_same_liquid)
977 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
978 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
981 If neighbor is not liquid, lower border of face is
986 vertices[0].Pos.Y = -0.5*BS;
987 vertices[1].Pos.Y = -0.5*BS;
990 for(s32 j=0; j<4; j++)
992 if(dir == v3s16(0,0,1))
993 vertices[j].Pos.rotateXZBy(0);
994 if(dir == v3s16(0,0,-1))
995 vertices[j].Pos.rotateXZBy(180);
996 if(dir == v3s16(-1,0,0))
997 vertices[j].Pos.rotateXZBy(90);
998 if(dir == v3s16(1,0,-0))
999 vertices[j].Pos.rotateXZBy(-90);
1001 // Do this to not cause glitches when two liquids are
1003 /*if(neighbor_is_same_liquid == false){
1004 vertices[j].Pos.X *= 0.98;
1005 vertices[j].Pos.Z *= 0.98;
1008 if (data->m_smooth_lighting)
1009 vertices[j].Color = blendLight(frame, vertices[j].Pos, current_tile->color);
1010 vertices[j].Pos += intToFloat(p, BS);
1013 u16 indices[] = {0,1,2,2,3,0};
1014 // Add to mesh collector
1015 collector.append(*current_tile, vertices, 4, indices, 6);
1019 Generate top side, if appropriate
1022 if(top_is_same_liquid == false)
1024 video::S3DVertex vertices[4] =
1026 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1),
1027 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1),
1028 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0),
1029 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0),
1032 // To get backface culling right, the vertices need to go
1033 // clockwise around the front of the face. And we happened to
1034 // calculate corner levels in exact reverse order.
1035 s32 corner_resolve[4] = {3,2,1,0};
1037 for(s32 i=0; i<4; i++)
1039 //vertices[i].Pos.Y += liquid_level;
1040 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1041 s32 j = corner_resolve[i];
1042 vertices[i].Pos.Y += corner_levels[j];
1043 if (data->m_smooth_lighting)
1044 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile_liquid.color);
1045 vertices[i].Pos += intToFloat(p, BS);
1048 // Default downwards-flowing texture animation goes from
1049 // -Z towards +Z, thus the direction is +Z.
1050 // Rotate texture to make animation go in flow direction
1051 // Positive if liquid moves towards +Z
1052 f32 dz = (corner_levels[side_corners[3][0]] +
1053 corner_levels[side_corners[3][1]]) -
1054 (corner_levels[side_corners[2][0]] +
1055 corner_levels[side_corners[2][1]]);
1056 // Positive if liquid moves towards +X
1057 f32 dx = (corner_levels[side_corners[1][0]] +
1058 corner_levels[side_corners[1][1]]) -
1059 (corner_levels[side_corners[0][0]] +
1060 corner_levels[side_corners[0][1]]);
1061 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG ;
1062 v2f tcoord_center(0.5, 0.5);
1063 v2f tcoord_translate(
1064 blockpos_nodes.Z + z,
1065 blockpos_nodes.X + x);
1066 tcoord_translate.rotateBy(tcoord_angle);
1067 tcoord_translate.X -= floor(tcoord_translate.X);
1068 tcoord_translate.Y -= floor(tcoord_translate.Y);
1070 for(s32 i=0; i<4; i++)
1072 vertices[i].TCoords.rotateBy(
1075 vertices[i].TCoords += tcoord_translate;
1078 v2f t = vertices[0].TCoords;
1079 vertices[0].TCoords = vertices[2].TCoords;
1080 vertices[2].TCoords = t;
1082 u16 indices[] = {0,1,2,2,3,0};
1083 // Add to mesh collector
1084 collector.append(tile_liquid, vertices, 4, indices, 6);
1089 TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1091 u16 l = getInteriorLight(n, 1, nodedef);
1092 video::SColor c = encode_light_and_color(l, tile.color,
1094 for(u32 j=0; j<6; j++)
1096 // Check this neighbor
1097 v3s16 dir = g_6dirs[j];
1098 v3s16 n2p = blockpos_nodes + p + dir;
1099 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1100 // Don't make face if neighbor is of same type
1101 if(n2.getContent() == n.getContent())
1105 applyFacesShading(c2, v3f(dir.X, dir.Y, dir.Z));
1109 video::S3DVertex vertices[4] = {
1110 video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,1),
1111 video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,1),
1112 video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,0),
1113 video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,0),
1116 // Rotations in the g_6dirs format
1118 for(u16 i=0; i<4; i++)
1119 vertices[i].Pos.rotateXZBy(0);
1120 else if(j == 1) // Y+
1121 for(u16 i=0; i<4; i++)
1122 vertices[i].Pos.rotateYZBy(-90);
1123 else if(j == 2) // X+
1124 for(u16 i=0; i<4; i++)
1125 vertices[i].Pos.rotateXZBy(-90);
1126 else if(j == 3) // Z-
1127 for(u16 i=0; i<4; i++)
1128 vertices[i].Pos.rotateXZBy(180);
1129 else if(j == 4) // Y-
1130 for(u16 i=0; i<4; i++)
1131 vertices[i].Pos.rotateYZBy(90);
1132 else if(j == 5) // X-
1133 for(u16 i=0; i<4; i++)
1134 vertices[i].Pos.rotateXZBy(90);
1136 for (u16 i = 0; i < 4; i++) {
1137 if (data->m_smooth_lighting)
1138 vertices[i].Color = blendLight(frame, vertices[i].Pos, vertices[i].Normal, tile.color);
1139 vertices[i].Pos += intToFloat(p, BS);
1142 u16 indices[] = {0,1,2,2,3,0};
1143 // Add to mesh collector
1144 collector.append(tile, vertices, 4, indices, 6);
1147 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
1148 // This is always pre-converted to something else
1149 FATAL_ERROR("NDT_GLASSLIKE_FRAMED_OPTIONAL not pre-converted as expected");
1151 case NDT_GLASSLIKE_FRAMED:
1153 static const v3s16 dirs[6] = {
1162 u16 l = getInteriorLight(n, 1, nodedef);
1165 for (i = 0; i < 6; i++)
1166 tiles[i] = getNodeTile(n, p, dirs[i], data);
1168 video::SColor tile0color = encode_light_and_color(l,
1169 tiles[0].color, f.light_source);
1171 TileSpec glass_tiles[6];
1172 video::SColor glasscolor[6];
1173 if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
1174 glass_tiles[0] = tiles[2];
1175 glass_tiles[1] = tiles[3];
1176 glass_tiles[2] = tiles[1];
1177 glass_tiles[3] = tiles[1];
1178 glass_tiles[4] = tiles[1];
1179 glass_tiles[5] = tiles[1];
1181 for (i = 0; i < 6; i++)
1182 glass_tiles[i] = tiles[1];
1184 for (i = 0; i < 6; i++)
1185 glasscolor[i] = encode_light_and_color(l, glass_tiles[i].color,
1188 u8 param2 = n.getParam2();
1189 bool H_merge = ! bool(param2 & 128);
1190 bool V_merge = ! bool(param2 & 64);
1191 param2 = param2 & 63;
1193 v3f pos = intToFloat(p, BS);
1194 static const float a = BS / 2;
1195 static const float g = a - 0.003;
1196 static const float b = .876 * ( BS / 2 );
1198 static const aabb3f frame_edges[12] = {
1199 aabb3f( b, b,-a, a, a, a), // y+
1200 aabb3f(-a, b,-a,-b, a, a), // y+
1201 aabb3f( b,-a,-a, a,-b, a), // y-
1202 aabb3f(-a,-a,-a,-b,-b, a), // y-
1203 aabb3f( b,-a, b, a, a, a), // x+
1204 aabb3f( b,-a,-a, a, a,-b), // x+
1205 aabb3f(-a,-a, b,-b, a, a), // x-
1206 aabb3f(-a,-a,-a,-b, a,-b), // x-
1207 aabb3f(-a, b, b, a, a, a), // z+
1208 aabb3f(-a,-a, b, a,-b, a), // z+
1209 aabb3f(-a,-a,-a, a,-b,-b), // z-
1210 aabb3f(-a, b,-a, a, a,-b) // z-
1212 static const aabb3f glass_faces[6] = {
1213 aabb3f(-g, g,-g, g, g, g), // y+
1214 aabb3f(-g,-g,-g, g,-g, g), // y-
1215 aabb3f( g,-g,-g, g, g, g), // x+
1216 aabb3f(-g,-g,-g,-g, g, g), // x-
1217 aabb3f(-g,-g, g, g, g, g), // z+
1218 aabb3f(-g,-g,-g, g, g,-g) // z-
1221 // table of node visible faces, 0 = invisible
1222 int visible_faces[6] = {0,0,0,0,0,0};
1224 // table of neighbours, 1 = same type, checked with g_26dirs
1225 int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1227 // g_26dirs to check when only horizontal merge is allowed
1228 int nb_H_dirs[8] = {0,2,3,5,10,11,12,13};
1230 content_t current = n.getContent();
1235 // neighbours checks for frames visibility
1237 if (!H_merge && V_merge) {
1238 n2p = blockpos_nodes + p + g_26dirs[1];
1239 n2 = data->m_vmanip.getNodeNoEx(n2p);
1240 n2c = n2.getContent();
1241 if (n2c == current || n2c == CONTENT_IGNORE)
1243 n2p = blockpos_nodes + p + g_26dirs[4];
1244 n2 = data->m_vmanip.getNodeNoEx(n2p);
1245 n2c = n2.getContent();
1246 if (n2c == current || n2c == CONTENT_IGNORE)
1248 } else if (H_merge && !V_merge) {
1249 for(i = 0; i < 8; i++) {
1250 n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]];
1251 n2 = data->m_vmanip.getNodeNoEx(n2p);
1252 n2c = n2.getContent();
1253 if (n2c == current || n2c == CONTENT_IGNORE)
1254 nb[nb_H_dirs[i]] = 1;
1256 } else if (H_merge && V_merge) {
1257 for(i = 0; i < 18; i++) {
1258 n2p = blockpos_nodes + p + g_26dirs[i];
1259 n2 = data->m_vmanip.getNodeNoEx(n2p);
1260 n2c = n2.getContent();
1261 if (n2c == current || n2c == CONTENT_IGNORE)
1266 // faces visibility checks
1269 visible_faces[0] = 1;
1270 visible_faces[1] = 1;
1272 for(i = 0; i < 2; i++) {
1273 n2p = blockpos_nodes + p + dirs[i];
1274 n2 = data->m_vmanip.getNodeNoEx(n2p);
1275 n2c = n2.getContent();
1277 visible_faces[i] = 1;
1282 visible_faces[2] = 1;
1283 visible_faces[3] = 1;
1284 visible_faces[4] = 1;
1285 visible_faces[5] = 1;
1287 for(i = 2; i < 6; i++) {
1288 n2p = blockpos_nodes + p + dirs[i];
1289 n2 = data->m_vmanip.getNodeNoEx(n2p);
1290 n2c = n2.getContent();
1292 visible_faces[i] = 1;
1296 static const u8 nb_triplet[12*3] = {
1297 1,2, 7, 1,5, 6, 4,2,15, 4,5,14,
1298 2,0,11, 2,3,13, 5,0,10, 5,3,12,
1299 0,1, 8, 0,4,16, 3,4,17, 3,1, 9
1304 for(i = 0; i < 12; i++)
1307 if (nb[nb_triplet[i*3+2]])
1308 edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]];
1310 edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]];
1313 box = frame_edges[i];
1314 makeAutoLightedCuboid(&collector, data, pos, box, tiles[0], tile0color, frame);
1317 for(i = 0; i < 6; i++)
1319 if (!visible_faces[i])
1321 box = glass_faces[i];
1322 makeAutoLightedCuboid(&collector, data, pos, box, glass_tiles[i], glasscolor[i], frame);
1325 if (param2 > 0 && f.special_tiles[0].texture) {
1326 // Interior volume level is in range 0 .. 63,
1327 // convert it to -0.5 .. 0.5
1328 float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0);
1329 TileSpec tile = getSpecialTile(f, n, 0);
1330 video::SColor special_color = encode_light_and_color(l,
1331 tile.color, f.light_source);
1332 float offset = 0.003;
1333 box = aabb3f(visible_faces[3] ? -b : -a + offset,
1334 visible_faces[1] ? -b : -a + offset,
1335 visible_faces[5] ? -b : -a + offset,
1336 visible_faces[2] ? b : a - offset,
1337 visible_faces[0] ? b * vlev : a * vlev - offset,
1338 visible_faces[4] ? b : a - offset);
1339 makeAutoLightedCuboid(&collector, data, pos, box, tile, special_color, frame);
1344 TileSpec tile_leaves = getNodeTile(n, p,
1345 v3s16(0,0,0), data);
1346 u16 l = getInteriorLight(n, 1, nodedef);
1347 video::SColor c = encode_light_and_color(l,
1348 tile_leaves.color, f.light_source);
1350 v3f pos = intToFloat(p, BS);
1351 aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
1352 makeAutoLightedCuboid(&collector, data, pos, box, tile_leaves, c, frame);
1354 case NDT_ALLFACES_OPTIONAL:
1355 // This is always pre-converted to something else
1356 FATAL_ERROR("NDT_ALLFACES_OPTIONAL not pre-converted");
1360 v3s16 dir = n.getWallMountedDir(nodedef);
1363 if(dir == v3s16(0,-1,0)){
1364 tileindex = 0; // floor
1365 } else if(dir == v3s16(0,1,0)){
1366 tileindex = 1; // ceiling
1367 // For backwards compatibility
1368 } else if(dir == v3s16(0,0,0)){
1369 tileindex = 0; // floor
1371 tileindex = 2; // side
1374 TileSpec tile = getNodeTileN(n, p, tileindex, data);
1375 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1376 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1378 u16 l = getInteriorLight(n, 1, nodedef);
1379 video::SColor c = encode_light_and_color(l, tile.color,
1382 float s = BS/2*f.visual_scale;
1383 // Wall at X+ of node
1384 video::S3DVertex vertices[4] =
1386 video::S3DVertex(-s,-s,0, 0,0,0, c, 0,1),
1387 video::S3DVertex( s,-s,0, 0,0,0, c, 1,1),
1388 video::S3DVertex( s, s,0, 0,0,0, c, 1,0),
1389 video::S3DVertex(-s, s,0, 0,0,0, c, 0,0),
1392 for (s32 i = 0; i < 4; i++)
1394 if(dir == v3s16(1,0,0))
1395 vertices[i].Pos.rotateXZBy(0);
1396 if(dir == v3s16(-1,0,0))
1397 vertices[i].Pos.rotateXZBy(180);
1398 if(dir == v3s16(0,0,1))
1399 vertices[i].Pos.rotateXZBy(90);
1400 if(dir == v3s16(0,0,-1))
1401 vertices[i].Pos.rotateXZBy(-90);
1402 if(dir == v3s16(0,-1,0))
1403 vertices[i].Pos.rotateXZBy(45);
1404 if(dir == v3s16(0,1,0))
1405 vertices[i].Pos.rotateXZBy(-45);
1407 if (data->m_smooth_lighting)
1408 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1409 vertices[i].Pos += intToFloat(p, BS);
1412 u16 indices[] = {0,1,2,2,3,0};
1413 // Add to mesh collector
1414 collector.append(tile, vertices, 4, indices, 6);
1418 TileSpec tile = getNodeTileN(n, p, 0, data);
1419 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1420 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1422 u16 l = getInteriorLight(n, 0, nodedef);
1423 video::SColor c = encode_light_and_color(l, tile.color,
1426 float d = (float)BS/16;
1427 float s = BS/2*f.visual_scale;
1428 // Wall at X+ of node
1429 video::S3DVertex vertices[4] =
1431 video::S3DVertex(BS/2-d, s, s, 0,0,0, c, 0,0),
1432 video::S3DVertex(BS/2-d, s, -s, 0,0,0, c, 1,0),
1433 video::S3DVertex(BS/2-d, -s, -s, 0,0,0, c, 1,1),
1434 video::S3DVertex(BS/2-d, -s, s, 0,0,0, c, 0,1),
1437 v3s16 dir = n.getWallMountedDir(nodedef);
1439 for (s32 i = 0; i < 4; i++)
1441 if(dir == v3s16(1,0,0))
1442 vertices[i].Pos.rotateXZBy(0);
1443 if(dir == v3s16(-1,0,0))
1444 vertices[i].Pos.rotateXZBy(180);
1445 if(dir == v3s16(0,0,1))
1446 vertices[i].Pos.rotateXZBy(90);
1447 if(dir == v3s16(0,0,-1))
1448 vertices[i].Pos.rotateXZBy(-90);
1449 if(dir == v3s16(0,-1,0))
1450 vertices[i].Pos.rotateXYBy(-90);
1451 if(dir == v3s16(0,1,0))
1452 vertices[i].Pos.rotateXYBy(90);
1454 if (data->m_smooth_lighting)
1455 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1456 vertices[i].Pos += intToFloat(p, BS);
1459 u16 indices[] = {0,1,2,2,3,0};
1460 // Add to mesh collector
1461 collector.append(tile, vertices, 4, indices, 6);
1465 PseudoRandom rng(x<<8 | z | y<<16);
1467 TileSpec tile = getNodeTileN(n, p, 0, data);
1468 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1470 u16 l = getInteriorLight(n, 1, nodedef);
1471 video::SColor c = encode_light_and_color(l, tile.color,
1474 float s = BS / 2 * f.visual_scale;
1475 // add sqrt(2) visual scale
1476 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x10) != 0))
1479 float random_offset_X = .0;
1480 float random_offset_Z = .0;
1481 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) {
1482 random_offset_X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1483 random_offset_Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1486 for (int j = 0; j < 4; j++) {
1487 video::S3DVertex vertices[4] =
1489 video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1),
1490 video::S3DVertex( s,-BS/2, 0, 0,0,0, c, 1,1),
1491 video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
1492 video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
1495 float rotate_degree = 0;
1497 if (f.param_type_2 == CPT2_DEGROTATE)
1498 rotate_degree = n.param2 * 2;
1499 if (f.param_type_2 != CPT2_MESHOPTIONS) {
1501 for (u16 i = 0; i < 4; i++)
1502 vertices[i].Pos.rotateXZBy(46 + rotate_degree);
1503 } else if (j == 1) {
1504 for (u16 i = 0; i < 4; i++)
1505 vertices[i].Pos.rotateXZBy(-44 + rotate_degree);
1508 p2mesh = n.param2 & 0x7;
1513 for (u16 i = 0; i < 4; i++)
1514 vertices[i].Pos.rotateXZBy(46);
1515 } else if (j == 1) {
1516 for (u16 i = 0; i < 4; i++)
1517 vertices[i].Pos.rotateXZBy(-44);
1523 for (u16 i = 0; i < 4; i++)
1524 vertices[i].Pos.rotateXZBy(91);
1525 } else if (j == 1) {
1526 for (u16 i = 0; i < 4; i++)
1527 vertices[i].Pos.rotateXZBy(1);
1533 for (u16 i = 0; i < 4; i++)
1534 vertices[i].Pos.rotateXZBy(121);
1535 } else if (j == 1) {
1536 for (u16 i = 0; i < 4; i++)
1537 vertices[i].Pos.rotateXZBy(241);
1538 } else { // (j == 2)
1539 for (u16 i = 0; i < 4; i++)
1540 vertices[i].Pos.rotateXZBy(1);
1547 for (u16 i = 0; i < 4; i++) {
1548 vertices[i].Pos.rotateXZBy(1);
1549 vertices[i].Pos.Z += BS / 4;
1553 for (u16 i = 0; i < 4; i++) {
1554 vertices[i].Pos.rotateXZBy(91);
1555 vertices[i].Pos.X += BS / 4;
1559 for (u16 i = 0; i < 4; i++) {
1560 vertices[i].Pos.rotateXZBy(181);
1561 vertices[i].Pos.Z -= BS / 4;
1565 for (u16 i = 0; i < 4; i++) {
1566 vertices[i].Pos.rotateXZBy(271);
1567 vertices[i].Pos.X -= BS / 4;
1573 // outward leaning #-like
1576 for (u16 i = 2; i < 4; i++)
1577 vertices[i].Pos.Z -= BS / 2;
1578 for (u16 i = 0; i < 4; i++)
1579 vertices[i].Pos.rotateXZBy(1);
1582 for (u16 i = 2; i < 4; i++)
1583 vertices[i].Pos.Z -= BS / 2;
1584 for (u16 i = 0; i < 4; i++)
1585 vertices[i].Pos.rotateXZBy(91);
1588 for (u16 i = 2; i < 4; i++)
1589 vertices[i].Pos.Z -= BS / 2;
1590 for (u16 i = 0; i < 4; i++)
1591 vertices[i].Pos.rotateXZBy(181);
1594 for (u16 i = 2; i < 4; i++)
1595 vertices[i].Pos.Z -= BS / 2;
1596 for (u16 i = 0; i < 4; i++)
1597 vertices[i].Pos.rotateXZBy(271);
1604 for (int i = 0; i < 4; i++) {
1605 if (data->m_smooth_lighting)
1606 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1607 vertices[i].Pos += intToFloat(p, BS);
1608 // move to a random spot to avoid moire
1609 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) {
1610 vertices[i].Pos.X += random_offset_X;
1611 vertices[i].Pos.Z += random_offset_Z;
1613 // randomly move each face up/down
1614 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x20) != 0)) {
1615 PseudoRandom yrng(j | x<<16 | z<<8 | y<<24 );
1616 vertices[i].Pos.Y -= BS * ((yrng.next() % 16 / 16.0) * 0.125);
1620 u16 indices[] = {0, 1, 2, 2, 3, 0};
1621 // Add to mesh collector
1622 collector.append(tile, vertices, 4, indices, 6);
1624 // stop adding faces for meshes with less than 4 faces
1625 if (f.param_type_2 == CPT2_MESHOPTIONS) {
1626 if (((p2mesh == 0) || (p2mesh == 1)) && (j == 1))
1628 else if ((p2mesh == 2) && (j == 2))
1630 } else if (j == 1) {
1638 TileSpec tile = getNodeTileN(n, p, 0, data);
1639 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1641 u16 l = getInteriorLight(n, 1, nodedef);
1642 video::SColor c = encode_light_and_color(l, tile.color,
1645 float s = BS / 2 * f.visual_scale;
1647 content_t current = n.getContent();
1652 static const v3s16 dirs[6] = {
1661 int doDraw[6] = {0, 0, 0, 0, 0, 0};
1663 bool drawAllFaces = true;
1665 // Check for adjacent nodes
1666 for (int i = 0; i < 6; i++) {
1667 n2p = blockpos_nodes + p + dirs[i];
1668 n2 = data->m_vmanip.getNodeNoEx(n2p);
1669 n2c = n2.getContent();
1670 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1673 drawAllFaces = false;
1678 for (int j = 0; j < 6; j++) {
1680 video::S3DVertex vertices[4] = {
1681 video::S3DVertex(-s, -BS / 2, 0, 0, 0, 0, c, 0, 1),
1682 video::S3DVertex( s, -BS / 2, 0, 0, 0, 0, c, 1, 1),
1683 video::S3DVertex( s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 1, 0),
1684 video::S3DVertex(-s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 0, 0),
1687 // Calculate which faces should be drawn, (top or sides)
1688 if (j == 0 && (drawAllFaces ||
1689 (doDraw[3] == 1 || doDraw[1] == 1))) {
1690 for (int i = 0; i < 4; i++) {
1691 vertices[i].Pos.rotateXZBy(90);
1692 vertices[i].Pos.rotateXYBy(-10);
1693 vertices[i].Pos.X -= 4.0;
1695 } else if (j == 1 && (drawAllFaces ||
1696 (doDraw[5] == 1 || doDraw[1] == 1))) {
1697 for (int i = 0; i < 4; i++) {
1698 vertices[i].Pos.rotateXZBy(180);
1699 vertices[i].Pos.rotateYZBy(10);
1700 vertices[i].Pos.Z -= 4.0;
1702 } else if (j == 2 && (drawAllFaces ||
1703 (doDraw[2] == 1 || doDraw[1] == 1))) {
1704 for (int i = 0; i < 4; i++) {
1705 vertices[i].Pos.rotateXZBy(270);
1706 vertices[i].Pos.rotateXYBy(10);
1707 vertices[i].Pos.X += 4.0;
1709 } else if (j == 3 && (drawAllFaces ||
1710 (doDraw[4] == 1 || doDraw[1] == 1))) {
1711 for (int i = 0; i < 4; i++) {
1712 vertices[i].Pos.rotateYZBy(-10);
1713 vertices[i].Pos.Z += 4.0;
1715 // Center cross-flames
1716 } else if (j == 4 && (drawAllFaces || doDraw[1] == 1)) {
1717 for (int i = 0; i < 4; i++) {
1718 vertices[i].Pos.rotateXZBy(45);
1720 } else if (j == 5 && (drawAllFaces || doDraw[1] == 1)) {
1721 for (int i = 0; i < 4; i++) {
1722 vertices[i].Pos.rotateXZBy(-45);
1724 // Render flames on bottom of node above
1725 } else if (j == 0 && doDraw[0] == 1 && doDraw[1] == 0) {
1726 for (int i = 0; i < 4; i++) {
1727 vertices[i].Pos.rotateYZBy(70);
1728 vertices[i].Pos.rotateXZBy(90);
1729 vertices[i].Pos.Y += 4.84;
1730 vertices[i].Pos.X -= 4.7;
1732 } else if (j == 1 && doDraw[0] == 1 && doDraw[1] == 0) {
1733 for (int i = 0; i < 4; i++) {
1734 vertices[i].Pos.rotateYZBy(70);
1735 vertices[i].Pos.rotateXZBy(180);
1736 vertices[i].Pos.Y += 4.84;
1737 vertices[i].Pos.Z -= 4.7;
1739 } else if (j == 2 && doDraw[0] == 1 && doDraw[1] == 0) {
1740 for (int i = 0; i < 4; i++) {
1741 vertices[i].Pos.rotateYZBy(70);
1742 vertices[i].Pos.rotateXZBy(270);
1743 vertices[i].Pos.Y += 4.84;
1744 vertices[i].Pos.X += 4.7;
1746 } else if (j == 3 && doDraw[0] == 1 && doDraw[1] == 0) {
1747 for (int i = 0; i < 4; i++) {
1748 vertices[i].Pos.rotateYZBy(70);
1749 vertices[i].Pos.Y += 4.84;
1750 vertices[i].Pos.Z += 4.7;
1753 // Skip faces that aren't adjacent to a node
1757 for (int i = 0; i < 4; i++) {
1758 vertices[i].Pos *= f.visual_scale;
1759 if (data->m_smooth_lighting)
1760 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1761 vertices[i].Pos += intToFloat(p, BS);
1764 u16 indices[] = {0, 1, 2, 2, 3, 0};
1765 // Add to mesh collector
1766 collector.append(tile, vertices, 4, indices, 6);
1771 TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1772 TileSpec tile_nocrack = tile;
1773 tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1775 // Put wood the right way around in the posts
1776 TileSpec tile_rot = tile;
1777 tile_rot.rotation = 1;
1779 u16 l = getInteriorLight(n, 1, nodedef);
1780 video::SColor c = encode_light_and_color(l, tile.color,
1783 const f32 post_rad=(f32)BS/8;
1784 const f32 bar_rad=(f32)BS/16;
1785 const f32 bar_len=(f32)(BS/2)-post_rad;
1787 v3f pos = intToFloat(p, BS);
1789 // The post - always present
1790 aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
1792 6/16.,6/16.,10/16.,10/16.,
1793 6/16.,6/16.,10/16.,10/16.,
1798 makeAutoLightedCuboidEx(&collector, data, pos, post, tile_rot, postuv, c, frame);
1800 // Now a section of fence, +X, if there's a post there
1803 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1804 const ContentFeatures *f2 = &nodedef->get(n2);
1805 if(f2->drawtype == NDT_FENCELIKE)
1807 aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
1808 bar_len+BS/2,bar_rad+BS/4,bar_rad);
1810 0/16.,2/16.,16/16.,4/16.,
1811 0/16.,4/16.,16/16.,6/16.,
1812 6/16.,6/16.,8/16.,8/16.,
1813 10/16.,10/16.,12/16.,12/16.,
1814 0/16.,8/16.,16/16.,10/16.,
1815 0/16.,14/16.,16/16.,16/16.};
1816 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame);
1817 bar.MinEdge.Y -= BS/2;
1818 bar.MaxEdge.Y -= BS/2;
1819 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame);
1822 // Now a section of fence, +Z, if there's a post there
1825 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1826 f2 = &nodedef->get(n2);
1827 if(f2->drawtype == NDT_FENCELIKE)
1829 aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
1830 bar_rad,bar_rad+BS/4,bar_len+BS/2);
1832 3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
1833 4/16.,1/16.,6/16.,5/16., // for wood texture instead
1834 0/16.,9/16.,16/16.,11/16.,
1835 0/16.,6/16.,16/16.,8/16.,
1836 6/16.,6/16.,8/16.,8/16.,
1837 10/16.,10/16.,12/16.,12/16.};
1838 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame);
1839 bar.MinEdge.Y -= BS/2;
1840 bar.MaxEdge.Y -= BS/2;
1841 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame);
1846 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) */
1849 content_t thiscontent = n.getContent();
1850 std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
1851 int self_group = ((ItemGroupList) nodedef->get(n).groups)[groupname];
1854 for (s8 y0 = -1; y0 <= 1; y0++) {
1855 // Prevent from indexing never used coordinates
1856 for (s8 xz = -1; xz <= 1; xz++) {
1859 MapNode n_xy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x + xz, y + y0, z));
1860 MapNode n_zy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y + y0, z + xz));
1861 const ContentFeatures &def_xy = nodedef->get(n_xy);
1862 const ContentFeatures &def_zy = nodedef->get(n_zy);
1864 // Check if current node would connect with the rail
1865 is_rail_x[index] = ((def_xy.drawtype == NDT_RAILLIKE
1866 && ((ItemGroupList) def_xy.groups)[groupname] == self_group)
1867 || n_xy.getContent() == thiscontent);
1869 is_rail_z[index] = ((def_zy.drawtype == NDT_RAILLIKE
1870 && ((ItemGroupList) def_zy.groups)[groupname] == self_group)
1871 || n_zy.getContent() == thiscontent);
1876 bool is_rail_x_all[2]; // [0] = negative x, [1] = positive x coordinate from the current node position
1877 bool is_rail_z_all[2];
1878 is_rail_x_all[0] = is_rail_x[0] || is_rail_x[2] || is_rail_x[4];
1879 is_rail_x_all[1] = is_rail_x[1] || is_rail_x[3] || is_rail_x[5];
1880 is_rail_z_all[0] = is_rail_z[0] || is_rail_z[2] || is_rail_z[4];
1881 is_rail_z_all[1] = is_rail_z[1] || is_rail_z[3] || is_rail_z[5];
1883 // reasonable default, flat straight unrotated rail
1884 bool is_straight = true;
1885 int adjacencies = 0;
1889 // check for sloped rail
1890 if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5]) {
1891 adjacencies = 5; // 5 means sloped
1892 is_straight = true; // sloped is always straight
1894 // is really straight, rails on both sides
1895 is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
1896 adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
1899 switch (adjacencies) {
1901 if (is_rail_x_all[0] || is_rail_x_all[1])
1906 tileindex = 1; // curved
1907 if (is_rail_x_all[0] && is_rail_x_all[1])
1909 if (is_rail_z_all[0] && is_rail_z_all[1]) {
1913 else if (is_rail_x_all[0] && is_rail_z_all[0])
1915 else if (is_rail_x_all[0] && is_rail_z_all[1])
1917 else if (is_rail_x_all[1] && is_rail_z_all[1])
1921 // here is where the potential to 'switch' a junction is, but not implemented at present
1922 tileindex = 2; // t-junction
1923 if(!is_rail_x_all[1])
1925 if(!is_rail_z_all[0])
1927 if(!is_rail_z_all[1])
1931 tileindex = 3; // crossing
1945 TileSpec tile = getNodeTileN(n, p, tileindex, data);
1946 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1947 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1949 u16 l = getInteriorLight(n, 0, nodedef);
1950 video::SColor c = encode_light_and_color(l, tile.color,
1953 float d = (float)BS/64;
1957 if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5])
1958 g = 1; //Object is at a slope
1960 video::S3DVertex vertices[4] =
1962 video::S3DVertex(-s, -s+d, -s, 0, 0, 0, c, 0, 1),
1963 video::S3DVertex( s, -s+d, -s, 0, 0, 0, c, 1, 1),
1964 video::S3DVertex( s, g*s+d, s, 0, 0, 0, c, 1, 0),
1965 video::S3DVertex(-s, g*s+d, s, 0, 0, 0, c, 0, 0),
1968 for(s32 i=0; i<4; i++)
1971 vertices[i].Pos.rotateXZBy(angle);
1972 if (data->m_smooth_lighting)
1973 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1974 vertices[i].Pos += intToFloat(p, BS);
1977 u16 indices[] = {0,1,2,2,3,0};
1978 collector.append(tile, vertices, 4, indices, 6);
1982 static const v3s16 tile_dirs[6] = {
1992 video::SColor colors[6];
1993 for (int j = 0; j < 6; j++) {
1994 // Handles facedir rotation for textures
1995 tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
1997 if (!data->m_smooth_lighting) {
1998 u16 l = getInteriorLight(n, 1, nodedef);
1999 for (int j = 0; j < 6; j++)
2000 colors[j] = encode_light_and_color(l, tiles[j].color, f.light_source);
2003 v3f pos = intToFloat(p, BS);
2007 // locate possible neighboring nodes to connect to
2008 if (f.node_box.type == NODEBOX_CONNECTED) {
2012 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
2016 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
2020 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
2024 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
2028 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
2032 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
2035 std::vector<aabb3f> boxes;
2036 n.getNodeBoxes(nodedef, &boxes, neighbors);
2037 for (std::vector<aabb3f>::iterator
2039 i != boxes.end(); ++i) {
2042 f32 dx1 = box.MinEdge.X;
2043 f32 dy1 = box.MinEdge.Y;
2044 f32 dz1 = box.MinEdge.Z;
2045 f32 dx2 = box.MaxEdge.X;
2046 f32 dy2 = box.MaxEdge.Y;
2047 f32 dz2 = box.MaxEdge.Z;
2052 if (box.MinEdge.X > box.MaxEdge.X)
2053 std::swap(box.MinEdge.X, box.MaxEdge.X);
2054 if (box.MinEdge.Y > box.MaxEdge.Y)
2055 std::swap(box.MinEdge.Y, box.MaxEdge.Y);
2056 if (box.MinEdge.Z > box.MaxEdge.Z)
2057 std::swap(box.MinEdge.Z, box.MaxEdge.Z);
2060 // Compute texture coords
2061 f32 tx1 = (box.MinEdge.X/BS)+0.5;
2062 f32 ty1 = (box.MinEdge.Y/BS)+0.5;
2063 f32 tz1 = (box.MinEdge.Z/BS)+0.5;
2064 f32 tx2 = (box.MaxEdge.X/BS)+0.5;
2065 f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
2066 f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
2069 tx1, 1-tz2, tx2, 1-tz1,
2073 tz1, 1-ty2, tz2, 1-ty1,
2075 1-tz2, 1-ty2, 1-tz1, 1-ty1,
2077 1-tx2, 1-ty2, 1-tx1, 1-ty1,
2079 tx1, 1-ty2, tx2, 1-ty1,
2081 if (data->m_smooth_lighting) {
2083 for (int j = 0; j < 8; ++j) {
2084 f32 x = (j & 4) ? dx2 : dx1;
2085 f32 y = (j & 2) ? dy2 : dy1;
2086 f32 z = (j & 1) ? dz2 : dz1;
2087 lights[j] = blendLight(frame, core::vector3df(x, y, z));
2089 makeSmoothLightedCuboid(&collector, box, tiles, 6, lights, txc, f.light_source);
2091 makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source);
2097 v3f pos = intToFloat(p, BS);
2098 u16 l = getInteriorLight(n, 1, nodedef);
2100 if (f.param_type_2 == CPT2_FACEDIR ||
2101 f.param_type_2 == CPT2_COLORED_FACEDIR) {
2102 facedir = n.getFaceDir(nodedef);
2103 } else if (f.param_type_2 == CPT2_WALLMOUNTED ||
2104 f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
2105 //convert wallmounted to 6dfacedir.
2106 //when cache enabled, it is already converted
2107 facedir = n.getWallMounted(nodedef);
2108 if (!enable_mesh_cache) {
2109 static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
2110 facedir = wm_to_6d[facedir];
2114 if (!data->m_smooth_lighting && f.mesh_ptr[facedir]) {
2115 // use cached meshes
2116 for (u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) {
2117 const TileSpec &tile = getNodeTileN(n, p, j, data);
2118 scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j);
2119 collector.append(tile, (video::S3DVertex *)
2120 buf->getVertices(), buf->getVertexCount(),
2121 buf->getIndices(), buf->getIndexCount(), pos,
2122 encode_light_and_color(l, tile.color, f.light_source),
2125 } else if (f.mesh_ptr[0]) {
2126 // no cache, clone and rotate mesh
2127 scene::IMesh* mesh = cloneMesh(f.mesh_ptr[0]);
2128 rotateMeshBy6dFacedir(mesh, facedir);
2129 recalculateBoundingBox(mesh);
2130 meshmanip->recalculateNormals(mesh, true, false);
2131 for (u16 j = 0; j < mesh->getMeshBufferCount(); j++) {
2132 const TileSpec &tile = getNodeTileN(n, p, j, data);
2133 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
2134 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
2135 u32 vertex_count = buf->getVertexCount();
2136 if (data->m_smooth_lighting) {
2137 for (u16 m = 0; m < vertex_count; ++m) {
2138 video::S3DVertex &vertex = vertices[m];
2139 vertex.Color = blendLight(frame, vertex.Pos, vertex.Normal, tile.color);
2142 collector.append(tile, vertices, vertex_count,
2143 buf->getIndices(), buf->getIndexCount());
2145 collector.append(tile, vertices, vertex_count,
2146 buf->getIndices(), buf->getIndexCount(), pos,
2147 encode_light_and_color(l, tile.color, f.light_source),