]> git.lizzy.rs Git - minetest.git/blob - src/content_mapblock.cpp
Revert "Plantlike: Fix visual_scale being applied squared (#5115)"
[minetest.git] / src / content_mapblock.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
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
24 #include "settings.h"
25 #include "nodedef.h"
26 #include "client/tile.h"
27 #include "mesh.h"
28 #include <IMeshManipulator.h>
29 #include "client.h"
30 #include "log.h"
31 #include "noise.h"
32
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
36
37 struct LightFrame
38 {
39         f32 lightsA[8];
40         f32 lightsB[8];
41         u8 light_source;
42 };
43
44 static const v3s16 light_dirs[8] = {
45         v3s16(-1, -1, -1),
46         v3s16(-1, -1,  1),
47         v3s16(-1,  1, -1),
48         v3s16(-1,  1,  1),
49         v3s16( 1, -1, -1),
50         v3s16( 1, -1,  1),
51         v3s16( 1,  1, -1),
52         v3s16( 1,  1,  1),
53 };
54
55 // Create a cuboid.
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
68 //                  passing NULL.
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)
73 {
74         assert(tilecount >= 1 && tilecount <= 6); // pre-condition
75
76         v3f min = box.MinEdge;
77         v3f max = box.MaxEdge;
78
79         if(txc == NULL) {
80                 static const f32 txc_default[24] = {
81                         0,0,1,1,
82                         0,0,1,1,
83                         0,0,1,1,
84                         0,0,1,1,
85                         0,0,1,1,
86                         0,0,1,1
87                 };
88                 txc = txc_default;
89         }
90
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];
97         if (!light_source) {
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));
104         }
105
106         video::S3DVertex vertices[24] =
107         {
108                 // up
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]),
113                 // down
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]),
118                 // right
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]),
123                 // left
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]),
128                 // back
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]),
133                 // front
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]),
138         };
139
140         for(int i = 0; i < 6; i++)
141                                 {
142                                 switch (tiles[MYMIN(i, tilecount-1)].rotation)
143                                 {
144                                 case 0:
145                                         break;
146                                 case 1: //R90
147                                         for (int x = 0; x < 4; x++)
148                                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
149                                         break;
150                                 case 2: //R180
151                                         for (int x = 0; x < 4; x++)
152                                                 vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
153                                         break;
154                                 case 3: //R270
155                                         for (int x = 0; x < 4; x++)
156                                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
157                                         break;
158                                 case 4: //FXR90
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));
162                                         }
163                                         break;
164                                 case 5: //FXR270
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));
168                                         }
169                                         break;
170                                 case 6: //FYR90
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));
174                                         }
175                                         break;
176                                 case 7: //FYR270
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));
180                                         }
181                                         break;
182                                 case 8: //FX
183                                         for (int x = 0; x < 4; x++){
184                                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
185                                         }
186                                         break;
187                                 case 9: //FY
188                                         for (int x = 0; x < 4; x++){
189                                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
190                                         }
191                                         break;
192                                 default:
193                                         break;
194                                 }
195                         }
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);
201         }
202 }
203
204 // Create a cuboid.
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)
221 {
222         assert(tilecount >= 1 && tilecount <= 6); // pre-condition
223
224         v3f min = box.MinEdge;
225         v3f max = box.MaxEdge;
226
227         if (txc == NULL) {
228                 static const f32 txc_default[24] = {
229                         0,0,1,1,
230                         0,0,1,1,
231                         0,0,1,1,
232                         0,0,1,1,
233                         0,0,1,1,
234                         0,0,1,1
235                 };
236                 txc = txc_default;
237         }
238         static const u8 light_indices[24] = {
239                 3, 7, 6, 2,
240                 0, 4, 5, 1,
241                 6, 7, 5, 4,
242                 3, 2, 0, 1,
243                 7, 3, 1, 5,
244                 2, 6, 4, 0
245         };
246         video::S3DVertex vertices[24] = {
247                 // up
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]),
252                 // down
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]),
257                 // right
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]),
262                 // left
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]),
267                 // back
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]),
272                 // front
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]),
277         };
278
279         for(int i = 0; i < 6; i++) {
280                 switch (tiles[MYMIN(i, tilecount-1)].rotation) {
281                 case 0:
282                         break;
283                 case 1: //R90
284                         for (int x = 0; x < 4; x++)
285                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
286                         break;
287                 case 2: //R180
288                         for (int x = 0; x < 4; x++)
289                                 vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
290                         break;
291                 case 3: //R270
292                         for (int x = 0; x < 4; x++)
293                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
294                         break;
295                 case 4: //FXR90
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));
299                         }
300                         break;
301                 case 5: //FXR270
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));
305                         }
306                         break;
307                 case 6: //FYR90
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));
311                         }
312                         break;
313                 case 7: //FYR270
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));
317                         }
318                         break;
319                 case 8: //FX
320                         for (int x = 0; x < 4; x++) {
321                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
322                         }
323                         break;
324                 case 9: //FY
325                         for (int x = 0; x < 4; x++) {
326                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
327                         }
328                         break;
329                 default:
330                         break;
331                 }
332         }
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);
338                 if (!light_source)
339                         applyFacesShading(vertices[j].Color, vertices[j].Normal);
340         }
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);
345         }
346 }
347
348 // Create a cuboid.
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
361 //                  passing NULL.
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)
366 {
367         video::SColor color[6];
368         for (u8 i = 0; i < 6; i++)
369                 color[i] = c;
370         makeCuboid(collector, box, tiles, tilecount, color, txc, light_source);
371 }
372
373 // Gets the base lighting values for a node
374 //  frame  - resulting (opaque) data
375 //  p      - node position (absolute)
376 //  data   - ...
377 //  light_source - node light emission level
378 static void getSmoothLightFrame(LightFrame *frame, const v3s16 &p, MeshMakeData *data, u8 light_source)
379 {
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;
384         }
385         frame->light_source = light_source;
386 }
387
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)
392 {
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);
396         f32 lightA = 0.0;
397         f32 lightB = 0.0;
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];
404         }
405         return
406                 core::clamp(core::round32(lightA), 0, 255) |
407                 core::clamp(core::round32(lightB), 0, 255) << 8;
408 }
409
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)
416 {
417         u16 light = blendLight(frame, vertex_pos);
418         return encode_light_and_color(light, tile_color, frame.light_source);
419 }
420
421 static video::SColor blendLight(const LightFrame &frame,
422         const core::vector3df& vertex_pos, const core::vector3df& vertex_normal,
423         video::SColor tile_color)
424 {
425         video::SColor color = blendLight(frame, vertex_pos, tile_color);
426         if (!frame.light_source)
427                         applyFacesShading(color, vertex_normal);
428         return color;
429 }
430
431 static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
432                 MeshMakeData *data, MapNode n, int v, int *neighbors)
433 {
434         MapNode n2 = data->m_vmanip.getNodeNoEx(p);
435         if (nodedef->nodeboxConnects(n, n2, v))
436                 *neighbors |= v;
437 }
438
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)
443 {
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;
450         box.MinEdge += pos;
451         box.MaxEdge += pos;
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;
458         f32 txc[24] = {
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
465         };
466         if (data->m_smooth_lighting) {
467                 u16 lights[8];
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));
473                 }
474                 makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source);
475         } else {
476                 makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source);
477         }
478 }
479
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)
484 {
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;
491         box.MinEdge += pos;
492         box.MaxEdge += pos;
493         if (data->m_smooth_lighting) {
494                 u16 lights[8];
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));
500                 }
501                 makeSmoothLightedCuboid(collector, box, &tile, 1, lights, txc, frame.light_source);
502         } else {
503                 makeCuboid(collector, box, &tile, 1, color, txc, frame.light_source);
504         }
505 }
506
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)
512 {
513         return 9 * pos.X + 3 * pos.Y + pos.Z + 13;
514 }
515
516 /*!
517  * Returns the i-th special tile for a map node.
518  */
519 static TileSpec getSpecialTile(const ContentFeatures &f,
520         const MapNode &n, u8 i)
521 {
522         TileSpec copy = f.special_tiles[i];
523         if (!copy.has_color)
524                 n.getColor(f, &copy.color);
525         return copy;
526 }
527
528 /*
529         TODO: Fix alpha blending for special nodes
530         Currently only the last element rendered is blended correct
531 */
532 void mapblock_mesh_generate_special(MeshMakeData *data,
533                 MeshCollector &collector)
534 {
535         INodeDefManager *nodedef = data->m_client->ndef();
536         scene::ISceneManager* smgr = data->m_client->getSceneManager();
537         scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
538
539         // 0ms
540         //TimeTaker timer("mapblock_mesh_generate_special()");
541
542         /*
543                 Some settings
544         */
545         bool enable_mesh_cache  = g_settings->getBool("enable_mesh_cache") &&
546                 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
547
548         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
549
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++)
553         {
554                 v3s16 p(x,y,z);
555
556                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
557                 const ContentFeatures &f = nodedef->get(n);
558
559                 // Only solidness=0 stuff is drawn here
560                 if(f.solidness != 0)
561                         continue;
562
563                 if (f.drawtype == NDT_AIRLIKE)
564                         continue;
565
566                 LightFrame frame;
567                 if (data->m_smooth_lighting)
568                         getSmoothLightFrame(&frame, blockpos_nodes + p, data, f.light_source);
569                 else
570                         frame.light_source = f.light_source;
571
572                 switch(f.drawtype) {
573                 default:
574                         infostream << "Got " << f.drawtype << std::endl;
575                         FATAL_ERROR("Unknown drawtype");
576                         break;
577                 case NDT_LIQUID:
578                 {
579                         /*
580                                 Add water sources to mesh if using new style
581                         */
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);
589
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;
596
597                         /*
598                                 Generate sides
599                          */
600                         v3s16 side_dirs[4] = {
601                                 v3s16(1,0,0),
602                                 v3s16(-1,0,0),
603                                 v3s16(0,0,1),
604                                 v3s16(0,0,-1),
605                         };
606                         for(u32 i=0; i<4; i++)
607                         {
608                                 v3s16 dir = side_dirs[i];
609
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();
615
616                                 if(neighbor_content == CONTENT_IGNORE)
617                                         continue;
618
619                                 /*
620                                         If our topside is liquid and neighbor's topside
621                                         is liquid, don't draw side face
622                                 */
623                                 if(top_is_same_liquid && (n_top_c == c_flowing ||
624                                                 n_top_c == c_source || n_top_c == CONTENT_IGNORE))
625                                         continue;
626
627                                 // Don't draw face if neighbor is blocking the view
628                                 if(n_feat.solidness == 2)
629                                         continue;
630
631                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
632                                                 || neighbor_content == c_flowing);
633
634                                 // Don't draw any faces if neighbor same is liquid and top is
635                                 // same liquid
636                                 if(neighbor_is_same_liquid && !top_is_same_liquid)
637                                         continue;
638
639                                 // Use backface culled material if neighbor doesn't have a
640                                 // solidness of 0
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;
645                                         c = &c2;
646                                 }
647
648                                 video::S3DVertex vertices[4] =
649                                 {
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),
654                                 };
655
656                                 /*
657                                         If our topside is liquid, set upper border of face
658                                         at upper border of node
659                                 */
660                                 if (top_is_same_liquid) {
661                                         vertices[2].Pos.Y = 0.5 * BS;
662                                         vertices[3].Pos.Y = 0.5 * BS;
663                                 } else {
664                                 /*
665                                         Otherwise upper position of face is liquid level
666                                 */
667                                         vertices[2].Pos.Y = 0.5 * BS;
668                                         vertices[3].Pos.Y = 0.5 * BS;
669                                 }
670                                 /*
671                                         If neighbor is liquid, lower border of face is liquid level
672                                 */
673                                 if (neighbor_is_same_liquid) {
674                                         vertices[0].Pos.Y = 0.5 * BS;
675                                         vertices[1].Pos.Y = 0.5 * BS;
676                                 } else {
677                                 /*
678                                         If neighbor is not liquid, lower border of face is
679                                         lower border of node
680                                 */
681                                         vertices[0].Pos.Y = -0.5 * BS;
682                                         vertices[1].Pos.Y = -0.5 * BS;
683                                 }
684
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);
694
695                                         // Do this to not cause glitches when two liquids are
696                                         // side-by-side
697                                         /*if(neighbor_is_same_liquid == false){
698                                                 vertices[j].Pos.X *= 0.98;
699                                                 vertices[j].Pos.Z *= 0.98;
700                                         }*/
701
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);
705                                 }
706
707                                 u16 indices[] = {0,1,2,2,3,0};
708                                 // Add to mesh collector
709                                 collector.append(*current_tile, vertices, 4, indices, 6);
710                         }
711
712                         /*
713                                 Generate top
714                          */
715                         if(top_is_same_liquid)
716                                 continue;
717
718                         video::S3DVertex vertices[4] =
719                         {
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),
724                         };
725
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);
731                         }
732
733                         u16 indices[] = {0,1,2,2,3,0};
734                         // Add to mesh collector
735                         collector.append(tile_liquid, vertices, 4, indices, 6);
736                 break;}
737                 case NDT_FLOWINGLIQUID:
738                 {
739                         /*
740                                 Add flowing liquid to mesh
741                         */
742                         TileSpec tile_liquid = getSpecialTile(f, n, 0);
743                         TileSpec tile_liquid_bfculled = getSpecialTile(f, n, 1);
744
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;
751
752                         u16 l = 0;
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);
758                                 l = l | (l<<8);
759                         }
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)
764                         else
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);
770
771                         u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
772
773                         // Neighbor liquid levels (key = relative position)
774                         // Includes current node
775
776                         struct NeighborData {
777                                 f32 level;
778                                 content_t content;
779                                 u8 flags;
780                         };
781                         NeighborData neighbor_data_matrix[27];
782
783                         const u8 neighborflag_top_is_same_liquid = 0x01;
784                         v3s16 neighbor_dirs[9] = {
785                                 v3s16(0,0,0),
786                                 v3s16(0,0,1),
787                                 v3s16(0,0,-1),
788                                 v3s16(1,0,0),
789                                 v3s16(-1,0,0),
790                                 v3s16(1,0,1),
791                                 v3s16(-1,0,-1),
792                                 v3s16(1,0,-1),
793                                 v3s16(-1,0,1),
794                         };
795                         for(u32 i=0; i<9; i++)
796                         {
797                                 content_t content = CONTENT_AIR;
798                                 float level = -0.5 * BS;
799                                 u8 flags = 0;
800                                 // Check neighbor
801                                 v3s16 p2 = p + neighbor_dirs[i];
802                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
803                                 if(n2.getContent() != CONTENT_IGNORE)
804                                 {
805                                         content = n2.getContent();
806
807                                         if(n2.getContent() == c_source)
808                                                 level = 0.5 * BS;
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)
812                                                         liquid_level = 0;
813                                                 else
814                                                         liquid_level -= (LIQUID_LEVEL_MAX+1-range);
815                                                 level = (-0.5 + ((float)liquid_level + 0.5) / (float)range) * BS;
816                                         }
817
818                                         // Check node above neighbor.
819                                         // NOTE: This doesn't get executed if neighbor
820                                         //       doesn't exist
821                                         p2.Y += 1;
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;
826                                 }
827
828                                 NeighborData &neighbor_data =
829                                         neighbor_data_matrix[NeighborToIndex(neighbor_dirs[i])];
830
831                                 neighbor_data.level = level;
832                                 neighbor_data.content = content;
833                                 neighbor_data.flags = flags;
834                         }
835
836                         // Corner heights (average between four liquids)
837                         f32 corner_levels[4];
838
839                         v3s16 halfdirs[4] = {
840                                 v3s16(0,0,0),
841                                 v3s16(1,0,0),
842                                 v3s16(1,0,1),
843                                 v3s16(0,0,1),
844                         };
845                         for(u32 i=0; i<4; i++)
846                         {
847                                 v3s16 cornerdir = halfdirs[i];
848                                 float cornerlevel = 0;
849                                 u32 valid_count = 0;
850                                 u32 air_count = 0;
851                                 for(u32 j=0; j<4; j++)
852                                 {
853                                         v3s16 neighbordir = cornerdir - halfdirs[j];
854
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)
860                                         {
861                                                 cornerlevel = 0.5*BS;
862                                                 valid_count = 1;
863                                                 break;
864                                         }
865                                         // Source is always the same height
866                                         else if(content == c_source)
867                                         {
868                                                 cornerlevel = 0.5 * BS;
869                                                 valid_count = 1;
870                                                 break;
871                                         }
872                                         // Flowing liquid has level information
873                                         else if(content == c_flowing)
874                                         {
875                                                 cornerlevel += neighbor_data.level;
876                                                 valid_count++;
877                                         }
878                                         else if(content == CONTENT_AIR)
879                                         {
880                                                 air_count++;
881                                         }
882                                 }
883                                 if(air_count >= 2)
884                                         cornerlevel = -0.5*BS+0.2;
885                                 else if(valid_count > 0)
886                                         cornerlevel /= valid_count;
887                                 corner_levels[i] = cornerlevel;
888                         }
889
890                         /*
891                                 Generate sides
892                         */
893
894                         v3s16 side_dirs[4] = {
895                                 v3s16(1,0,0),
896                                 v3s16(-1,0,0),
897                                 v3s16(0,0,1),
898                                 v3s16(0,0,-1),
899                         };
900                         s16 side_corners[4][2] = {
901                                 {1, 2},
902                                 {3, 0},
903                                 {2, 3},
904                                 {0, 1},
905                         };
906                         for(u32 i=0; i<4; i++)
907                         {
908                                 v3s16 dir = side_dirs[i];
909
910                                 NeighborData& neighbor_data =
911                                         neighbor_data_matrix[NeighborToIndex(dir)];
912                                 /*
913                                         If our topside is liquid and neighbor's topside
914                                         is liquid, don't draw side face
915                                 */
916                                 if (top_is_same_liquid &&
917                                                 (neighbor_data.flags & neighborflag_top_is_same_liquid))
918                                         continue;
919
920                                 content_t neighbor_content = neighbor_data.content;
921                                 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
922
923                                 // Don't draw face if neighbor is blocking the view
924                                 if(n_feat.solidness == 2)
925                                         continue;
926
927                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
928                                                 || neighbor_content == c_flowing);
929
930                                 // Don't draw any faces if neighbor same is liquid and top is
931                                 // same liquid
932                                 if(neighbor_is_same_liquid == true
933                                                 && top_is_same_liquid == false)
934                                         continue;
935
936                                 // Use backface culled material if neighbor doesn't have a
937                                 // solidness of 0
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;
942                                         c = &c2;
943                                 }
944
945                                 video::S3DVertex vertices[4] =
946                                 {
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),
951                                 };
952
953                                 /*
954                                         If our topside is liquid, set upper border of face
955                                         at upper border of node
956                                 */
957                                 if(top_is_same_liquid)
958                                 {
959                                         vertices[2].Pos.Y = 0.5*BS;
960                                         vertices[3].Pos.Y = 0.5*BS;
961                                 }
962                                 /*
963                                         Otherwise upper position of face is corner levels
964                                 */
965                                 else
966                                 {
967                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
968                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
969                                 }
970
971                                 /*
972                                         If neighbor is liquid, lower border of face is corner
973                                         liquid levels
974                                 */
975                                 if(neighbor_is_same_liquid)
976                                 {
977                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
978                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
979                                 }
980                                 /*
981                                         If neighbor is not liquid, lower border of face is
982                                         lower border of node
983                                 */
984                                 else
985                                 {
986                                         vertices[0].Pos.Y = -0.5*BS;
987                                         vertices[1].Pos.Y = -0.5*BS;
988                                 }
989
990                                 for(s32 j=0; j<4; j++)
991                                 {
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);
1000
1001                                         // Do this to not cause glitches when two liquids are
1002                                         // side-by-side
1003                                         /*if(neighbor_is_same_liquid == false){
1004                                                 vertices[j].Pos.X *= 0.98;
1005                                                 vertices[j].Pos.Z *= 0.98;
1006                                         }*/
1007
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);
1011                                 }
1012
1013                                 u16 indices[] = {0,1,2,2,3,0};
1014                                 // Add to mesh collector
1015                                 collector.append(*current_tile, vertices, 4, indices, 6);
1016                         }
1017
1018                         /*
1019                                 Generate top side, if appropriate
1020                         */
1021
1022                         if(top_is_same_liquid == false)
1023                         {
1024                                 video::S3DVertex vertices[4] =
1025                                 {
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),
1030                                 };
1031
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};
1036
1037                                 for(s32 i=0; i<4; i++)
1038                                 {
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);
1046                                 }
1047
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);
1069
1070                                 for(s32 i=0; i<4; i++)
1071                                 {
1072                                         vertices[i].TCoords.rotateBy(
1073                                                         tcoord_angle,
1074                                                         tcoord_center);
1075                                         vertices[i].TCoords += tcoord_translate;
1076                                 }
1077
1078                                 v2f t = vertices[0].TCoords;
1079                                 vertices[0].TCoords = vertices[2].TCoords;
1080                                 vertices[2].TCoords = t;
1081
1082                                 u16 indices[] = {0,1,2,2,3,0};
1083                                 // Add to mesh collector
1084                                 collector.append(tile_liquid, vertices, 4, indices, 6);
1085                         }
1086                 break;}
1087                 case NDT_GLASSLIKE:
1088                 {
1089                         TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1090
1091                         u16 l = getInteriorLight(n, 1, nodedef);
1092                         video::SColor c = encode_light_and_color(l, tile.color,
1093                                 f.light_source);
1094                         for(u32 j=0; j<6; j++)
1095                         {
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())
1102                                         continue;
1103                                 video::SColor c2=c;
1104                                 if(!f.light_source)
1105                                         applyFacesShading(c2, v3f(dir.X, dir.Y, dir.Z));
1106
1107
1108                                 // The face at 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),
1114                                 };
1115
1116                                 // Rotations in the g_6dirs format
1117                                 if(j == 0) // Z+
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);
1135
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);
1140                                 }
1141
1142                                 u16 indices[] = {0,1,2,2,3,0};
1143                                 // Add to mesh collector
1144                                 collector.append(tile, vertices, 4, indices, 6);
1145                         }
1146                 break;}
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");
1150                         break;
1151                 case NDT_GLASSLIKE_FRAMED:
1152                 {
1153                         static const v3s16 dirs[6] = {
1154                                 v3s16( 0, 1, 0),
1155                                 v3s16( 0,-1, 0),
1156                                 v3s16( 1, 0, 0),
1157                                 v3s16(-1, 0, 0),
1158                                 v3s16( 0, 0, 1),
1159                                 v3s16( 0, 0,-1)
1160                         };
1161
1162                         u16 l = getInteriorLight(n, 1, nodedef);
1163                         u8 i;
1164                         TileSpec tiles[6];
1165                         for (i = 0; i < 6; i++)
1166                                 tiles[i] = getNodeTile(n, p, dirs[i], data);
1167
1168                         video::SColor tile0color = encode_light_and_color(l,
1169                                 tiles[0].color, f.light_source);
1170
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];
1180                         } else {
1181                                 for (i = 0; i < 6; i++)
1182                                         glass_tiles[i] = tiles[1];
1183                         }
1184                         for (i = 0; i < 6; i++)
1185                                 glasscolor[i] = encode_light_and_color(l, glass_tiles[i].color,
1186                                         f.light_source);
1187
1188                         u8 param2 = n.getParam2();
1189                         bool H_merge = ! bool(param2 & 128);
1190                         bool V_merge = ! bool(param2 & 64);
1191                         param2  = param2 & 63;
1192
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 );
1197
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-
1211                         };
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-
1219                         };
1220
1221                         // table of node visible faces, 0 = invisible
1222                         int visible_faces[6] = {0,0,0,0,0,0};
1223
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};
1226
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};
1229
1230                         content_t current = n.getContent();
1231                         content_t n2c;
1232                         MapNode n2;
1233                         v3s16 n2p;
1234
1235                         // neighbours checks for frames visibility
1236
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)
1242                                         nb[1] = 1;
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)
1247                                         nb[4] = 1;
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;
1255                                 }
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)
1262                                                 nb[i] = 1;
1263                                 }
1264                         }
1265
1266                         // faces visibility checks
1267
1268                         if (!V_merge) {
1269                                 visible_faces[0] = 1;
1270                                 visible_faces[1] = 1;
1271                         } else {
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();
1276                                         if (n2c != current)
1277                                                 visible_faces[i] = 1;
1278                                 }
1279                         }
1280
1281                         if (!H_merge) {
1282                                 visible_faces[2] = 1;
1283                                 visible_faces[3] = 1;
1284                                 visible_faces[4] = 1;
1285                                 visible_faces[5] = 1;
1286                         } else {
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();
1291                                         if (n2c != current)
1292                                                 visible_faces[i] = 1;
1293                                 }
1294                         }
1295
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
1300                         };
1301
1302                         aabb3f box;
1303
1304                         for(i = 0; i < 12; i++)
1305                         {
1306                                 int edge_invisible;
1307                                 if (nb[nb_triplet[i*3+2]])
1308                                         edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]];
1309                                 else
1310                                         edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]];
1311                                 if (edge_invisible)
1312                                         continue;
1313                                 box = frame_edges[i];
1314                                 makeAutoLightedCuboid(&collector, data, pos, box, tiles[0], tile0color, frame);
1315                         }
1316
1317                         for(i = 0; i < 6; i++)
1318                         {
1319                                 if (!visible_faces[i])
1320                                         continue;
1321                                 box = glass_faces[i];
1322                                 makeAutoLightedCuboid(&collector, data, pos, box, glass_tiles[i], glasscolor[i], frame);
1323                         }
1324
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);
1340                         }
1341                 break;}
1342                 case NDT_ALLFACES:
1343                 {
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);
1349
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);
1353                 break;}
1354                 case NDT_ALLFACES_OPTIONAL:
1355                         // This is always pre-converted to something else
1356                         FATAL_ERROR("NDT_ALLFACES_OPTIONAL not pre-converted");
1357                         break;
1358                 case NDT_TORCHLIKE:
1359                 {
1360                         v3s16 dir = n.getWallMountedDir(nodedef);
1361
1362                         u8 tileindex = 0;
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
1370                         } else {
1371                                 tileindex = 2; // side
1372                         }
1373
1374                         TileSpec tile = getNodeTileN(n, p, tileindex, data);
1375                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1376                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1377
1378                         u16 l = getInteriorLight(n, 1, nodedef);
1379                         video::SColor c = encode_light_and_color(l, tile.color,
1380                                 f.light_source);
1381
1382                         float s = BS/2*f.visual_scale;
1383                         // Wall at X+ of node
1384                         video::S3DVertex vertices[4] =
1385                         {
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),
1390                         };
1391
1392                         for (s32 i = 0; i < 4; i++)
1393                         {
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);
1406
1407                                 if (data->m_smooth_lighting)
1408                                         vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1409                                 vertices[i].Pos += intToFloat(p, BS);
1410                         }
1411
1412                         u16 indices[] = {0,1,2,2,3,0};
1413                         // Add to mesh collector
1414                         collector.append(tile, vertices, 4, indices, 6);
1415                 break;}
1416                 case NDT_SIGNLIKE:
1417                 {
1418                         TileSpec tile = getNodeTileN(n, p, 0, data);
1419                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1420                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1421
1422                         u16 l = getInteriorLight(n, 0, nodedef);
1423                         video::SColor c = encode_light_and_color(l, tile.color,
1424                                 f.light_source);
1425
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] =
1430                         {
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),
1435                         };
1436
1437                         v3s16 dir = n.getWallMountedDir(nodedef);
1438
1439                         for (s32 i = 0; i < 4; i++)
1440                         {
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);
1453
1454                                 if (data->m_smooth_lighting)
1455                                         vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1456                                 vertices[i].Pos += intToFloat(p, BS);
1457                         }
1458
1459                         u16 indices[] = {0,1,2,2,3,0};
1460                         // Add to mesh collector
1461                         collector.append(tile, vertices, 4, indices, 6);
1462                 break;}
1463                 case NDT_PLANTLIKE:
1464                 {
1465                         PseudoRandom rng(x<<8 | z | y<<16);
1466
1467                         TileSpec tile = getNodeTileN(n, p, 0, data);
1468                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1469
1470                         u16 l = getInteriorLight(n, 1, nodedef);
1471                         video::SColor c = encode_light_and_color(l, tile.color,
1472                                 f.light_source);
1473
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))
1477                                 s *= 1.41421;
1478
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);
1484                         }
1485
1486                         for (int j = 0; j < 4; j++) {
1487                                 video::S3DVertex vertices[4] =
1488                                 {
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),
1493                                 };
1494
1495                                 float rotate_degree = 0;
1496                                 u8 p2mesh = 0;
1497                                 if (f.param_type_2 == CPT2_DEGROTATE)
1498                                         rotate_degree = n.param2 * 2;
1499                                 if (f.param_type_2 != CPT2_MESHOPTIONS) {
1500                                         if (j == 0) {
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);
1506                                         }
1507                                 } else {
1508                                         p2mesh = n.param2 & 0x7;
1509                                         switch (p2mesh) {
1510                                         case 0:
1511                                                 // x
1512                                                 if (j == 0) {
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);
1518                                                 }
1519                                                 break;
1520                                         case 1:
1521                                                 // +
1522                                                 if (j == 0) {
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);
1528                                                 }
1529                                                 break;
1530                                         case 2:
1531                                                 // *
1532                                                 if (j == 0) {
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);
1541                                                 }
1542                                                 break;
1543                                         case 3:
1544                                                 // #
1545                                                 switch (j) {
1546                                                 case 0:
1547                                                         for (u16 i = 0; i < 4; i++) {
1548                                                                 vertices[i].Pos.rotateXZBy(1);
1549                                                                 vertices[i].Pos.Z += BS / 4;
1550                                                         }
1551                                                         break;
1552                                                 case 1:
1553                                                         for (u16 i = 0; i < 4; i++) {
1554                                                                 vertices[i].Pos.rotateXZBy(91);
1555                                                                 vertices[i].Pos.X += BS / 4;
1556                                                         }
1557                                                         break;
1558                                                 case 2:
1559                                                         for (u16 i = 0; i < 4; i++) {
1560                                                                 vertices[i].Pos.rotateXZBy(181);
1561                                                                 vertices[i].Pos.Z -= BS / 4;
1562                                                         }
1563                                                         break;
1564                                                 case 3:
1565                                                         for (u16 i = 0; i < 4; i++) {
1566                                                                 vertices[i].Pos.rotateXZBy(271);
1567                                                                 vertices[i].Pos.X -= BS / 4;
1568                                                         }
1569                                                         break;
1570                                                 }
1571                                                 break;
1572                                         case 4:
1573                                                 // outward leaning #-like
1574                                                 switch (j) {
1575                                                 case 0:
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);
1580                                                         break;
1581                                                 case 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);
1586                                                         break;
1587                                                 case 2:
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);
1592                                                         break;
1593                                                 case 3:
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);
1598                                                         break;
1599                                                 }
1600                                                 break;
1601                                         }
1602                                 }
1603
1604                                 for (int i = 0; i < 4; i++) {
1605                                         vertices[i].Pos *= f.visual_scale;
1606                                         vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1);
1607                                         if (data->m_smooth_lighting)
1608                                                 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1609                                         vertices[i].Pos += intToFloat(p, BS);
1610                                         // move to a random spot to avoid moire
1611                                         if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) {
1612                                                 vertices[i].Pos.X += random_offset_X;
1613                                                 vertices[i].Pos.Z += random_offset_Z;
1614                                         }
1615                                         // randomly move each face up/down
1616                                         if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x20) != 0)) {
1617                                                 PseudoRandom yrng(j | x<<16 | z<<8 | y<<24 );
1618                                                 vertices[i].Pos.Y -= BS * ((yrng.next() % 16 / 16.0) * 0.125);
1619                                         }
1620                                 }
1621
1622                                 u16 indices[] = {0, 1, 2, 2, 3, 0};
1623                                 // Add to mesh collector
1624                                 collector.append(tile, vertices, 4, indices, 6);
1625
1626                                 // stop adding faces for meshes with less than 4 faces
1627                                 if (f.param_type_2 == CPT2_MESHOPTIONS) {
1628                                         if (((p2mesh == 0) || (p2mesh == 1)) && (j == 1))
1629                                                 break;
1630                                         else if ((p2mesh == 2) && (j == 2))
1631                                                 break;
1632                                 } else if (j == 1) {
1633                                         break;
1634                                 }
1635
1636                         }
1637                 break;}
1638                 case NDT_FIRELIKE:
1639                 {
1640                         TileSpec tile = getNodeTileN(n, p, 0, data);
1641                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1642
1643                         u16 l = getInteriorLight(n, 1, nodedef);
1644                         video::SColor c = encode_light_and_color(l, tile.color,
1645                                 f.light_source);
1646
1647                         float s = BS / 2 * f.visual_scale;
1648
1649                         content_t current = n.getContent();
1650                         content_t n2c;
1651                         MapNode n2;
1652                         v3s16 n2p;
1653
1654                         static const v3s16 dirs[6] = {
1655                                 v3s16( 0,  1,  0),
1656                                 v3s16( 0, -1,  0),
1657                                 v3s16( 1,  0,  0),
1658                                 v3s16(-1,  0,  0),
1659                                 v3s16( 0,  0,  1),
1660                                 v3s16( 0,  0, -1)
1661                         };
1662
1663                         int doDraw[6] = {0, 0, 0, 0, 0, 0};
1664
1665                         bool drawAllFaces = true;
1666
1667                         // Check for adjacent nodes
1668                         for (int i = 0; i < 6; i++) {
1669                                 n2p = blockpos_nodes + p + dirs[i];
1670                                 n2 = data->m_vmanip.getNodeNoEx(n2p);
1671                                 n2c = n2.getContent();
1672                                 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1673                                         doDraw[i] = 1;
1674                                         if (drawAllFaces)
1675                                                 drawAllFaces = false;
1676
1677                                 }
1678                         }
1679
1680                         for (int j = 0; j < 6; j++) {
1681
1682                                 video::S3DVertex vertices[4] = {
1683                                         video::S3DVertex(-s, -BS / 2,         0, 0, 0, 0, c, 0, 1),
1684                                         video::S3DVertex( s, -BS / 2,         0, 0, 0, 0, c, 1, 1),
1685                                         video::S3DVertex( s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 1, 0),
1686                                         video::S3DVertex(-s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 0, 0),
1687                                 };
1688
1689                                 // Calculate which faces should be drawn, (top or sides)
1690                                 if (j == 0 && (drawAllFaces ||
1691                                                 (doDraw[3] == 1 || doDraw[1] == 1))) {
1692                                         for (int i = 0; i < 4; i++) {
1693                                                 vertices[i].Pos.rotateXZBy(90);
1694                                                 vertices[i].Pos.rotateXYBy(-10);
1695                                                 vertices[i].Pos.X -= 4.0;
1696                                         }
1697                                 } else if (j == 1 && (drawAllFaces ||
1698                                                 (doDraw[5] == 1 || doDraw[1] == 1))) {
1699                                         for (int i = 0; i < 4; i++) {
1700                                                 vertices[i].Pos.rotateXZBy(180);
1701                                                 vertices[i].Pos.rotateYZBy(10);
1702                                                 vertices[i].Pos.Z -= 4.0;
1703                                         }
1704                                 } else if (j == 2 && (drawAllFaces ||
1705                                                 (doDraw[2] == 1 || doDraw[1] == 1))) {
1706                                         for (int i = 0; i < 4; i++) {
1707                                                 vertices[i].Pos.rotateXZBy(270);
1708                                                 vertices[i].Pos.rotateXYBy(10);
1709                                                 vertices[i].Pos.X += 4.0;
1710                                         }
1711                                 } else if (j == 3 && (drawAllFaces ||
1712                                                 (doDraw[4] == 1 || doDraw[1] == 1))) {
1713                                         for (int i = 0; i < 4; i++) {
1714                                                 vertices[i].Pos.rotateYZBy(-10);
1715                                                 vertices[i].Pos.Z += 4.0;
1716                                         }
1717                                 // Center cross-flames
1718                                 } else if (j == 4 && (drawAllFaces || doDraw[1] == 1)) {
1719                                         for (int i = 0; i < 4; i++) {
1720                                                 vertices[i].Pos.rotateXZBy(45);
1721                                         }
1722                                 } else if (j == 5 && (drawAllFaces || doDraw[1] == 1)) {
1723                                         for (int i = 0; i < 4; i++) {
1724                                                 vertices[i].Pos.rotateXZBy(-45);
1725                                         }
1726                                 // Render flames on bottom of node above
1727                                 } else if (j == 0 && doDraw[0] == 1 && doDraw[1] == 0) {
1728                                         for (int i = 0; i < 4; i++) {
1729                                                 vertices[i].Pos.rotateYZBy(70);
1730                                                 vertices[i].Pos.rotateXZBy(90);
1731                                                 vertices[i].Pos.Y += 4.84;
1732                                                 vertices[i].Pos.X -= 4.7;
1733                                         }
1734                                 } else if (j == 1 && doDraw[0] == 1 && doDraw[1] == 0) {
1735                                         for (int i = 0; i < 4; i++) {
1736                                                 vertices[i].Pos.rotateYZBy(70);
1737                                                 vertices[i].Pos.rotateXZBy(180);
1738                                                 vertices[i].Pos.Y += 4.84;
1739                                                 vertices[i].Pos.Z -= 4.7;
1740                                         }
1741                                 } else if (j == 2 && doDraw[0] == 1 && doDraw[1] == 0) {
1742                                         for (int i = 0; i < 4; i++) {
1743                                                 vertices[i].Pos.rotateYZBy(70);
1744                                                 vertices[i].Pos.rotateXZBy(270);
1745                                                 vertices[i].Pos.Y += 4.84;
1746                                                 vertices[i].Pos.X += 4.7;
1747                                         }
1748                                 } else if (j == 3 && doDraw[0] == 1 && doDraw[1] == 0) {
1749                                         for (int i = 0; i < 4; i++) {
1750                                                 vertices[i].Pos.rotateYZBy(70);
1751                                                 vertices[i].Pos.Y += 4.84;
1752                                                 vertices[i].Pos.Z += 4.7;
1753                                         }
1754                                 } else {
1755                                         // Skip faces that aren't adjacent to a node
1756                                         continue;
1757                                 }
1758
1759                                 for (int i = 0; i < 4; i++) {
1760                                         vertices[i].Pos *= f.visual_scale;
1761                                         if (data->m_smooth_lighting)
1762                                                 vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1763                                         vertices[i].Pos += intToFloat(p, BS);
1764                                 }
1765
1766                                 u16 indices[] = {0, 1, 2, 2, 3, 0};
1767                                 // Add to mesh collector
1768                                 collector.append(tile, vertices, 4, indices, 6);
1769                         }
1770                 break;}
1771                 case NDT_FENCELIKE:
1772                 {
1773                         TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1774                         TileSpec tile_nocrack = tile;
1775                         tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1776
1777                         // Put wood the right way around in the posts
1778                         TileSpec tile_rot = tile;
1779                         tile_rot.rotation = 1;
1780
1781                         u16 l = getInteriorLight(n, 1, nodedef);
1782                         video::SColor c = encode_light_and_color(l, tile.color,
1783                                 f.light_source);
1784
1785                         const f32 post_rad=(f32)BS/8;
1786                         const f32 bar_rad=(f32)BS/16;
1787                         const f32 bar_len=(f32)(BS/2)-post_rad;
1788
1789                         v3f pos = intToFloat(p, BS);
1790
1791                         // The post - always present
1792                         aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
1793                         f32 postuv[24]={
1794                                         6/16.,6/16.,10/16.,10/16.,
1795                                         6/16.,6/16.,10/16.,10/16.,
1796                                         0/16.,0,4/16.,1,
1797                                         4/16.,0,8/16.,1,
1798                                         8/16.,0,12/16.,1,
1799                                         12/16.,0,16/16.,1};
1800                         makeAutoLightedCuboidEx(&collector, data, pos, post, tile_rot, postuv, c, frame);
1801
1802                         // Now a section of fence, +X, if there's a post there
1803                         v3s16 p2 = p;
1804                         p2.X++;
1805                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1806                         const ContentFeatures *f2 = &nodedef->get(n2);
1807                         if(f2->drawtype == NDT_FENCELIKE)
1808                         {
1809                                 aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
1810                                                 bar_len+BS/2,bar_rad+BS/4,bar_rad);
1811                                 f32 xrailuv[24]={
1812                                         0/16.,2/16.,16/16.,4/16.,
1813                                         0/16.,4/16.,16/16.,6/16.,
1814                                         6/16.,6/16.,8/16.,8/16.,
1815                                         10/16.,10/16.,12/16.,12/16.,
1816                                         0/16.,8/16.,16/16.,10/16.,
1817                                         0/16.,14/16.,16/16.,16/16.};
1818                                 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame);
1819                                 bar.MinEdge.Y -= BS/2;
1820                                 bar.MaxEdge.Y -= BS/2;
1821                                 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, xrailuv, c, frame);
1822                         }
1823
1824                         // Now a section of fence, +Z, if there's a post there
1825                         p2 = p;
1826                         p2.Z++;
1827                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1828                         f2 = &nodedef->get(n2);
1829                         if(f2->drawtype == NDT_FENCELIKE)
1830                         {
1831                                 aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
1832                                                 bar_rad,bar_rad+BS/4,bar_len+BS/2);
1833                                 f32 zrailuv[24]={
1834                                         3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
1835                                         4/16.,1/16.,6/16.,5/16., // for wood texture instead
1836                                         0/16.,9/16.,16/16.,11/16.,
1837                                         0/16.,6/16.,16/16.,8/16.,
1838                                         6/16.,6/16.,8/16.,8/16.,
1839                                         10/16.,10/16.,12/16.,12/16.};
1840                                 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame);
1841                                 bar.MinEdge.Y -= BS/2;
1842                                 bar.MaxEdge.Y -= BS/2;
1843                                 makeAutoLightedCuboidEx(&collector, data, pos, bar, tile_nocrack, zrailuv, c, frame);
1844                         }
1845                 break;}
1846                 case NDT_RAILLIKE:
1847                 {
1848                         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                         bool is_rail_z[6];
1850
1851                         content_t thiscontent = n.getContent();
1852                         std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
1853                         int self_group = ((ItemGroupList) nodedef->get(n).groups)[groupname];
1854
1855                         u8 index = 0;
1856                         for (s8 y0 = -1; y0 <= 1; y0++) {
1857                                 // Prevent from indexing never used coordinates
1858                                 for (s8 xz = -1; xz <= 1; xz++) {
1859                                         if (xz == 0)
1860                                                 continue;
1861                                         MapNode n_xy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x + xz, y + y0, z));
1862                                         MapNode n_zy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y + y0, z + xz));
1863                                         const ContentFeatures &def_xy = nodedef->get(n_xy);
1864                                         const ContentFeatures &def_zy = nodedef->get(n_zy);
1865
1866                                         // Check if current node would connect with the rail
1867                                         is_rail_x[index] = ((def_xy.drawtype == NDT_RAILLIKE
1868                                                         && ((ItemGroupList) def_xy.groups)[groupname] == self_group)
1869                                                         || n_xy.getContent() == thiscontent);
1870
1871                                         is_rail_z[index] = ((def_zy.drawtype == NDT_RAILLIKE
1872                                                         && ((ItemGroupList) def_zy.groups)[groupname] == self_group)
1873                                                         || n_zy.getContent() == thiscontent);
1874                                         index++;
1875                                 }
1876                         }
1877
1878                         bool is_rail_x_all[2]; // [0] = negative x, [1] = positive x coordinate from the current node position
1879                         bool is_rail_z_all[2];
1880                         is_rail_x_all[0] = is_rail_x[0] || is_rail_x[2] || is_rail_x[4];
1881                         is_rail_x_all[1] = is_rail_x[1] || is_rail_x[3] || is_rail_x[5];
1882                         is_rail_z_all[0] = is_rail_z[0] || is_rail_z[2] || is_rail_z[4];
1883                         is_rail_z_all[1] = is_rail_z[1] || is_rail_z[3] || is_rail_z[5];
1884
1885                         // reasonable default, flat straight unrotated rail
1886                         bool is_straight = true;
1887                         int adjacencies = 0;
1888                         int angle = 0;
1889                         u8 tileindex = 0;
1890
1891                         // check for sloped rail
1892                         if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5]) {
1893                                 adjacencies = 5; // 5 means sloped
1894                                 is_straight = true; // sloped is always straight
1895                         } else {
1896                                 // is really straight, rails on both sides
1897                                 is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
1898                                 adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
1899                         }
1900
1901                         switch (adjacencies) {
1902                         case 1:
1903                                 if (is_rail_x_all[0] || is_rail_x_all[1])
1904                                         angle = 90;
1905                                 break;
1906                         case 2:
1907                                 if (!is_straight)
1908                                         tileindex = 1; // curved
1909                                 if (is_rail_x_all[0] && is_rail_x_all[1])
1910                                         angle = 90;
1911                                 if (is_rail_z_all[0] && is_rail_z_all[1]) {
1912                                         if (is_rail_z[4])
1913                                                 angle = 180;
1914                                 }
1915                                 else if (is_rail_x_all[0] && is_rail_z_all[0])
1916                                         angle = 270;
1917                                 else if (is_rail_x_all[0] && is_rail_z_all[1])
1918                                         angle = 180;
1919                                 else if (is_rail_x_all[1] && is_rail_z_all[1])
1920                                         angle = 90;
1921                                 break;
1922                         case 3:
1923                                 // here is where the potential to 'switch' a junction is, but not implemented at present
1924                                 tileindex = 2; // t-junction
1925                                 if(!is_rail_x_all[1])
1926                                         angle = 180;
1927                                 if(!is_rail_z_all[0])
1928                                         angle = 90;
1929                                 if(!is_rail_z_all[1])
1930                                         angle = 270;
1931                                 break;
1932                         case 4:
1933                                 tileindex = 3; // crossing
1934                                 break;
1935                         case 5: //sloped
1936                                 if (is_rail_z[4])
1937                                         angle = 180;
1938                                 if (is_rail_x[4])
1939                                         angle = 90;
1940                                 if (is_rail_x[5])
1941                                         angle = -90;
1942                                 break;
1943                         default:
1944                                 break;
1945                         }
1946
1947                         TileSpec tile = getNodeTileN(n, p, tileindex, data);
1948                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1949                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1950
1951                         u16 l = getInteriorLight(n, 0, nodedef);
1952                         video::SColor c = encode_light_and_color(l, tile.color,
1953                                 f.light_source);
1954
1955                         float d = (float)BS/64;
1956                         float s = BS/2;
1957
1958                         short g = -1;
1959                         if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5])
1960                                 g = 1; //Object is at a slope
1961
1962                         video::S3DVertex vertices[4] =
1963                         {
1964                                         video::S3DVertex(-s,  -s+d, -s, 0, 0, 0, c, 0, 1),
1965                                         video::S3DVertex( s,  -s+d, -s, 0, 0, 0, c, 1, 1),
1966                                         video::S3DVertex( s, g*s+d,  s, 0, 0, 0, c, 1, 0),
1967                                         video::S3DVertex(-s, g*s+d,  s, 0, 0, 0, c, 0, 0),
1968                         };
1969
1970                         for(s32 i=0; i<4; i++)
1971                         {
1972                                 if(angle != 0)
1973                                         vertices[i].Pos.rotateXZBy(angle);
1974                                 if (data->m_smooth_lighting)
1975                                         vertices[i].Color = blendLight(frame, vertices[i].Pos, tile.color);
1976                                 vertices[i].Pos += intToFloat(p, BS);
1977                         }
1978
1979                         u16 indices[] = {0,1,2,2,3,0};
1980                         collector.append(tile, vertices, 4, indices, 6);
1981                 break;}
1982                 case NDT_NODEBOX:
1983                 {
1984                         static const v3s16 tile_dirs[6] = {
1985                                 v3s16(0, 1, 0),
1986                                 v3s16(0, -1, 0),
1987                                 v3s16(1, 0, 0),
1988                                 v3s16(-1, 0, 0),
1989                                 v3s16(0, 0, 1),
1990                                 v3s16(0, 0, -1)
1991                         };
1992
1993                         TileSpec tiles[6];
1994                         video::SColor colors[6];
1995                         for (int j = 0; j < 6; j++) {
1996                                 // Handles facedir rotation for textures
1997                                 tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
1998                         }
1999                         if (!data->m_smooth_lighting) {
2000                                 u16 l = getInteriorLight(n, 1, nodedef);
2001                                 for (int j = 0; j < 6; j++)
2002                                         colors[j] = encode_light_and_color(l, tiles[j].color, f.light_source);
2003                         }
2004
2005                         v3f pos = intToFloat(p, BS);
2006
2007                         int neighbors = 0;
2008
2009                         // locate possible neighboring nodes to connect to
2010                         if (f.node_box.type == NODEBOX_CONNECTED) {
2011                                 v3s16 p2 = p;
2012
2013                                 p2.Y++;
2014                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
2015
2016                                 p2 = p;
2017                                 p2.Y--;
2018                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
2019
2020                                 p2 = p;
2021                                 p2.Z--;
2022                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
2023
2024                                 p2 = p;
2025                                 p2.X--;
2026                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
2027
2028                                 p2 = p;
2029                                 p2.Z++;
2030                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
2031
2032                                 p2 = p;
2033                                 p2.X++;
2034                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
2035                         }
2036
2037                         std::vector<aabb3f> boxes;
2038                         n.getNodeBoxes(nodedef, &boxes, neighbors);
2039                         for (std::vector<aabb3f>::iterator
2040                                         i = boxes.begin();
2041                                         i != boxes.end(); ++i) {
2042                                 aabb3f box = *i;
2043
2044                                 f32 dx1 = box.MinEdge.X;
2045                                 f32 dy1 = box.MinEdge.Y;
2046                                 f32 dz1 = box.MinEdge.Z;
2047                                 f32 dx2 = box.MaxEdge.X;
2048                                 f32 dy2 = box.MaxEdge.Y;
2049                                 f32 dz2 = box.MaxEdge.Z;
2050
2051                                 box.MinEdge += pos;
2052                                 box.MaxEdge += pos;
2053
2054                                 if (box.MinEdge.X > box.MaxEdge.X)
2055                                         std::swap(box.MinEdge.X, box.MaxEdge.X);
2056                                 if (box.MinEdge.Y > box.MaxEdge.Y)
2057                                         std::swap(box.MinEdge.Y, box.MaxEdge.Y);
2058                                 if (box.MinEdge.Z > box.MaxEdge.Z)
2059                                         std::swap(box.MinEdge.Z, box.MaxEdge.Z);
2060
2061                                 //
2062                                 // Compute texture coords
2063                                 f32 tx1 = (box.MinEdge.X/BS)+0.5;
2064                                 f32 ty1 = (box.MinEdge.Y/BS)+0.5;
2065                                 f32 tz1 = (box.MinEdge.Z/BS)+0.5;
2066                                 f32 tx2 = (box.MaxEdge.X/BS)+0.5;
2067                                 f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
2068                                 f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
2069                                 f32 txc[24] = {
2070                                         // up
2071                                         tx1, 1-tz2, tx2, 1-tz1,
2072                                         // down
2073                                         tx1, tz1, tx2, tz2,
2074                                         // right
2075                                         tz1, 1-ty2, tz2, 1-ty1,
2076                                         // left
2077                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
2078                                         // back
2079                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
2080                                         // front
2081                                         tx1, 1-ty2, tx2, 1-ty1,
2082                                 };
2083                                 if (data->m_smooth_lighting) {
2084                                         u16 lights[8];
2085                                         for (int j = 0; j < 8; ++j) {
2086                                                 f32 x = (j & 4) ? dx2 : dx1;
2087                                                 f32 y = (j & 2) ? dy2 : dy1;
2088                                                 f32 z = (j & 1) ? dz2 : dz1;
2089                                                 lights[j] = blendLight(frame, core::vector3df(x, y, z));
2090                                         }
2091                                         makeSmoothLightedCuboid(&collector, box, tiles, 6, lights, txc, f.light_source);
2092                                 } else {
2093                                         makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source);
2094                                 }
2095                         }
2096                 break;}
2097                 case NDT_MESH:
2098                 {
2099                         v3f pos = intToFloat(p, BS);
2100                         u16 l = getInteriorLight(n, 1, nodedef);
2101                         u8 facedir = 0;
2102                         if (f.param_type_2 == CPT2_FACEDIR ||
2103                                         f.param_type_2 == CPT2_COLORED_FACEDIR) {
2104                                 facedir = n.getFaceDir(nodedef);
2105                         } else if (f.param_type_2 == CPT2_WALLMOUNTED ||
2106                                         f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
2107                                 //convert wallmounted to 6dfacedir.
2108                                 //when cache enabled, it is already converted
2109                                 facedir = n.getWallMounted(nodedef);
2110                                 if (!enable_mesh_cache) {
2111                                         static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
2112                                         facedir = wm_to_6d[facedir];
2113                                 }
2114                         }
2115
2116                         if (!data->m_smooth_lighting && f.mesh_ptr[facedir]) {
2117                                 // use cached meshes
2118                                 for (u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) {
2119                                         const TileSpec &tile = getNodeTileN(n, p, j, data);
2120                                         scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j);
2121                                         collector.append(tile, (video::S3DVertex *)
2122                                                 buf->getVertices(), buf->getVertexCount(),
2123                                                 buf->getIndices(), buf->getIndexCount(), pos,
2124                                                 encode_light_and_color(l, tile.color, f.light_source),
2125                                                 f.light_source);
2126                                 }
2127                         } else if (f.mesh_ptr[0]) {
2128                                 // no cache, clone and rotate mesh
2129                                 scene::IMesh* mesh = cloneMesh(f.mesh_ptr[0]);
2130                                 rotateMeshBy6dFacedir(mesh, facedir);
2131                                 recalculateBoundingBox(mesh);
2132                                 meshmanip->recalculateNormals(mesh, true, false);
2133                                 for (u16 j = 0; j < mesh->getMeshBufferCount(); j++) {
2134                                         const TileSpec &tile = getNodeTileN(n, p, j, data);
2135                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
2136                                         video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
2137                                         u32 vertex_count = buf->getVertexCount();
2138                                         if (data->m_smooth_lighting) {
2139                                                 for (u16 m = 0; m < vertex_count; ++m) {
2140                                                         video::S3DVertex &vertex = vertices[m];
2141                                                         vertex.Color = blendLight(frame, vertex.Pos, vertex.Normal, tile.color);
2142                                                         vertex.Pos += pos;
2143                                                 }
2144                                                 collector.append(tile, vertices, vertex_count,
2145                                                         buf->getIndices(), buf->getIndexCount());
2146                                         } else {
2147                                                 collector.append(tile, vertices, vertex_count,
2148                                                         buf->getIndices(), buf->getIndexCount(), pos,
2149                                                         encode_light_and_color(l, tile.color, f.light_source),
2150                                                         f.light_source);
2151                                         }
2152                                 }
2153                                 mesh->drop();
2154                         }
2155                 break;}
2156                 }
2157         }
2158 }
2159