1 /*
2 content_mapblock.cpp
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5 
6 /*
7 This file is part of Freeminer.
8 
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Freeminer  is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Freeminer.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "content_mapblock.h"
24 #include "util/numeric.h"
25 #include "util/directiontables.h"
26 #include "main.h" // For g_settings
27 #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
28 #include "map.h"
29 #include "settings.h"
30 #include "nodedef.h"
31 #include "tile.h"
32 #include "mesh.h"
33 #include <IMeshManipulator.h>
34 #include "gamedef.h"
35 #include "log.h"
36 
37 
38 // Create a cuboid.
39 //  collector - the MeshCollector for the resulting polygons
40 //  box       - the position and size of the box
41 //  tiles     - the tiles (materials) to use (for all 6 faces)
42 //  tilecount - number of entries in tiles, 1<=tilecount<=6
43 //  c         - vertex colour - used for all
44 //  txc       - texture coordinates - this is a list of texture coordinates
45 //              for the opposite corners of each face - therefore, there
46 //              should be (2+2)*6=24 values in the list. Alternatively, pass
47 //              NULL to use the entire texture for each face. The order of
48 //              the faces in the list is up-down-right-left-back-front
49 //              (compatible with ContentFeatures). If you specified 0,0,1,1
50 //              for each face, that would be the same as passing NULL.
makeCuboid(MeshCollector * collector,const aabb3f & box,TileSpec * tiles,int tilecount,video::SColor & c,const f32 * txc)51 void makeCuboid(MeshCollector *collector, const aabb3f &box,
52 	TileSpec *tiles, int tilecount,
53 	video::SColor &c, const f32* txc)
54 {
55 	assert(tilecount >= 1 && tilecount <= 6);
56 
57 	v3f min = box.MinEdge;
58 	v3f max = box.MaxEdge;
59 
60 
61 
62 	if(txc == NULL)
63 	{
64 		static const f32 txc_default[24] = {
65 			0,0,1,1,
66 			0,0,1,1,
67 			0,0,1,1,
68 			0,0,1,1,
69 			0,0,1,1,
70 			0,0,1,1
71 		};
72 		txc = txc_default;
73 	}
74 
75 	video::S3DVertex vertices[24] =
76 	{
77 		// up
78 		video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
79 		video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
80 		video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
81 		video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
82 		// down
83 		video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
84 		video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
85 		video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
86 		video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
87 		// right
88 		video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
89 		video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
90 		video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
91 		video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
92 		// left
93 		video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
94 		video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
95 		video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
96 		video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
97 		// back
98 		video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
99 		video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
100 		video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
101 		video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
102 		// front
103 		video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
104 		video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
105 		video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
106 		video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
107 	};
108 
109 	for(int i = 0; i < 6; i++)
110 				{
111 				switch (tiles[MYMIN(i, tilecount-1)].rotation)
112 				{
113 				case 0:
114 					break;
115 				case 1: //R90
116 					for (int x = 0; x < 4; x++)
117 						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
118 					break;
119 				case 2: //R180
120 					for (int x = 0; x < 4; x++)
121 						vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
122 					break;
123 				case 3: //R270
124 					for (int x = 0; x < 4; x++)
125 						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
126 					break;
127 				case 4: //FXR90
128 					for (int x = 0; x < 4; x++){
129 						vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
130 						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
131 					}
132 					break;
133 				case 5: //FXR270
134 					for (int x = 0; x < 4; x++){
135 						vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
136 						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
137 					}
138 					break;
139 				case 6: //FYR90
140 					for (int x = 0; x < 4; x++){
141 						vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
142 						vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
143 					}
144 					break;
145 				case 7: //FYR270
146 					for (int x = 0; x < 4; x++){
147 						vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
148 						vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
149 					}
150 					break;
151 				case 8: //FX
152 					for (int x = 0; x < 4; x++){
153 						vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
154 					}
155 					break;
156 				case 9: //FY
157 					for (int x = 0; x < 4; x++){
158 						vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
159 					}
160 					break;
161 				default:
162 					break;
163 				}
164 			}
165 	u16 indices[] = {0,1,2,2,3,0};
166 	// Add to mesh collector
167 	for(s32 j=0; j<24; j+=4)
168 	{
169 		int tileindex = MYMIN(j/4, tilecount-1);
170 		collector->append(tiles[tileindex],
171 				vertices+j, 4, indices, 6);
172 	}
173 }
174 
175 class neighborRail {
176 	public:
177 		bool is_rail_x_all[2];
178 		bool is_rail_z_all[2];
179 		int adjacencies;
180 		bool is_straight;
181 		bool force_end;
recurseRail(v3s16 p,MeshMakeData * data,MeshCollector & collector,int recurse=0)182 }; neighborRail recurseRail(v3s16 p, MeshMakeData *data, MeshCollector &collector, int recurse = 0)
183 {
184 	INodeDefManager *nodedef = data->m_gamedef->ndef();
185 	v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
186 	s16 x = p.X, y = p.Y, z = p.Z;
187 	MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
188 	const ContentFeatures &f = nodedef->get(n);
189 
190 	bool is_rail_x [] = { false, false };  /* x-1, x+1 */
191 	bool is_rail_z [] = { false, false };  /* z-1, z+1 */
192 
193 	bool is_rail_z_minus_y [] = { false, false };  /* z-1, z+1; y-1 */
194 	bool is_rail_x_minus_y [] = { false, false };  /* x-1, z+1; y-1 */
195 	bool is_rail_z_plus_y [] = { false, false };  /* z-1, z+1; y+1 */
196 	bool is_rail_x_plus_y [] = { false, false };  /* x-1, x+1; y+1 */
197 
198 	MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
199 	MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
200 	MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
201 	MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
202 	MapNode n_plus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y+1, z));
203 	MapNode n_plus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y-1, z));
204 	MapNode n_minus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y+1, z));
205 	MapNode n_minus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y-1, z));
206 	MapNode n_plus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z+1));
207 	MapNode n_minus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z-1));
208 	MapNode n_plus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z+1));
209 	MapNode n_minus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z-1));
210 
211 	content_t thiscontent = n.getContent();
212 	std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
213 	bool self_connect_to_raillike = ((ItemGroupList) nodedef->get(n).groups)[groupname] != 0;
214 
215 	if ((nodedef->get(n_minus_x).drawtype == NDT_RAILLIKE
216 			&& ((ItemGroupList) nodedef->get(n_minus_x).groups)[groupname] != 0
217 			&& self_connect_to_raillike)
218 			|| n_minus_x.getContent() == thiscontent)
219 		is_rail_x[0] = true;
220 
221 	if ((nodedef->get(n_minus_x_minus_y).drawtype == NDT_RAILLIKE
222 			&& ((ItemGroupList) nodedef->get(n_minus_x_minus_y).groups)[groupname] != 0
223 			&& self_connect_to_raillike)
224 			|| n_minus_x_minus_y.getContent() == thiscontent)
225 		is_rail_x_minus_y[0] = true;
226 
227 	if ((nodedef->get(n_minus_x_plus_y).drawtype == NDT_RAILLIKE
228 			&& ((ItemGroupList) nodedef->get(n_minus_x_plus_y).groups)[groupname] != 0
229 			&& self_connect_to_raillike)
230 			|| n_minus_x_plus_y.getContent() == thiscontent)
231 		is_rail_x_plus_y[0] = true;
232 
233 	if ((nodedef->get(n_plus_x).drawtype == NDT_RAILLIKE
234 			&& ((ItemGroupList) nodedef->get(n_plus_x).groups)[groupname] != 0
235 			&& self_connect_to_raillike)
236 			|| n_plus_x.getContent() == thiscontent)
237 		is_rail_x[1] = true;
238 
239 	if ((nodedef->get(n_plus_x_minus_y).drawtype == NDT_RAILLIKE
240 			&& ((ItemGroupList) nodedef->get(n_plus_x_minus_y).groups)[groupname] != 0
241 			&& self_connect_to_raillike)
242 			|| n_plus_x_minus_y.getContent() == thiscontent)
243 		is_rail_x_minus_y[1] = true;
244 
245 	if ((nodedef->get(n_plus_x_plus_y).drawtype == NDT_RAILLIKE
246 			&& ((ItemGroupList) nodedef->get(n_plus_x_plus_y).groups)[groupname] != 0
247 			&& self_connect_to_raillike)
248 			|| n_plus_x_plus_y.getContent() == thiscontent)
249 		is_rail_x_plus_y[1] = true;
250 
251 	if ((nodedef->get(n_minus_z).drawtype == NDT_RAILLIKE
252 			&& ((ItemGroupList) nodedef->get(n_minus_z).groups)[groupname] != 0
253 			&& self_connect_to_raillike)
254 			|| n_minus_z.getContent() == thiscontent)
255 		is_rail_z[0] = true;
256 
257 	if ((nodedef->get(n_minus_z_minus_y).drawtype == NDT_RAILLIKE
258 			&& ((ItemGroupList) nodedef->get(n_minus_z_minus_y).groups)[groupname] != 0
259 			&& self_connect_to_raillike)
260 			|| n_minus_z_minus_y.getContent() == thiscontent)
261 		is_rail_z_minus_y[0] = true;
262 
263 	if ((nodedef->get(n_minus_z_plus_y).drawtype == NDT_RAILLIKE
264 			&& ((ItemGroupList) nodedef->get(n_minus_z_plus_y).groups)[groupname] != 0
265 			&& self_connect_to_raillike)
266 			|| n_minus_z_plus_y.getContent() == thiscontent)
267 		is_rail_z_plus_y[0] = true;
268 
269 	if ((nodedef->get(n_plus_z).drawtype == NDT_RAILLIKE
270 			&& ((ItemGroupList) nodedef->get(n_plus_z).groups)[groupname] != 0
271 			&& self_connect_to_raillike)
272 			|| n_plus_z.getContent() == thiscontent)
273 		is_rail_z[1] = true;
274 
275 	if ((nodedef->get(n_plus_z_minus_y).drawtype == NDT_RAILLIKE
276 			&& ((ItemGroupList) nodedef->get(n_plus_z_minus_y).groups)[groupname] != 0
277 			&& self_connect_to_raillike)
278 			|| n_plus_z_minus_y.getContent() == thiscontent)
279 		is_rail_z_minus_y[1] = true;
280 
281 	if ((nodedef->get(n_plus_z_plus_y).drawtype == NDT_RAILLIKE
282 			&& ((ItemGroupList) nodedef->get(n_plus_z_plus_y).groups)[groupname] != 0
283 			&& self_connect_to_raillike)
284 			|| n_plus_z_plus_y.getContent() == thiscontent)
285 		is_rail_z_plus_y[1] = true;
286 
287 	bool is_rail_x_all[] = {false, false};
288 	bool is_rail_z_all[] = {false, false};
289 	is_rail_x_all[0]=is_rail_x[0] || is_rail_x_minus_y[0] || is_rail_x_plus_y[0];
290 	is_rail_x_all[1]=is_rail_x[1] || is_rail_x_minus_y[1] || is_rail_x_plus_y[1];
291 	is_rail_z_all[0]=is_rail_z[0] || is_rail_z_minus_y[0] || is_rail_z_plus_y[0];
292 	is_rail_z_all[1]=is_rail_z[1] || is_rail_z_minus_y[1] || is_rail_z_plus_y[1];
293 
294 	// reasonable default, flat straight unrotated rail
295 	bool is_straight = true;
296 	int adjacencies = 0;
297 	int angle = 0;
298 	u8 tileindex = 0;
299 
300 	// check for sloped rail
301 	if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
302 	{
303 		adjacencies = 5; //5 means sloped
304 		is_straight = true; // sloped is always straight
305 	}
306 	else
307 	{
308 		// is really straight, rails on both sides
309 		is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
310 		adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
311 	}
312 
313 	bool diagonalflip = false, force_end = false;
314 
315 	neighborRail self;
316 	memcpy(self.is_rail_x_all,is_rail_x_all, sizeof(bool[2]));
317 	memcpy(self.is_rail_z_all,is_rail_z_all, sizeof(bool[2]));
318 	self.adjacencies = adjacencies;
319 	self.is_straight = is_straight;
320 	self.force_end = force_end;
321 	if (recurse >= 2)
322 		return self;
323 
324 	neighborRail neighbor_x_all[2], neighbor_z_all[2];
325 	if(is_rail_x_all[0]) {
326 		if(is_rail_x_plus_y[0]) {
327 			neighbor_x_all[0] = recurseRail(v3s16(x-1, y+1, z), data, collector, recurse+1);
328 		} else if (is_rail_x[0]) {
329 			neighbor_x_all[0] = recurseRail(v3s16(x-1, y  , z), data, collector, recurse+1);
330 		} else { //if (is_rail_x_minus_y[0]) {
331 			neighbor_x_all[0] = recurseRail(v3s16(x-1, y-1, z), data, collector, recurse+1);
332 		}
333 	}
334 	if(is_rail_x_all[1]) {
335 		if(is_rail_x_plus_y[1]) {
336 			neighbor_x_all[1] = recurseRail(v3s16(x+1, y+1, z), data, collector, recurse+1);
337 		} else if (is_rail_x[1]) {
338 			neighbor_x_all[1] = recurseRail(v3s16(x+1, y  , z), data, collector, recurse+1);
339 		} else { //if (is_rail_x_minus_y[1]) {
340 			neighbor_x_all[1] = recurseRail(v3s16(x+1, y-1, z), data, collector, recurse+1);
341 		}
342 	}
343 	if(is_rail_z_all[0]) {
344 		if(is_rail_z_plus_y[0]) {
345 			neighbor_z_all[0] = recurseRail(v3s16(x, y+1, z-1), data, collector, recurse+1);
346 		} else if (is_rail_z[0]) {
347 			neighbor_z_all[0] = recurseRail(v3s16(x, y  , z-1), data, collector, recurse+1);
348 		} else { //if (is_rail_z_minus_y[0]) {
349 			neighbor_z_all[0] = recurseRail(v3s16(x, y-1, z-1), data, collector, recurse+1);
350 		}
351 	}
352 	if(is_rail_z_all[1]) {
353 		if(is_rail_z_plus_y[1]) {
354 			neighbor_z_all[1] = recurseRail(v3s16(x, y+1, z+1), data, collector, recurse+1);
355 		} else if (is_rail_z[1]) {
356 			neighbor_z_all[1] = recurseRail(v3s16(x, y  , z+1), data, collector, recurse+1);
357 		} else { //if (is_rail_z_minus_y[1]) {
358 			neighbor_z_all[1] = recurseRail(v3s16(x, y-1, z+1), data, collector, recurse+1);
359 		}
360 	}
361 
362 	bool is_corner_x_all[2], is_corner_z_all[2];
363 	is_corner_x_all[0] = is_rail_x_all[0] && neighbor_x_all[0].adjacencies == 2 && !neighbor_x_all[0].is_straight;
364 	is_corner_x_all[1] = is_rail_x_all[1] && neighbor_x_all[1].adjacencies == 2 && !neighbor_x_all[1].is_straight;
365 	is_corner_z_all[0] = is_rail_z_all[0] && neighbor_z_all[0].adjacencies == 2 && !neighbor_z_all[0].is_straight;
366 	is_corner_z_all[1] = is_rail_z_all[1] && neighbor_z_all[1].adjacencies == 2 && !neighbor_z_all[1].is_straight;
367 
368 	switch (adjacencies) {
369 	case 1: //straight
370 		tileindex = 5; //diagonal rail end
371 		       if(is_corner_z_all[0] && neighbor_z_all[0].force_end) {
372 			//angle = 0;
373 		    if(neighbor_z_all[0].is_rail_x_all[1]) {
374 		    	angle -= 90;
375 		    	diagonalflip = true;
376 		    }
377 		} else if(is_corner_x_all[0] && neighbor_x_all[0].force_end) {
378 			angle = -90;
379 			if(neighbor_x_all[0].is_rail_z_all[0]) {
380 				angle -= 90;
381 				diagonalflip = true;
382 			}
383 		} else if(is_corner_z_all[1] && neighbor_z_all[1].force_end) {
384 			angle = -180;
385 			if(neighbor_z_all[1].is_rail_x_all[0]) {
386 				angle -= 90;
387 				diagonalflip = true;
388 			}
389 		} else if(is_corner_x_all[1] && neighbor_x_all[1].force_end) {
390 			angle = -270;
391 			if(neighbor_x_all[1].is_rail_z_all[1]) {
392 				angle -= 90;
393 				diagonalflip = true;
394 			}
395 		} else {
396 			tileindex = 0; //straight
397 			if(is_rail_x_all[0] || is_rail_x_all[1])
398 				angle = 90;
399 		}
400 		break;
401 	case 2:
402 		if(!is_straight) {
403 			tileindex = 1; //curved
404 			//attempt diagonal
405 			if (
406 					(is_rail_x_all[0] && (
407 							(is_corner_z_all[0] && neighbor_z_all[0].is_rail_x_all[1]) ||
408 							(is_corner_z_all[1] && neighbor_z_all[1].is_rail_x_all[1])
409 					)) ||
410 					(is_rail_x_all[1] && (
411 							(is_corner_z_all[0] && neighbor_z_all[0].is_rail_x_all[0]) ||
412 							(is_corner_z_all[1] && neighbor_z_all[1].is_rail_x_all[0])
413 					)) ||
414 					(is_rail_z_all[0] && (
415 							(is_corner_x_all[0] && neighbor_x_all[0].is_rail_z_all[1]) ||
416 							(is_corner_x_all[1] && neighbor_x_all[1].is_rail_z_all[1])
417 					)) ||
418 					(is_rail_z_all[1] && (
419 							(is_corner_x_all[0] && neighbor_x_all[0].is_rail_z_all[0]) ||
420 							(is_corner_x_all[1] && neighbor_x_all[1].is_rail_z_all[0])
421 					))
422 			) { //at least one adjacent corner, not a U-turn
423 				tileindex = 4; //diagonal (middle)
424 				if((is_corner_x_all[0] || is_corner_x_all[1]) && (is_corner_z_all[0] || is_corner_z_all[1])) {
425 					//keep middle diagonal
426 				} else { //diagonal end/start/force_end
427 					if(
428 							(is_rail_x_all[0] && neighbor_x_all[0].adjacencies == 1) ||
429 							(is_rail_x_all[1] && neighbor_x_all[1].adjacencies == 1) ||
430 							(is_rail_z_all[0] && neighbor_z_all[0].adjacencies == 1) ||
431 							(is_rail_z_all[1] && neighbor_z_all[1].adjacencies == 1)
432 					) {
433 						//Rail end is the new end, keep middle diagonal.
434 						force_end = true;
435 					} else {
436 						tileindex = 5; //diagonal end (or start)
437 						if(
438 								((is_rail_z_all[0] && !is_corner_z_all[0]) && (is_rail_x_all[1] && is_corner_x_all[1])) ||
439 								((is_rail_x_all[0] && !is_corner_x_all[0]) && (is_rail_z_all[0] && is_corner_z_all[0])) ||
440 								((is_rail_z_all[1] && !is_corner_z_all[1]) && (is_rail_x_all[0] && is_corner_x_all[0])) ||
441 								((is_rail_x_all[1] && !is_corner_x_all[1]) && (is_rail_z_all[1] && is_corner_z_all[1]))
442 						) {
443 							diagonalflip = true; //diagonal start
444 						}
445 					}
446 				}
447 			}
448 		}
449 		if(is_rail_x_all[0] && is_rail_x_all[1])
450 			angle = 90;
451 		if(is_rail_z_all[0] && is_rail_z_all[1]){
452 			if (is_rail_z_plus_y[0])
453 				angle = 180;
454 		}
455 		else if(is_rail_x_all[0] && is_rail_z_all[0])
456 			angle = 270;
457 		else if(is_rail_x_all[0] && is_rail_z_all[1])
458 			angle = 180;
459 		else if(is_rail_x_all[1] && is_rail_z_all[1])
460 			angle = 90;
461 		break;
462 	case 3:
463 		// here is where the potential to 'switch' a junction is, but not implemented at present
464 		tileindex = 2; // t-junction
465 		if(!is_rail_x_all[1])
466 			angle=180;
467 		if(!is_rail_z_all[0])
468 			angle=90;
469 		if(!is_rail_z_all[1])
470 			angle=270;
471 		break;
472 	case 4:
473 		tileindex = 3; // crossing
474 		break;
475 	case 5: //sloped
476 		if(is_rail_z_plus_y[0])
477 			angle = 180;
478 		if(is_rail_x_plus_y[0])
479 			angle = 90;
480 		if(is_rail_x_plus_y[1])
481 			angle = -90;
482 		break;
483 	default:
484 		break;
485 	}
486 
487 	self.force_end = force_end;
488 	if (recurse >= 1)
489 		return self;
490 
491 	TileSpec tile = getNodeTileN(n, p, tileindex, data);
492 	tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
493 	tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
494 
495 	u16 l = getInteriorLight(n, 0, nodedef);
496 	video::SColor c = MapBlock_LightColor(255, l, f.light_source);
497 
498 	float d = (float)BS/64;
499 
500 			float s = BS/2;
501 
502 			short g = -1;
503 	if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
504 		g = 1; //Object is at a slope
505 
506 			video::S3DVertex vertices[4] =
507 			{
508 					video::S3DVertex(-s,  -s+d,-s,  0,0,0,  c,0,1),
509 					video::S3DVertex( s,  -s+d,-s,  0,0,0,  c,1,1),
510 					video::S3DVertex( s, g*s+d, s,  0,0,0,  c,1,0),
511 					video::S3DVertex(-s, g*s+d, s,  0,0,0,  c,0,0),
512 			};
513 
514 	for(s32 i=0; i<4; i++)
515 	{
516 		if(diagonalflip) {
517 			//swap -x,-z with +x,+z
518 			vertices[i].Pos.rotateXZBy(90);
519 			vertices[i].Pos.Z = -vertices[i].Pos.Z;
520 		}
521 		if(angle != 0)
522 			vertices[i].Pos.rotateXZBy(angle);
523 		vertices[i].Pos += intToFloat(p, BS);
524 	}
525 
526 	u16 indices[] = {0,1,2,2,3,0};
527 
528 	collector.append(tile, vertices, 4, indices, 6);
529 
530 	//required return, not used.
531 	return self;
532 }
533 
mapblock_mesh_generate_special(MeshMakeData * data,MeshCollector & collector)534 void mapblock_mesh_generate_special(MeshMakeData *data,
535 		MeshCollector &collector)
536 {
537 	INodeDefManager *nodedef = data->m_gamedef->ndef();
538 	ITextureSource *tsrc = data->m_gamedef->tsrc();
539 	scene::ISceneManager* smgr = data->m_gamedef->getSceneManager();
540 	scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
541 
542 	// 0ms
543 	//TimeTaker timer("mapblock_mesh_generate_special()");
544 
545 	/*
546 		Some settings
547 	*/
548 	bool enable_mesh_cache	= g_settings->getBool("enable_mesh_cache");
549 	bool new_style_water = g_settings->getBool("new_style_water");
550 
551 	float node_liquid_level = 1.0;
552 	if (new_style_water)
553 		node_liquid_level = 0.85;
554 
555 	v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
556 
557 	// Create selection mesh
558 	v3s16 p = data->m_highlighted_pos_relative;
559 	if (data->m_show_hud &&
560 			(p.X >= 0) && (p.X < MAP_BLOCKSIZE) &&
561 			(p.Y >= 0) && (p.Y < MAP_BLOCKSIZE) &&
562 			(p.Z >= 0) && (p.Z < MAP_BLOCKSIZE)) {
563 
564 		MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
565 		if(n.getContent() != CONTENT_AIR) {
566 			// Get selection mesh light level
567 			static const v3s16 dirs[7] = {
568 					v3s16( 0, 0, 0),
569 					v3s16( 0, 1, 0),
570 					v3s16( 0,-1, 0),
571 					v3s16( 1, 0, 0),
572 					v3s16(-1, 0, 0),
573 					v3s16( 0, 0, 1),
574 					v3s16( 0, 0,-1)
575 			};
576 
577 			u16 l = 0;
578 			u16 l1 = 0;
579 			for (u8 i = 0; i < 7; i++) {
580 				MapNode n1 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dirs[i]);
581 				l1 = getInteriorLight(n1, -4, nodedef);
582 				if (l1 > l)
583 					l = l1;
584 			}
585 			video::SColor c = MapBlock_LightColor(255, l, 0);
586 			data->m_highlight_mesh_color = c;
587 			std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
588 			TileSpec h_tile;
589 			h_tile.material_flags |= MATERIAL_FLAG_HIGHLIGHTED;
590 			h_tile.texture = tsrc->getTexture("halo.png",&h_tile.texture_id);
591 			v3f pos = intToFloat(p, BS);
592 			f32 d = 0.05 * BS;
593 			for(std::vector<aabb3f>::iterator
594 					i = boxes.begin();
595 					i != boxes.end(); i++)
596 			{
597 				aabb3f box = *i;
598 				box.MinEdge += v3f(-d, -d, -d) + pos;
599 				box.MaxEdge += v3f(d, d, d) + pos;
600 				makeCuboid(&collector, box, &h_tile, 1, c, NULL);
601 			}
602 		}
603 	}
604 
605 	for(s16 z = 0; z < MAP_BLOCKSIZE; z++)
606 	for(s16 y = 0; y < MAP_BLOCKSIZE; y++)
607 	for(s16 x = 0; x < MAP_BLOCKSIZE; x++)
608 	{
609 		v3s16 p(x,y,z);
610 
611 		MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
612 		const ContentFeatures &f = nodedef->get(n);
613 
614 		// Only solidness=0 stuff is drawn here
615 		if(f.solidness != 0)
616 			continue;
617 
618 		switch(f.drawtype){
619 		default:
620 			infostream<<"Got "<<f.drawtype<<std::endl;
621 			assert(0);
622 			break;
623 		case NDT_AIRLIKE:
624 			break;
625 		case NDT_LIQUID:
626 		{
627 			/*
628 				Add water sources to mesh if using new style
629 			*/
630 			TileSpec tile_liquid = f.special_tiles[0];
631 			TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
632 
633 			bool top_is_same_liquid = false;
634 			MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
635 			content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
636 			content_t c_source = nodedef->getId(f.liquid_alternative_source);
637 			if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
638 				top_is_same_liquid = true;
639 
640 			u16 l = getInteriorLight(n, 0, nodedef);
641 			video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
642 
643 			/*
644 				Generate sides
645 			 */
646 			v3s16 side_dirs[4] = {
647 				v3s16(1,0,0),
648 				v3s16(-1,0,0),
649 				v3s16(0,0,1),
650 				v3s16(0,0,-1),
651 			};
652 			for(u32 i=0; i<4; i++)
653 			{
654 				v3s16 dir = side_dirs[i];
655 
656 				MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
657 				content_t neighbor_content = neighbor.getContent();
658 				const ContentFeatures &n_feat = nodedef->get(neighbor_content);
659 				MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
660 				content_t n_top_c = n_top.getContent();
661 
662 				if(neighbor_content == CONTENT_IGNORE)
663 					continue;
664 
665 				/*
666 					If our topside is liquid and neighbor's topside
667 					is liquid, don't draw side face
668 				*/
669 				if(top_is_same_liquid && (n_top_c == c_flowing ||
670 						n_top_c == c_source || n_top_c == CONTENT_IGNORE))
671 					continue;
672 
673 				// Don't draw face if neighbor is blocking the view
674 				if(n_feat.solidness == 2)
675 					continue;
676 
677 				bool neighbor_is_same_liquid = (neighbor_content == c_source
678 						|| neighbor_content == c_flowing);
679 
680 				// Don't draw any faces if neighbor same is liquid and top is
681 				// same liquid
682 				if(neighbor_is_same_liquid && top_is_same_liquid)
683 					continue;
684 
685 				// Use backface culled material if neighbor doesn't have a
686 				// solidness of 0
687 				const TileSpec *current_tile = &tile_liquid;
688 				if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
689 					current_tile = &tile_liquid_bfculled;
690 
691 				video::S3DVertex vertices[4] =
692 				{
693 					video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, 0,1),
694 					video::S3DVertex(BS/2,0,BS/2,0,0,0, c, 1,1),
695 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
696 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
697 				};
698 
699 				/*
700 					If our topside is liquid, set upper border of face
701 					at upper border of node
702 				*/
703 				if(top_is_same_liquid)
704 				{
705 					vertices[2].Pos.Y = 0.5*BS;
706 					vertices[3].Pos.Y = 0.5*BS;
707 				}
708 				/*
709 					Otherwise upper position of face is liquid level
710 				*/
711 				else
712 				{
713 					vertices[2].Pos.Y = (node_liquid_level-0.5)*BS;
714 					vertices[3].Pos.Y = (node_liquid_level-0.5)*BS;
715 				}
716 				/*
717 					If neighbor is liquid, lower border of face is liquid level
718 				*/
719 				if(neighbor_is_same_liquid)
720 				{
721 					vertices[0].Pos.Y = (node_liquid_level-0.5)*BS;
722 					vertices[1].Pos.Y = (node_liquid_level-0.5)*BS;
723 				}
724 				/*
725 					If neighbor is not liquid, lower border of face is
726 					lower border of node
727 				*/
728 				else
729 				{
730 					vertices[0].Pos.Y = -0.5*BS;
731 					vertices[1].Pos.Y = -0.5*BS;
732 				}
733 
734 				for(s32 j=0; j<4; j++)
735 				{
736 					if(dir == v3s16(0,0,1))
737 						vertices[j].Pos.rotateXZBy(0);
738 					if(dir == v3s16(0,0,-1))
739 						vertices[j].Pos.rotateXZBy(180);
740 					if(dir == v3s16(-1,0,0))
741 						vertices[j].Pos.rotateXZBy(90);
742 					if(dir == v3s16(1,0,-0))
743 						vertices[j].Pos.rotateXZBy(-90);
744 
745 					// Do this to not cause glitches when two liquids are
746 					// side-by-side
747 					/*if(neighbor_is_same_liquid == false){
748 						vertices[j].Pos.X *= 0.98;
749 						vertices[j].Pos.Z *= 0.98;
750 					}*/
751 
752 					vertices[j].Pos += intToFloat(p, BS);
753 				}
754 
755 				u16 indices[] = {0,1,2,2,3,0};
756 				// Add to mesh collector
757 				collector.append(*current_tile, vertices, 4, indices, 6);
758 			}
759 
760 			/*
761 				Generate top
762 			 */
763 			if(top_is_same_liquid)
764 				continue;
765 
766 			video::S3DVertex vertices[4] =
767 			{
768 				video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
769 				video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
770 				video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
771 				video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
772 			};
773 
774 			v3f offset(p.X*BS, p.Y*BS + (-0.5+node_liquid_level)*BS, p.Z*BS);
775 			for(s32 i=0; i<4; i++)
776 			{
777 				vertices[i].Pos += offset;
778 			}
779 
780 			u16 indices[] = {0,1,2,2,3,0};
781 			// Add to mesh collector
782 			collector.append(tile_liquid, vertices, 4, indices, 6);
783 		break;}
784 		case NDT_FLOWINGLIQUID:
785 		{
786 			/*
787 				Add flowing liquid to mesh
788 			*/
789 			TileSpec tile_liquid = f.special_tiles[0];
790 			TileSpec tile_liquid_bfculled = f.special_tiles[1];
791 			const TileSpec *current_tile = &tile_liquid;
792 
793 			bool top_is_same_liquid = false;
794 			MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
795 			content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
796 			content_t c_source = nodedef->getId(f.liquid_alternative_source);
797 			TileSpec tile_liquid_source = nodedef->get(c_source).special_tiles[0];
798 			if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
799 				top_is_same_liquid = true;
800 
801 			u16 l = 0;
802 			// If this liquid emits light and doesn't contain light, draw
803 			// it at what it emits, for an increased effect
804 			u8 light_source = nodedef->get(n).light_source;
805 			if(light_source != 0){
806 				l = decode_light(light_source);
807 				l = l | (l<<8);
808 			}
809 			// Use the light of the node on top if possible
810 			else if(nodedef->get(ntop).param_type == CPT_LIGHT)
811 				l = getInteriorLight(ntop, 0, nodedef);
812 			// Otherwise use the light of this node (the liquid)
813 			else
814 				l = getInteriorLight(n, 0, nodedef);
815 			video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
816 
817 			// Neighbor liquid levels (key = relative position)
818 			// Includes current node
819 			std::map<v3s16, f32> neighbor_levels;
820 			std::map<v3s16, content_t> neighbor_contents;
821 			std::map<v3s16, u8> neighbor_flags;
822 			const u8 neighborflag_top_is_same_liquid = 0x01;
823 			v3s16 neighbor_dirs[9] = {
824 				v3s16(0,0,0),
825 				v3s16(0,0,1),
826 				v3s16(0,0,-1),
827 				v3s16(1,0,0),
828 				v3s16(-1,0,0),
829 				v3s16(1,0,1),
830 				v3s16(-1,0,-1),
831 				v3s16(1,0,-1),
832 				v3s16(-1,0,1),
833 			};
834 			for(u32 i=0; i<9; i++)
835 			{
836 				content_t content = CONTENT_AIR;
837 				float level = -0.5 * BS;
838 				u8 flags = 0;
839 				// Check neighbor
840 				v3s16 p2 = p + neighbor_dirs[i];
841 				MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
842 				if(n2.getContent() != CONTENT_IGNORE)
843 				{
844 					content = n2.getContent();
845 
846 					if(n2.getContent() == c_source)
847 						level = (-0.5+node_liquid_level) * BS;
848 					else if(n2.getContent() == c_flowing){
849 						level = (-0.5 + ((float)n2.getLevel(nodedef)
850 							 + 0.5) / n2.getMaxLevel(nodedef) * node_liquid_level) * BS;
851 					}
852 
853 					// Check node above neighbor.
854 					// NOTE: This doesn't get executed if neighbor
855 					//       doesn't exist
856 					p2.Y += 1;
857 					n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
858 					if(n2.getContent() == c_source ||
859 							n2.getContent() == c_flowing)
860 						flags |= neighborflag_top_is_same_liquid;
861 				}
862 
863 				neighbor_levels[neighbor_dirs[i]] = level;
864 				neighbor_contents[neighbor_dirs[i]] = content;
865 				neighbor_flags[neighbor_dirs[i]] = flags;
866 			}
867 
868 			// Corner heights (average between four liquids)
869 			f32 corner_levels[4];
870 
871 			v3s16 halfdirs[4] = {
872 				v3s16(0,0,0),
873 				v3s16(1,0,0),
874 				v3s16(1,0,1),
875 				v3s16(0,0,1),
876 			};
877 			for(u32 i=0; i<4; i++)
878 			{
879 				v3s16 cornerdir = halfdirs[i];
880 				float cornerlevel = 0;
881 				u32 valid_count = 0;
882 				u32 air_count = 0;
883 				for(u32 j=0; j<4; j++)
884 				{
885 					v3s16 neighbordir = cornerdir - halfdirs[j];
886 					content_t content = neighbor_contents[neighbordir];
887 					// If top is liquid, draw starting from top of node
888 					if(neighbor_flags[neighbordir] &
889 							neighborflag_top_is_same_liquid)
890 					{
891 						cornerlevel = 0.5*BS;
892 						valid_count = 1;
893 						break;
894 					}
895 					// Source is always the same height
896 					else if(content == c_source)
897 					{
898 						cornerlevel = (-0.5+node_liquid_level)*BS;
899 						valid_count = 1;
900 						break;
901 					}
902 					// Flowing liquid has level information
903 					else if(content == c_flowing)
904 					{
905 						cornerlevel += neighbor_levels[neighbordir];
906 						valid_count++;
907 					}
908 					else if(content == CONTENT_AIR)
909 					{
910 						air_count++;
911 					}
912 				}
913 				if(air_count >= 2)
914 					cornerlevel = -0.5*BS+(float)1/f.getMaxLevel();
915 				else if(valid_count > 0)
916 					cornerlevel /= valid_count;
917 				corner_levels[i] = cornerlevel;
918 			}
919 
920 			/*
921 				Generate sides
922 			*/
923 
924 			v3s16 side_dirs[4] = {
925 				v3s16(1,0,0),
926 				v3s16(-1,0,0),
927 				v3s16(0,0,1),
928 				v3s16(0,0,-1),
929 			};
930 			s16 side_corners[4][2] = {
931 				{1, 2},
932 				{3, 0},
933 				{2, 3},
934 				{0, 1},
935 			};
936 			for(u32 i=0; i<4; i++)
937 			{
938 				v3s16 dir = side_dirs[i];
939 
940 				/*
941 					If our topside is liquid and neighbor's topside
942 					is liquid, don't draw side face
943 				*/
944 				if(top_is_same_liquid &&
945 						neighbor_flags[dir] & neighborflag_top_is_same_liquid)
946 					continue;
947 
948 				content_t neighbor_content = neighbor_contents[dir];
949 				const ContentFeatures &n_feat = nodedef->get(neighbor_content);
950 
951 				// Don't draw face if neighbor is blocking the view
952 				if(n_feat.solidness == 2)
953 					continue;
954 
955 				bool neighbor_is_same_liquid = (neighbor_content == c_source
956 						|| neighbor_content == c_flowing);
957 
958 				// Don't draw any faces if neighbor same is liquid and top is
959 				// same liquid
960 				if(neighbor_is_same_liquid == true
961 						&& top_is_same_liquid == true)
962 					continue;
963 
964 				// Use backface culled material if neighbor doesn't have a
965 				// solidness of 0
966 				current_tile = &tile_liquid;
967 				if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
968 					current_tile = &tile_liquid_bfculled;
969 
970 				video::S3DVertex vertices[4] =
971 				{
972 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
973 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
974 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
975 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
976 				};
977 
978 				/*
979 					If our topside is liquid, set upper border of face
980 					at upper border of node
981 				*/
982 				if(top_is_same_liquid)
983 				{
984 					vertices[2].Pos.Y = 0.5*BS;
985 					vertices[3].Pos.Y = 0.5*BS;
986 				}
987 				/*
988 					Otherwise upper position of face is corner levels
989 				*/
990 				else
991 				{
992 					vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
993 					vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
994 				}
995 
996 				/*
997 					If neighbor is liquid, lower border of face is corner
998 					liquid levels
999 				*/
1000 				if(neighbor_is_same_liquid)
1001 				{
1002 					vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1003 					vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1004 				}
1005 				/*
1006 					If neighbor is not liquid, lower border of face is
1007 					lower border of node
1008 				*/
1009 				else
1010 				{
1011 					vertices[0].Pos.Y = -0.5*BS;
1012 					vertices[1].Pos.Y = -0.5*BS;
1013 				}
1014 
1015 				for(s32 j=0; j<4; j++)
1016 				{
1017 					if(dir == v3s16(0,0,1))
1018 						vertices[j].Pos.rotateXZBy(0);
1019 					if(dir == v3s16(0,0,-1))
1020 						vertices[j].Pos.rotateXZBy(180);
1021 					if(dir == v3s16(-1,0,0))
1022 						vertices[j].Pos.rotateXZBy(90);
1023 					if(dir == v3s16(1,0,-0))
1024 						vertices[j].Pos.rotateXZBy(-90);
1025 
1026 					// Do this to not cause glitches when two liquids are
1027 					// side-by-side
1028 					/*if(neighbor_is_same_liquid == false){
1029 						vertices[j].Pos.X *= 0.98;
1030 						vertices[j].Pos.Z *= 0.98;
1031 					}*/
1032 
1033 					vertices[j].Pos += intToFloat(p, BS);
1034 				}
1035 
1036 				u16 indices[] = {0,1,2,2,3,0};
1037 				// Add to mesh collector
1038 				collector.append(*current_tile, vertices, 4, indices, 6);
1039 			}
1040 
1041 			/*
1042 				Generate top side, if appropriate
1043 			*/
1044 
1045 			if(top_is_same_liquid == false)
1046 			{
1047 				current_tile = &tile_liquid_source;
1048 				video::S3DVertex vertices[4] =
1049 				{
1050 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1051 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1052 					video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
1053 					video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
1054 				};
1055 
1056 				// To get backface culling right, the vertices need to go
1057 				// clockwise around the front of the face. And we happened to
1058 				// calculate corner levels in exact reverse order.
1059 				s32 corner_resolve[4] = {3,2,1,0};
1060 
1061 				for(s32 i=0; i<4; i++)
1062 				{
1063 					//vertices[i].Pos.Y += liquid_level;
1064 					//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1065 					s32 j = corner_resolve[i];
1066 					vertices[i].Pos.Y += corner_levels[j];
1067 					if (neighbor_levels[neighbor_dirs[0]] > corner_levels[j] + 0.25 ||
1068 					    neighbor_levels[neighbor_dirs[0]] < corner_levels[j] - 0.25)
1069 						current_tile = &tile_liquid;
1070 					vertices[i].Pos += intToFloat(p, BS);
1071 				}
1072 
1073 				// Default downwards-flowing texture animation goes from
1074 				// -Z towards +Z, thus the direction is +Z.
1075 				// Rotate texture to make animation go in flow direction
1076 				// Positive if liquid moves towards +Z
1077 				f32 dz = (corner_levels[side_corners[3][0]] +
1078 						corner_levels[side_corners[3][1]]) -
1079 						(corner_levels[side_corners[2][0]] +
1080 						corner_levels[side_corners[2][1]]);
1081 				// Positive if liquid moves towards +X
1082 				f32 dx = (corner_levels[side_corners[1][0]] +
1083 						corner_levels[side_corners[1][1]]) -
1084 						(corner_levels[side_corners[0][0]] +
1085 						corner_levels[side_corners[0][1]]);
1086 				f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG ;
1087 				v2f tcoord_center(0.5, 0.5);
1088 				v2f tcoord_translate(
1089 						blockpos_nodes.Z + z,
1090 						blockpos_nodes.X + x);
1091 				tcoord_translate.rotateBy(tcoord_angle);
1092 				tcoord_translate.X -= floor(tcoord_translate.X);
1093 				tcoord_translate.Y -= floor(tcoord_translate.Y);
1094 
1095 				for(s32 i=0; i<4; i++)
1096 				{
1097 					vertices[i].TCoords.rotateBy(
1098 							tcoord_angle,
1099 							tcoord_center);
1100 					vertices[i].TCoords += tcoord_translate;
1101 				}
1102 
1103 				v2f t = vertices[0].TCoords;
1104 				vertices[0].TCoords = vertices[2].TCoords;
1105 				vertices[2].TCoords = t;
1106 
1107 				u16 indices[] = {0,1,2,2,3,0};
1108 				// Add to mesh collector
1109 				collector.append(*current_tile, vertices, 4, indices, 6);
1110 			}
1111 
1112 			/*
1113 				Generate bottom side, if appropriate
1114 			*/
1115 			MapNode n_bottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y-1,z));
1116 			const ContentFeatures &f_bottom = nodedef->get(n_bottom);
1117 			if (!f_bottom.walkable && n_bottom.getContent() != c_flowing &&
1118 				n_bottom.getContent() != c_source) {
1119 				video::S3DVertex vertices[4] = {
1120 					video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1121 					video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1122 					video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
1123 					video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
1124 				};
1125 
1126 				v3f offset(p.X*BS, p.Y*BS + -0.5*BS, p.Z*BS);
1127 				for(s32 i=0; i<4; i++) {
1128 					vertices[i].Pos += offset;
1129 				}
1130 
1131 				u16 indices[] = {0,1,2,2,3,0};
1132 				// Add to mesh collector
1133 				collector.append(tile_liquid, vertices, 4, indices, 6);
1134                         }
1135 		break;}
1136 		case NDT_GLASSLIKE:
1137 		{
1138 			TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1139 
1140 			u16 l = getInteriorLight(n, 1, nodedef);
1141 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1142 
1143 			for(u32 j=0; j<6; j++)
1144 			{
1145 				// Check this neighbor
1146 				v3s16 n2p = blockpos_nodes + p + g_6dirs[j];
1147 				MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1148 				// Don't make face if neighbor is of same type
1149 				if(n2.getContent() == n.getContent())
1150 					continue;
1151 
1152 				// The face at Z+
1153 				video::S3DVertex vertices[4] = {
1154 					video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1155 					video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1156 					video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 0,0),
1157 					video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1158 				};
1159 
1160 				// Rotations in the g_6dirs format
1161 				if(j == 0) // Z+
1162 					for(u16 i=0; i<4; i++)
1163 						vertices[i].Pos.rotateXZBy(0);
1164 				else if(j == 1) // Y+
1165 					for(u16 i=0; i<4; i++)
1166 						vertices[i].Pos.rotateYZBy(-90);
1167 				else if(j == 2) // X+
1168 					for(u16 i=0; i<4; i++)
1169 						vertices[i].Pos.rotateXZBy(-90);
1170 				else if(j == 3) // Z-
1171 					for(u16 i=0; i<4; i++)
1172 						vertices[i].Pos.rotateXZBy(180);
1173 				else if(j == 4) // Y-
1174 					for(u16 i=0; i<4; i++)
1175 						vertices[i].Pos.rotateYZBy(90);
1176 				else if(j == 5) // X-
1177 					for(u16 i=0; i<4; i++)
1178 						vertices[i].Pos.rotateXZBy(90);
1179 
1180 				for(u16 i=0; i<4; i++){
1181 					vertices[i].Pos += intToFloat(p, BS);
1182 				}
1183 
1184 				u16 indices[] = {0,1,2,2,3,0};
1185 				// Add to mesh collector
1186 				collector.append(tile, vertices, 4, indices, 6);
1187 			}
1188 		break;}
1189 		case NDT_GLASSLIKE_FRAMED_OPTIONAL:
1190 			// This is always pre-converted to something else
1191 			assert(0);
1192 			break;
1193 		case NDT_GLASSLIKE_FRAMED:
1194 		{
1195 			static const v3s16 dirs[6] = {
1196 				v3s16( 0, 1, 0),
1197 				v3s16( 0,-1, 0),
1198 				v3s16( 1, 0, 0),
1199 				v3s16(-1, 0, 0),
1200 				v3s16( 0, 0, 1),
1201 				v3s16( 0, 0,-1)
1202 			};
1203 
1204 			u8 i;
1205 			TileSpec tiles[6];
1206 			for (i = 0; i < 6; i++)
1207 				tiles[i] = getNodeTile(n, p, dirs[i], data);
1208 
1209 			TileSpec glass_tiles[6];
1210 			if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
1211 				glass_tiles[0] = tiles[2];
1212 				glass_tiles[1] = tiles[3];
1213 				glass_tiles[2] = tiles[1];
1214 				glass_tiles[3] = tiles[1];
1215 				glass_tiles[4] = tiles[1];
1216 				glass_tiles[5] = tiles[1];
1217 			} else {
1218 				for (i = 0; i < 6; i++)
1219 					glass_tiles[i] = tiles[1];
1220 			}
1221 
1222 			u8 param2 = n.getParam2();
1223 			bool H_merge = ! bool(param2 & 128);
1224 			bool V_merge = ! bool(param2 & 64);
1225 			param2  = param2 & 63;
1226 
1227 			u16 l = getInteriorLight(n, 1, nodedef);
1228 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1229 			v3f pos = intToFloat(p, BS);
1230 			static const float a = BS / 2;
1231 			static const float g = a - 0.003;
1232 			static const float b = .876 * ( BS / 2 );
1233 
1234 			static const aabb3f frame_edges[12] = {
1235 				aabb3f( b, b,-a, a, a, a), // y+
1236 				aabb3f(-a, b,-a,-b, a, a), // y+
1237 				aabb3f( b,-a,-a, a,-b, a), // y-
1238 				aabb3f(-a,-a,-a,-b,-b, a), // y-
1239 				aabb3f( b,-a, b, a, a, a), // x+
1240 				aabb3f( b,-a,-a, a, a,-b), // x+
1241 				aabb3f(-a,-a, b,-b, a, a), // x-
1242 				aabb3f(-a,-a,-a,-b, a,-b), // x-
1243 				aabb3f(-a, b, b, a, a, a), // z+
1244 				aabb3f(-a,-a, b, a,-b, a), // z+
1245 				aabb3f(-a,-a,-a, a,-b,-b), // z-
1246 				aabb3f(-a, b,-a, a, a,-b)  // z-
1247 			};
1248 			static const aabb3f glass_faces[6] = {
1249 				aabb3f(-g, g,-g, g, g, g), // y+
1250 				aabb3f(-g,-g,-g, g,-g, g), // y-
1251 				aabb3f( g,-g,-g, g, g, g), // x+
1252 				aabb3f(-g,-g,-g,-g, g, g), // x-
1253 				aabb3f(-g,-g, g, g, g, g), // z+
1254 				aabb3f(-g,-g,-g, g, g,-g)  // z-
1255 			};
1256 
1257 			// table of node visible faces, 0 = invisible
1258 			int visible_faces[6] = {0,0,0,0,0,0};
1259 
1260 			// table of neighbours, 1 = same type, checked with g_26dirs
1261 			int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
1262 
1263 			// g_26dirs to check when only horizontal merge is allowed
1264 			int nb_H_dirs[8] = {0,2,3,5,10,11,12,13};
1265 
1266 			content_t current = n.getContent();
1267 			content_t n2c;
1268 			MapNode n2;
1269 			v3s16 n2p;
1270 
1271 			// neighbours checks for frames visibility
1272 
1273 			if (!H_merge && V_merge) {
1274 				n2p = blockpos_nodes + p + g_26dirs[1];
1275 				n2 = data->m_vmanip.getNodeNoEx(n2p);
1276 				n2c = n2.getContent();
1277 				if (n2c == current || n2c == CONTENT_IGNORE)
1278 					nb[1] = 1;
1279 				n2p = blockpos_nodes + p + g_26dirs[4];
1280 				n2 = data->m_vmanip.getNodeNoEx(n2p);
1281 				n2c = n2.getContent();
1282 				if (n2c == current || n2c == CONTENT_IGNORE)
1283 					nb[4] = 1;
1284 			} else if (H_merge && !V_merge) {
1285 				for(i = 0; i < 8; i++) {
1286 					n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]];
1287 					n2 = data->m_vmanip.getNodeNoEx(n2p);
1288 					n2c = n2.getContent();
1289 					if (n2c == current || n2c == CONTENT_IGNORE)
1290 						nb[nb_H_dirs[i]] = 1;
1291 				}
1292 			} else if (H_merge && V_merge) {
1293 				for(i = 0; i < 18; i++)	{
1294 					n2p = blockpos_nodes + p + g_26dirs[i];
1295 					n2 = data->m_vmanip.getNodeNoEx(n2p);
1296 					n2c = n2.getContent();
1297 					if (n2c == current || n2c == CONTENT_IGNORE)
1298 						nb[i] = 1;
1299 				}
1300 			}
1301 
1302 			// faces visibility checks
1303 
1304 			if (!V_merge) {
1305 				visible_faces[0] = 1;
1306 				visible_faces[1] = 1;
1307 			} else {
1308 				for(i = 0; i < 2; i++) {
1309 					n2p = blockpos_nodes + p + dirs[i];
1310 					n2 = data->m_vmanip.getNodeNoEx(n2p);
1311 					n2c = n2.getContent();
1312 					if (n2c != current)
1313 						visible_faces[i] = 1;
1314 				}
1315 			}
1316 
1317 			if (!H_merge) {
1318 				visible_faces[2] = 1;
1319 				visible_faces[3] = 1;
1320 				visible_faces[4] = 1;
1321 				visible_faces[5] = 1;
1322 			} else {
1323 				for(i = 2; i < 6; i++) {
1324 					n2p = blockpos_nodes + p + dirs[i];
1325 					n2 = data->m_vmanip.getNodeNoEx(n2p);
1326 					n2c = n2.getContent();
1327 					if (n2c != current)
1328 						visible_faces[i] = 1;
1329 				}
1330 			}
1331 
1332 			static const u8 nb_triplet[12*3] = {
1333 				1,2, 7,  1,5, 6,  4,2,15,  4,5,14,
1334 				2,0,11,  2,3,13,  5,0,10,  5,3,12,
1335 				0,1, 8,  0,4,16,  3,4,17,  3,1, 9
1336 			};
1337 
1338 			f32 tx1, ty1, tz1, tx2, ty2, tz2;
1339 			aabb3f box;
1340 
1341 			for(i = 0; i < 12; i++)
1342 			{
1343 				int edge_invisible;
1344 				if (nb[nb_triplet[i*3+2]])
1345 					edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]];
1346 				else
1347 					edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]];
1348 				if (edge_invisible)
1349 					continue;
1350 				box = frame_edges[i];
1351 				box.MinEdge += pos;
1352 				box.MaxEdge += pos;
1353 				tx1 = (box.MinEdge.X / BS) + 0.5;
1354 				ty1 = (box.MinEdge.Y / BS) + 0.5;
1355 				tz1 = (box.MinEdge.Z / BS) + 0.5;
1356 				tx2 = (box.MaxEdge.X / BS) + 0.5;
1357 				ty2 = (box.MaxEdge.Y / BS) + 0.5;
1358 				tz2 = (box.MaxEdge.Z / BS) + 0.5;
1359 				f32 txc1[24] = {
1360 					tx1,   1-tz2,   tx2, 1-tz1,
1361 					tx1,     tz1,   tx2,   tz2,
1362 					tz1,   1-ty2,   tz2, 1-ty1,
1363 					1-tz2, 1-ty2, 1-tz1, 1-ty1,
1364 					1-tx2, 1-ty2, 1-tx1, 1-ty1,
1365 					tx1,   1-ty2,   tx2, 1-ty1,
1366 				};
1367 				makeCuboid(&collector, box, &tiles[0], 1, c, txc1);
1368 			}
1369 
1370 			for(i = 0; i < 6; i++)
1371 			{
1372 				if (!visible_faces[i])
1373 					continue;
1374 				box = glass_faces[i];
1375 				box.MinEdge += pos;
1376 				box.MaxEdge += pos;
1377 				tx1 = (box.MinEdge.X / BS) + 0.5;
1378 				ty1 = (box.MinEdge.Y / BS) + 0.5;
1379 				tz1 = (box.MinEdge.Z / BS) + 0.5;
1380 				tx2 = (box.MaxEdge.X / BS) + 0.5;
1381 				ty2 = (box.MaxEdge.Y / BS) + 0.5;
1382 				tz2 = (box.MaxEdge.Z / BS) + 0.5;
1383 				f32 txc2[24] = {
1384 					tx1,   1-tz2,   tx2, 1-tz1,
1385 					tx1,     tz1,   tx2,   tz2,
1386 					tz1,   1-ty2,   tz2, 1-ty1,
1387 					1-tz2, 1-ty2, 1-tz1, 1-ty1,
1388 					1-tx2, 1-ty2, 1-tx1, 1-ty1,
1389 					tx1,   1-ty2,   tx2, 1-ty1,
1390 				};
1391 				makeCuboid(&collector, box, &glass_tiles[i], 1, c, txc2);
1392 			}
1393 
1394 			if (param2 > 0 && f.special_tiles[0].texture) {
1395 				// Interior volume level is in range 0 .. 63,
1396 				// convert it to -0.5 .. 0.5
1397 				float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0);
1398 				TileSpec interior_tiles[6];
1399 				for (i = 0; i < 6; i++)
1400 					interior_tiles[i] = f.special_tiles[0];
1401 				float offset = 0.003;
1402 				box = aabb3f(visible_faces[3] ? -b : -a + offset,
1403 							 visible_faces[1] ? -b : -a + offset,
1404 							 visible_faces[5] ? -b : -a + offset,
1405 							 visible_faces[2] ? b : a - offset,
1406 							 visible_faces[0] ? b * vlev : a * vlev - offset,
1407 							 visible_faces[4] ? b : a - offset);
1408 				box.MinEdge += pos;
1409 				box.MaxEdge += pos;
1410 				tx1 = (box.MinEdge.X / BS) + 0.5;
1411 				ty1 = (box.MinEdge.Y / BS) + 0.5;
1412 				tz1 = (box.MinEdge.Z / BS) + 0.5;
1413 				tx2 = (box.MaxEdge.X / BS) + 0.5;
1414 				ty2 = (box.MaxEdge.Y / BS) + 0.5;
1415 				tz2 = (box.MaxEdge.Z / BS) + 0.5;
1416 				f32 txc3[24] = {
1417 					tx1,   1-tz2,   tx2, 1-tz1,
1418 					tx1,     tz1,   tx2,   tz2,
1419 					tz1,   1-ty2,   tz2, 1-ty1,
1420 					1-tz2, 1-ty2, 1-tz1, 1-ty1,
1421 					1-tx2, 1-ty2, 1-tx1, 1-ty1,
1422 					tx1,   1-ty2,   tx2, 1-ty1,
1423 				};
1424 				makeCuboid(&collector, box, interior_tiles, 6, c,  txc3);
1425 			}
1426 		break;}
1427 		case NDT_ALLFACES:
1428 		{
1429 			TileSpec tile_leaves = getNodeTile(n, p,
1430 					v3s16(0,0,0), data);
1431 
1432 			u16 l = getInteriorLight(n, 1, nodedef);
1433 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1434 
1435 			v3f pos = intToFloat(p, BS);
1436 			aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
1437 			box.MinEdge += pos;
1438 			box.MaxEdge += pos;
1439 			makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
1440 		break;}
1441 		case NDT_ALLFACES_OPTIONAL:
1442 			// This is always pre-converted to something else
1443 			assert(0);
1444 			break;
1445 		case NDT_TORCHLIKE:
1446 		{
1447 			v3s16 dir(0,0,0);
1448 			dir = n.getWallMountedDir(nodedef);
1449 
1450 			// TileSpec tile = getNodeTileN(n, p, tileindex, data);
1451 			TileSpec tile = getNodeTileN(n, p, 0, data);
1452 			tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1453 			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1454 
1455 			u16 l = getInteriorLight(n, 1, nodedef);
1456 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1457 
1458 			float d = (float) BS/64;
1459 			f32 HBB = HBS/8;
1460 			video::S3DVertex vertices[6][4] = {
1461 				{	// right
1462 
1463 					video::S3DVertex(HBB,HBS-1,-HBS, 1,0,0, c, 0,0),
1464 					video::S3DVertex(HBB,HBS-1,HBS, 1,0,0, c, 1,0),
1465 					video::S3DVertex(HBB,-HBS,HBS, 1,0,0, c, 1,1),
1466 					video::S3DVertex(HBB,-HBS,-HBS, 1,0,0, c, 0,1)
1467 				},
1468 				{	// left
1469 					video::S3DVertex(-HBB,HBS,HBS, -1,0,0, c, 0,0),
1470 					video::S3DVertex(-HBB,HBS,-HBS, -1,0,0, c, 1,0),
1471 					video::S3DVertex(-HBB,-HBS,-HBS, -1,0,0, c, 1,1),
1472 					video::S3DVertex(-HBB,-HBS,HBS, -1,0,0, c, 0,1)
1473 				},
1474 				{	// back
1475 					video::S3DVertex(HBS,HBS-1,HBB, 0,0,0, c, 0,0),
1476 					video::S3DVertex(-HBS,HBS-1,HBB, 0,0,0, c, 1,0),
1477 					video::S3DVertex(-HBS,-HBS,HBB, 0,0,0, c, 1,1),
1478 					video::S3DVertex(HBS,-HBS,HBB, 0,0,0, c, 0,1)
1479 				},
1480 				{	// front
1481 					video::S3DVertex(-HBS,HBS,-HBB, 0,0,0, c, 0,0),
1482 					video::S3DVertex(HBS,HBS,-HBB, 0,0,0, c, 1,0),
1483 					video::S3DVertex(HBS,-HBS,-HBB, 0,0,0, c, 1,1),
1484 					video::S3DVertex(-HBS,-HBS,-HBB, 0,0,0, c, 0,1)
1485 				},
1486 				{	// up
1487 					video::S3DVertex(HBB,HBS-d-2.375,HBB, 0,0,0, c, HBB-0.18,0.18),
1488 					video::S3DVertex(-HBB,HBS-d-2.375,HBB, 0,0,0, c, HBB-0.06,0.18),
1489 					video::S3DVertex(-HBB,HBS-d-2.375,-HBB, 0,0,0, c, HBB-0.06,0.305),
1490 					video::S3DVertex(HBB,HBS-d-2.375,-HBB, 0,0,0, c, HBB-0.18,0.305)
1491 				},
1492 				{	// down
1493 					video::S3DVertex(-HBB,-HBS+d,-HBB, 0,0,0, c, HBB-0.18,0.875),
1494 					video::S3DVertex(HBB,-HBS+d,-HBB, 0,0,0, c, HBB-0.06,0.875),
1495 					video::S3DVertex(HBB,-HBS+d,HBB, 0,0,0, c, HBB-0.06,1),
1496 					video::S3DVertex(-HBB,-HBS+d,HBB, 0,0,0, c, HBB-0.18,1)
1497 				}
1498 			};
1499 
1500 			for(u16 j=0; j<6; j++) {
1501 				for(u16 i=0; i<4; i++) {
1502 					f32 y_offset = 0;
1503 					if (dir == v3s16(0,1,0)){
1504 						vertices[j][i].Pos.rotateXYBy(180); // ceiling
1505 					} else if(dir != v3s16(0,0,0) && dir != v3s16(0,-1,0)) {
1506 						vertices[j][i].Pos.rotateXYBy(20); // wall
1507 						y_offset = 1;
1508 					}
1509 					if(dir == v3s16(1,0,0))
1510 						vertices[j][i].Pos.rotateXZBy(0);
1511 					if(dir == v3s16(-1,0,0))
1512 						vertices[j][i].Pos.rotateXZBy(180);
1513 					if(dir == v3s16(0,0,1))
1514 						vertices[j][i].Pos.rotateXZBy(90);
1515 					if(dir == v3s16(0,0,-1))
1516 						vertices[j][i].Pos.rotateXZBy(-90);
1517 					if(dir == v3s16(0,-1,0))
1518 						vertices[j][i].Pos.rotateXZBy(0); // old 45
1519 					if(dir == v3s16(0,1,0))
1520 						vertices[j][i].Pos.rotateXZBy(0); // old -45
1521 					vertices[j][i].Pos += intToFloat(p, BS);
1522 					vertices[j][i].Pos += v3f(3.3*dir.X,y_offset,3.3*dir.Z);
1523 				}
1524 				u16 indices[] = {0,1,2,2,3,0};
1525 				// Add to mesh collector
1526 				collector.append(tile, vertices[j], 4, indices, 6);
1527 			}
1528 			break;}
1529 		case NDT_SIGNLIKE:
1530 		{
1531 			TileSpec tile = getNodeTileN(n, p, 0, data);
1532 			tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1533 			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1534 
1535 			u16 l = getInteriorLight(n, 0, nodedef);
1536 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1537 
1538 			float d = (float)BS/16;
1539 			float s = BS/2*f.visual_scale;
1540 			// Wall at X+ of node
1541 			video::S3DVertex vertices[4] =
1542 			{
1543 				video::S3DVertex(BS/2-d,  s,  s, 0,0,0, c, 0,0),
1544 				video::S3DVertex(BS/2-d,  s, -s, 0,0,0, c, 1,0),
1545 				video::S3DVertex(BS/2-d, -s, -s, 0,0,0, c, 1,1),
1546 				video::S3DVertex(BS/2-d, -s,  s, 0,0,0, c, 0,1),
1547 			};
1548 
1549 			v3s16 dir = n.getWallMountedDir(nodedef);
1550 
1551 			for(s32 i=0; i<4; i++)
1552 			{
1553 				if(dir == v3s16(1,0,0))
1554 					vertices[i].Pos.rotateXZBy(0);
1555 				if(dir == v3s16(-1,0,0))
1556 					vertices[i].Pos.rotateXZBy(180);
1557 				if(dir == v3s16(0,0,1))
1558 					vertices[i].Pos.rotateXZBy(90);
1559 				if(dir == v3s16(0,0,-1))
1560 					vertices[i].Pos.rotateXZBy(-90);
1561 				if(dir == v3s16(0,-1,0))
1562 					vertices[i].Pos.rotateXYBy(-90);
1563 				if(dir == v3s16(0,1,0))
1564 					vertices[i].Pos.rotateXYBy(90);
1565 
1566 				vertices[i].Pos += intToFloat(p, BS);
1567 			}
1568 
1569 			u16 indices[] = {0,1,2,2,3,0};
1570 			// Add to mesh collector
1571 			collector.append(tile, vertices, 4, indices, 6);
1572 		break;}
1573 		case NDT_PLANTLIKE:
1574 		{
1575 			TileSpec tile = getNodeTileN(n, p, 0, data);
1576 			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1577 
1578 			u16 l = getInteriorLight(n, 1, nodedef);
1579 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1580 
1581 			float s = BS / 2;
1582 			for(u32 j = 0; j < 2; j++)
1583 			{
1584 				video::S3DVertex vertices[4] =
1585 				{
1586 					video::S3DVertex(-s,-s, 0, 0,0,0, c, 0,1),
1587 					video::S3DVertex( s,-s, 0, 0,0,0, c, 1,1),
1588 					video::S3DVertex( s, s, 0, 0,0,0, c, 1,0),
1589 					video::S3DVertex(-s, s, 0, 0,0,0, c, 0,0),
1590 				};
1591 
1592 				if(j == 0)
1593 				{
1594 					for(u16 i = 0; i < 4; i++)
1595 						vertices[i].Pos.rotateXZBy(46 + n.param2 * 2);
1596 				}
1597 				else if(j == 1)
1598 				{
1599 					for(u16 i = 0; i < 4; i++)
1600 						vertices[i].Pos.rotateXZBy(-44 + n.param2 * 2);
1601 				}
1602 
1603 				for(u16 i = 0; i < 4; i++)
1604 				{
1605 					vertices[i].Pos *= f.visual_scale;
1606 					vertices[i].Pos.Y -= s * (1 - f.visual_scale);
1607 					vertices[i].Pos += intToFloat(p, BS);
1608 				}
1609 
1610 				u16 indices[] = {0, 1, 2, 2, 3, 0};
1611 				// Add to mesh collector
1612 				collector.append(tile, vertices, 4, indices, 6);
1613 			}
1614 		break;}
1615 		case NDT_FIRELIKE:
1616 		{
1617 			TileSpec tile = getNodeTileN(n, p, 0, data);
1618 			tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1619 
1620 			u16 l = getInteriorLight(n, 1, nodedef);
1621 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1622 
1623 			float s = BS/2*f.visual_scale;
1624 
1625 			content_t current = n.getContent();
1626 			content_t n2c;
1627 			MapNode n2;
1628 			v3s16 n2p;
1629 
1630 			static const v3s16 dirs[6] = {
1631 				v3s16( 0, 1, 0),
1632 				v3s16( 0,-1, 0),
1633 				v3s16( 1, 0, 0),
1634 				v3s16(-1, 0, 0),
1635 				v3s16( 0, 0, 1),
1636 				v3s16( 0, 0,-1)
1637 			};
1638 
1639 			int doDraw[6] = {0,0,0,0,0,0};
1640 
1641 			bool drawAllFaces = true;
1642 
1643 			bool drawBottomFacesOnly = false; // Currently unused
1644 
1645 			// Check for adjacent nodes
1646 			for(int i = 0; i < 6; i++)
1647 			{
1648 				n2p = blockpos_nodes + p + dirs[i];
1649 				n2 = data->m_vmanip.getNodeNoEx(n2p);
1650 				n2c = n2.getContent();
1651 				if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1652 					doDraw[i] = 1;
1653 					if(drawAllFaces)
1654 						drawAllFaces = false;
1655 
1656 				}
1657 			}
1658 
1659 			for(int j = 0; j < 6; j++)
1660 			{
1661 				int vOffset = 0; // Vertical offset of faces after rotation
1662 				int hOffset = 4; // Horizontal offset of faces to reach the edge
1663 
1664 				video::S3DVertex vertices[4] =
1665 				{
1666 					video::S3DVertex(-s,-BS/2,      0, 0,0,0, c, 0,1),
1667 					video::S3DVertex( s,-BS/2,      0, 0,0,0, c, 1,1),
1668 					video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
1669 					video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
1670 				};
1671 
1672 				// Calculate which faces should be drawn, (top or sides)
1673 				if(j == 0 && (drawAllFaces || (doDraw[3] == 1 || doDraw[1] == 1)))
1674 				{
1675 					for(int i = 0; i < 4; i++) {
1676 						vertices[i].Pos.rotateXZBy(90 + n.param2 * 2);
1677 						vertices[i].Pos.rotateXYBy(-10);
1678 						vertices[i].Pos.Y -= vOffset;
1679 						vertices[i].Pos.X -= hOffset;
1680 					}
1681 				}
1682 				else if(j == 1 && (drawAllFaces || (doDraw[5] == 1 || doDraw[1] == 1)))
1683 				{
1684 					for(int i = 0; i < 4; i++) {
1685 						vertices[i].Pos.rotateXZBy(180 + n.param2 * 2);
1686 						vertices[i].Pos.rotateYZBy(10);
1687 						vertices[i].Pos.Y -= vOffset;
1688 						vertices[i].Pos.Z -= hOffset;
1689 					}
1690 				}
1691 				else if(j == 2 && (drawAllFaces || (doDraw[2] == 1 || doDraw[1] == 1)))
1692 				{
1693 					for(int i = 0; i < 4; i++) {
1694 						vertices[i].Pos.rotateXZBy(270 + n.param2 * 2);
1695 						vertices[i].Pos.rotateXYBy(10);
1696 						vertices[i].Pos.Y -= vOffset;
1697 						vertices[i].Pos.X += hOffset;
1698 					}
1699 				}
1700 				else if(j == 3 && (drawAllFaces || (doDraw[4] == 1 || doDraw[1] == 1)))
1701 				{
1702 					for(int i = 0; i < 4; i++) {
1703 						vertices[i].Pos.rotateYZBy(-10);
1704 						vertices[i].Pos.Y -= vOffset;
1705 						vertices[i].Pos.Z += hOffset;
1706 					}
1707 				}
1708 
1709 				// Center cross-flames
1710 				else if(j == 4 && (drawAllFaces || doDraw[1] == 1))
1711 				{
1712 					for(int i=0; i<4; i++) {
1713 						vertices[i].Pos.rotateXZBy(45 + n.param2 * 2);
1714 						vertices[i].Pos.Y -= vOffset;
1715 					}
1716 				}
1717 				else if(j == 5 && (drawAllFaces || doDraw[1] == 1))
1718 				{
1719 					for(int i=0; i<4; i++) {
1720 						vertices[i].Pos.rotateXZBy(-45 + n.param2 * 2);
1721 						vertices[i].Pos.Y -= vOffset;
1722 					}
1723 				}
1724 
1725 				// Render flames on bottom
1726 				else if(j == 0 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
1727 				{
1728 					for(int i = 0; i < 4; i++) {
1729 						vertices[i].Pos.rotateYZBy(70);
1730 						vertices[i].Pos.rotateXZBy(90 + n.param2 * 2);
1731 						vertices[i].Pos.Y += 4.84;
1732 						vertices[i].Pos.X -= hOffset+0.7;
1733 					}
1734 				}
1735 				else if(j == 1 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
1736 				{
1737 					for(int i = 0; i < 4; i++) {
1738 						vertices[i].Pos.rotateYZBy(70);
1739 						vertices[i].Pos.rotateXZBy(180 + n.param2 * 2);
1740 						vertices[i].Pos.Y += 4.84;
1741 						vertices[i].Pos.Z -= hOffset+0.7;
1742 					}
1743 				}
1744 				else if(j == 2 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
1745 				{
1746 					for(int i = 0; i < 4; i++) {
1747 						vertices[i].Pos.rotateYZBy(70);
1748 						vertices[i].Pos.rotateXZBy(270 + n.param2 * 2);
1749 						vertices[i].Pos.Y += 4.84;
1750 						vertices[i].Pos.X += hOffset+0.7;
1751 					}
1752 				}
1753 				else if(j == 3 && (drawBottomFacesOnly || (doDraw[0] == 1 && doDraw[1] == 0)))
1754 				{
1755 					for(int i = 0; i < 4; i++) {
1756 						vertices[i].Pos.rotateYZBy(70);
1757 						vertices[i].Pos.Y += 4.84;
1758 						vertices[i].Pos.Z += hOffset+0.7;
1759 					}
1760 				}
1761 				else {
1762 					// Skip faces that aren't adjacent to a node
1763 					continue;
1764 				}
1765 
1766 				for(int i=0; i<4; i++)
1767 				{
1768 					vertices[i].Pos *= f.visual_scale;
1769 					vertices[i].Pos += intToFloat(p, BS);
1770 				}
1771 
1772 				u16 indices[] = {0,1,2,2,3,0};
1773 				// Add to mesh collector
1774 				collector.append(tile, vertices, 4, indices, 6);
1775 			}
1776 		break;}
1777 		case NDT_FENCELIKE:
1778 		{
1779 			TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1780 			TileSpec tile_nocrack = tile;
1781 			tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1782 
1783 			// Put wood the right way around in the posts
1784 			TileSpec tile_rot = tile;
1785 			tile_rot.rotation = 1;
1786 
1787 			u16 l = getInteriorLight(n, 1, nodedef);
1788 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1789 
1790 			const f32 post_rad=(f32)BS/8;
1791 			const f32 bar_rad=(f32)BS/16;
1792 			const f32 bar_len=(f32)(BS/2)-post_rad;
1793 
1794 			v3f pos = intToFloat(p, BS);
1795 
1796 			// The post - always present
1797 			aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
1798 			post.MinEdge += pos;
1799 			post.MaxEdge += pos;
1800 			f32 postuv[24]={
1801 					6/16.,6/16.,10/16.,10/16.,
1802 					6/16.,6/16.,10/16.,10/16.,
1803 					0/16.,0,4/16.,1,
1804 					4/16.,0,8/16.,1,
1805 					8/16.,0,12/16.,1,
1806 					12/16.,0,16/16.,1};
1807 			makeCuboid(&collector, post, &tile_rot, 1, c, postuv);
1808 
1809 			// Now a section of fence, +X, if there's a post there
1810 			v3s16 p2 = p;
1811 			p2.X++;
1812 			MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1813 			const ContentFeatures *f2 = &nodedef->get(n2);
1814 			if(f2->drawtype == NDT_FENCELIKE)
1815 			{
1816 				aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
1817 						bar_len+BS/2,bar_rad+BS/4,bar_rad);
1818 				bar.MinEdge += pos;
1819 				bar.MaxEdge += pos;
1820 				f32 xrailuv[24]={
1821 					0/16.,2/16.,16/16.,4/16.,
1822 					0/16.,4/16.,16/16.,6/16.,
1823 					6/16.,6/16.,8/16.,8/16.,
1824 					10/16.,10/16.,12/16.,12/16.,
1825 					0/16.,8/16.,16/16.,10/16.,
1826 					0/16.,14/16.,16/16.,16/16.};
1827 				makeCuboid(&collector, bar, &tile_nocrack, 1,
1828 						c, xrailuv);
1829 				bar.MinEdge.Y -= BS/2;
1830 				bar.MaxEdge.Y -= BS/2;
1831 				makeCuboid(&collector, bar, &tile_nocrack, 1,
1832 						c, xrailuv);
1833 			}
1834 
1835 			// Now a section of fence, +Z, if there's a post there
1836 			p2 = p;
1837 			p2.Z++;
1838 			n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1839 			f2 = &nodedef->get(n2);
1840 			if(f2->drawtype == NDT_FENCELIKE)
1841 			{
1842 				aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
1843 						bar_rad,bar_rad+BS/4,bar_len+BS/2);
1844 				bar.MinEdge += pos;
1845 				bar.MaxEdge += pos;
1846 				f32 zrailuv[24]={
1847 					3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
1848 					4/16.,1/16.,6/16.,5/16., // for wood texture instead
1849 					0/16.,9/16.,16/16.,11/16.,
1850 					0/16.,6/16.,16/16.,8/16.,
1851 					6/16.,6/16.,8/16.,8/16.,
1852 					10/16.,10/16.,12/16.,12/16.};
1853 				makeCuboid(&collector, bar, &tile_nocrack, 1,
1854 						c, zrailuv);
1855 				bar.MinEdge.Y -= BS/2;
1856 				bar.MaxEdge.Y -= BS/2;
1857 				makeCuboid(&collector, bar, &tile_nocrack, 1,
1858 						c, zrailuv);
1859 			}
1860 		break;}
1861 		case NDT_RAILLIKE:
1862 		{
1863 			recurseRail(p, data, collector);
1864 		break;}
1865 		case NDT_NODEBOX:
1866 		{
1867 			static const v3s16 tile_dirs[6] = {
1868 				v3s16(0, 1, 0),
1869 				v3s16(0, -1, 0),
1870 				v3s16(1, 0, 0),
1871 				v3s16(-1, 0, 0),
1872 				v3s16(0, 0, 1),
1873 				v3s16(0, 0, -1)
1874 			};
1875 			TileSpec tiles[6];
1876 
1877 			u16 l = getInteriorLight(n, 1, nodedef);
1878 			video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1879 
1880 			v3f pos = intToFloat(p, BS);
1881 
1882 			std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
1883 			for(std::vector<aabb3f>::iterator
1884 					i = boxes.begin();
1885 					i != boxes.end(); i++)
1886 			{
1887 			for(int j = 0; j < 6; j++)
1888 				{
1889 				// Handles facedir rotation for textures
1890 				tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
1891 				}
1892 				aabb3f box = *i;
1893 				box.MinEdge += pos;
1894 				box.MaxEdge += pos;
1895 
1896 				f32 temp;
1897 				if (box.MinEdge.X > box.MaxEdge.X)
1898 				{
1899 					temp=box.MinEdge.X;
1900 					box.MinEdge.X=box.MaxEdge.X;
1901 					box.MaxEdge.X=temp;
1902 				}
1903 				if (box.MinEdge.Y > box.MaxEdge.Y)
1904 				{
1905 					temp=box.MinEdge.Y;
1906 					box.MinEdge.Y=box.MaxEdge.Y;
1907 					box.MaxEdge.Y=temp;
1908 				}
1909 				if (box.MinEdge.Z > box.MaxEdge.Z)
1910 				{
1911 					temp=box.MinEdge.Z;
1912 					box.MinEdge.Z=box.MaxEdge.Z;
1913 					box.MaxEdge.Z=temp;
1914 				}
1915 
1916 				//
1917 				// Compute texture coords
1918 				f32 tx1 = (box.MinEdge.X/BS)+0.5;
1919 				f32 ty1 = (box.MinEdge.Y/BS)+0.5;
1920 				f32 tz1 = (box.MinEdge.Z/BS)+0.5;
1921 				f32 tx2 = (box.MaxEdge.X/BS)+0.5;
1922 				f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
1923 				f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
1924 				f32 txc[24] = {
1925 					// up
1926 					tx1, 1-tz2, tx2, 1-tz1,
1927 					// down
1928 					tx1, tz1, tx2, tz2,
1929 					// right
1930 					tz1, 1-ty2, tz2, 1-ty1,
1931 					// left
1932 					1-tz2, 1-ty2, 1-tz1, 1-ty1,
1933 					// back
1934 					1-tx2, 1-ty2, 1-tx1, 1-ty1,
1935 					// front
1936 					tx1, 1-ty2, tx2, 1-ty1,
1937 				};
1938 				makeCuboid(&collector, box, tiles, 6, c, txc);
1939 			}
1940 		break;}
1941 		case NDT_MESH:
1942 		{
1943 			v3f pos = intToFloat(p, BS);
1944 			video::SColor c = MapBlock_LightColor(255, getInteriorLight(n, 1, nodedef), f.light_source);
1945 
1946 			u8 facedir = 0;
1947 			if (f.param_type_2 == CPT2_FACEDIR) {
1948 				facedir = n.getFaceDir(nodedef);
1949 				if (facedir >= 24)
1950 					facedir = 0;
1951 			} else if (f.param_type_2 == CPT2_WALLMOUNTED) {
1952 				//convert wallmounted to 6dfacedir.
1953 				//when cache enabled, it is already converted
1954 				facedir = n.getWallMounted(nodedef);
1955 				if (!enable_mesh_cache) {
1956 					static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
1957 					facedir = wm_to_6d[facedir];
1958 				}
1959 			}
1960 
1961 			if (f.mesh_ptr[facedir]) {
1962 				// use cached meshes
1963 				for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) {
1964 					scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j);
1965 					collector.append(getNodeTileN(n, p, j, data),
1966 						(video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
1967 						buf->getIndices(), buf->getIndexCount(), pos, c);
1968 				}
1969 			} else if (f.mesh_ptr[0]) {
1970 				// no cache, clone and rotate mesh
1971 				scene::IMesh* mesh = cloneMesh(f.mesh_ptr[0]);
1972 				rotateMeshBy6dFacedir(mesh, facedir);
1973 				recalculateBoundingBox(mesh);
1974 				meshmanip->recalculateNormals(mesh, true, false);
1975 				for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) {
1976 					scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1977 					collector.append(getNodeTileN(n, p, j, data),
1978 						(video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
1979 						buf->getIndices(), buf->getIndexCount(), pos, c);
1980 				}
1981 				mesh->drop();
1982 			}
1983 		break;}
1984 		}
1985 	}
1986 }
1987 
1988