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