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