3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_mapblock.h"
21 #include "util/numeric.h"
22 #include "util/directiontables.h"
23 #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
26 #include "client/tile.h"
28 #include <IMeshManipulator.h>
35 // collector - the MeshCollector for the resulting polygons
36 // box - the position and size of the box
37 // tiles - the tiles (materials) to use (for all 6 faces)
38 // tilecount - number of entries in tiles, 1<=tilecount<=6
39 // c - colors of the cuboid's six sides
40 // txc - texture coordinates - this is a list of texture coordinates
41 // for the opposite corners of each face - therefore, there
42 // should be (2+2)*6=24 values in the list. Alternatively,
43 // pass NULL to use the entire texture for each face. The
44 // order of the faces in the list is up-down-right-left-back-
45 // front (compatible with ContentFeatures). If you specified
46 // 0,0,1,1 for each face, that would be the same as
48 // light source - if greater than zero, the box's faces will not be shaded
49 void makeCuboid(MeshCollector *collector, const aabb3f &box,
50 TileSpec *tiles, int tilecount, const video::SColor *c,
51 const f32* txc, const u8 light_source)
53 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
55 v3f min = box.MinEdge;
56 v3f max = box.MaxEdge;
59 static const f32 txc_default[24] = {
70 video::SColor c1 = c[0];
71 video::SColor c2 = c[1];
72 video::SColor c3 = c[2];
73 video::SColor c4 = c[3];
74 video::SColor c5 = c[4];
75 video::SColor c6 = c[5];
77 applyFacesShading(c1, v3f(0, 1, 0));
78 applyFacesShading(c2, v3f(0, -1, 0));
79 applyFacesShading(c3, v3f(1, 0, 0));
80 applyFacesShading(c4, v3f(-1, 0, 0));
81 applyFacesShading(c5, v3f(0, 0, 1));
82 applyFacesShading(c6, v3f(0, 0, -1));
85 video::S3DVertex vertices[24] =
88 video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c1, txc[0],txc[1]),
89 video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c1, txc[2],txc[1]),
90 video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c1, txc[2],txc[3]),
91 video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c1, txc[0],txc[3]),
93 video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c2, txc[4],txc[5]),
94 video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c2, txc[6],txc[5]),
95 video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c2, txc[6],txc[7]),
96 video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c2, txc[4],txc[7]),
98 video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c3, txc[ 8],txc[9]),
99 video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c3, txc[10],txc[9]),
100 video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c3, txc[10],txc[11]),
101 video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c3, txc[ 8],txc[11]),
103 video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c4, txc[12],txc[13]),
104 video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c4, txc[14],txc[13]),
105 video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c4, txc[14],txc[15]),
106 video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c4, txc[12],txc[15]),
108 video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c5, txc[16],txc[17]),
109 video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c5, txc[18],txc[17]),
110 video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c5, txc[18],txc[19]),
111 video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c5, txc[16],txc[19]),
113 video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c6, txc[20],txc[21]),
114 video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c6, txc[22],txc[21]),
115 video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c6, txc[22],txc[23]),
116 video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c6, txc[20],txc[23]),
119 for(int i = 0; i < 6; i++)
121 switch (tiles[MYMIN(i, tilecount-1)].rotation)
126 for (int x = 0; x < 4; x++)
127 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
130 for (int x = 0; x < 4; x++)
131 vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
134 for (int x = 0; x < 4; x++)
135 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
138 for (int x = 0; x < 4; x++){
139 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
140 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
144 for (int x = 0; x < 4; x++){
145 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
146 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
150 for (int x = 0; x < 4; x++){
151 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
152 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
156 for (int x = 0; x < 4; x++){
157 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
158 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
162 for (int x = 0; x < 4; x++){
163 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
167 for (int x = 0; x < 4; x++){
168 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
175 u16 indices[] = {0,1,2,2,3,0};
176 // Add to mesh collector
177 for (s32 j = 0; j < 24; j += 4) {
178 int tileindex = MYMIN(j / 4, tilecount - 1);
179 collector->append(tiles[tileindex], vertices + j, 4, indices, 6);
184 // collector - the MeshCollector for the resulting polygons
185 // box - the position and size of the box
186 // tiles - the tiles (materials) to use (for all 6 faces)
187 // tilecount - number of entries in tiles, 1<=tilecount<=6
188 // c - color of the cuboid
189 // txc - texture coordinates - this is a list of texture coordinates
190 // for the opposite corners of each face - therefore, there
191 // should be (2+2)*6=24 values in the list. Alternatively,
192 // pass NULL to use the entire texture for each face. The
193 // order of the faces in the list is up-down-right-left-back-
194 // front (compatible with ContentFeatures). If you specified
195 // 0,0,1,1 for each face, that would be the same as
197 // light source - if greater than zero, the box's faces will not be shaded
198 void makeCuboid(MeshCollector *collector, const aabb3f &box, TileSpec *tiles,
199 int tilecount, const video::SColor &c, const f32* txc,
200 const u8 light_source)
202 video::SColor color[6];
203 for (u8 i = 0; i < 6; i++)
205 makeCuboid(collector, box, tiles, tilecount, color, txc, light_source);
208 static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
209 MeshMakeData *data, MapNode n, int v, int *neighbors)
211 MapNode n2 = data->m_vmanip.getNodeNoEx(p);
212 if (nodedef->nodeboxConnects(n, n2, v))
216 // For use in mapblock_mesh_generate_special
217 // X,Y,Z of position must be -1,0,1
218 // This expression is a simplification of
219 // 3 * 3 * (pos.X + 1) + 3 * (pos.Y + 1) + (pos.Z + 1)
220 static inline int NeighborToIndex(const v3s16 &pos)
222 return 9 * pos.X + 3 * pos.Y + pos.Z + 13;
226 * Returns the i-th special tile for a map node.
228 static TileSpec getSpecialTile(const ContentFeatures &f,
229 const MapNode &n, u8 i)
231 TileSpec copy = f.special_tiles[i];
233 n.getColor(f, ©.color);
238 TODO: Fix alpha blending for special nodes
239 Currently only the last element rendered is blended correct
241 void mapblock_mesh_generate_special(MeshMakeData *data,
242 MeshCollector &collector)
244 INodeDefManager *nodedef = data->m_client->ndef();
245 scene::ISceneManager* smgr = data->m_client->getSceneManager();
246 scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
249 //TimeTaker timer("mapblock_mesh_generate_special()");
254 bool enable_mesh_cache = g_settings->getBool("enable_mesh_cache");
256 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
258 for(s16 z = 0; z < MAP_BLOCKSIZE; z++)
259 for(s16 y = 0; y < MAP_BLOCKSIZE; y++)
260 for(s16 x = 0; x < MAP_BLOCKSIZE; x++)
264 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
265 const ContentFeatures &f = nodedef->get(n);
267 // Only solidness=0 stuff is drawn here
273 infostream << "Got " << f.drawtype << std::endl;
274 FATAL_ERROR("Unknown drawtype");
281 Add water sources to mesh if using new style
283 TileSpec tile_liquid = getSpecialTile(f, n, 0);
284 TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
285 u16 l = getInteriorLight(n, 0, nodedef);
286 video::SColor c1 = encode_light_and_color(l,
287 tile_liquid.color, f.light_source);
288 video::SColor c2 = encode_light_and_color(l,
289 tile_liquid_bfculled.color, f.light_source);
291 bool top_is_same_liquid = false;
292 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
293 content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
294 content_t c_source = nodedef->getId(f.liquid_alternative_source);
295 if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
296 top_is_same_liquid = true;
301 v3s16 side_dirs[4] = {
307 for(u32 i=0; i<4; i++)
309 v3s16 dir = side_dirs[i];
311 MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
312 content_t neighbor_content = neighbor.getContent();
313 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
314 MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
315 content_t n_top_c = n_top.getContent();
317 if(neighbor_content == CONTENT_IGNORE)
321 If our topside is liquid and neighbor's topside
322 is liquid, don't draw side face
324 if(top_is_same_liquid && (n_top_c == c_flowing ||
325 n_top_c == c_source || n_top_c == CONTENT_IGNORE))
328 // Don't draw face if neighbor is blocking the view
329 if(n_feat.solidness == 2)
332 bool neighbor_is_same_liquid = (neighbor_content == c_source
333 || neighbor_content == c_flowing);
335 // Don't draw any faces if neighbor same is liquid and top is
337 if(neighbor_is_same_liquid && !top_is_same_liquid)
340 // Use backface culled material if neighbor doesn't have a
342 const TileSpec *current_tile = &tile_liquid;
343 video::SColor *c = &c1;
344 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) {
345 current_tile = &tile_liquid_bfculled;
349 video::S3DVertex vertices[4] =
351 video::S3DVertex(-BS/2,0,BS/2,0,0,0, *c, 0,1),
352 video::S3DVertex(BS/2,0,BS/2,0,0,0, *c, 1,1),
353 video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0),
354 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0),
358 If our topside is liquid, set upper border of face
359 at upper border of node
361 if (top_is_same_liquid) {
362 vertices[2].Pos.Y = 0.5 * BS;
363 vertices[3].Pos.Y = 0.5 * BS;
366 Otherwise upper position of face is liquid level
368 vertices[2].Pos.Y = 0.5 * BS;
369 vertices[3].Pos.Y = 0.5 * BS;
372 If neighbor is liquid, lower border of face is liquid level
374 if (neighbor_is_same_liquid) {
375 vertices[0].Pos.Y = 0.5 * BS;
376 vertices[1].Pos.Y = 0.5 * BS;
379 If neighbor is not liquid, lower border of face is
382 vertices[0].Pos.Y = -0.5 * BS;
383 vertices[1].Pos.Y = -0.5 * BS;
386 for(s32 j=0; j<4; j++)
388 if(dir == v3s16(0,0,1))
389 vertices[j].Pos.rotateXZBy(0);
390 if(dir == v3s16(0,0,-1))
391 vertices[j].Pos.rotateXZBy(180);
392 if(dir == v3s16(-1,0,0))
393 vertices[j].Pos.rotateXZBy(90);
394 if(dir == v3s16(1,0,-0))
395 vertices[j].Pos.rotateXZBy(-90);
397 // Do this to not cause glitches when two liquids are
399 /*if(neighbor_is_same_liquid == false){
400 vertices[j].Pos.X *= 0.98;
401 vertices[j].Pos.Z *= 0.98;
404 vertices[j].Pos += intToFloat(p, BS);
407 u16 indices[] = {0,1,2,2,3,0};
408 // Add to mesh collector
409 collector.append(*current_tile, vertices, 4, indices, 6);
415 if(top_is_same_liquid)
418 video::S3DVertex vertices[4] =
420 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1),
421 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1),
422 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0),
423 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0),
426 v3f offset(p.X * BS, (p.Y + 0.5) * BS, p.Z * BS);
427 for(s32 i=0; i<4; i++)
429 vertices[i].Pos += offset;
432 u16 indices[] = {0,1,2,2,3,0};
433 // Add to mesh collector
434 collector.append(tile_liquid, vertices, 4, indices, 6);
436 case NDT_FLOWINGLIQUID:
439 Add flowing liquid to mesh
441 TileSpec tile_liquid = getSpecialTile(f, n, 0);
442 TileSpec tile_liquid_bfculled = getSpecialTile(f, n, 1);
444 bool top_is_same_liquid = false;
445 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
446 content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
447 content_t c_source = nodedef->getId(f.liquid_alternative_source);
448 if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
449 top_is_same_liquid = true;
452 // If this liquid emits light and doesn't contain light, draw
453 // it at what it emits, for an increased effect
454 u8 light_source = nodedef->get(n).light_source;
455 if(light_source != 0){
456 l = decode_light(light_source);
459 // Use the light of the node on top if possible
460 else if(nodedef->get(ntop).param_type == CPT_LIGHT)
461 l = getInteriorLight(ntop, 0, nodedef);
462 // Otherwise use the light of this node (the liquid)
464 l = getInteriorLight(n, 0, nodedef);
465 video::SColor c1 = encode_light_and_color(l,
466 tile_liquid.color, f.light_source);
467 video::SColor c2 = encode_light_and_color(l,
468 tile_liquid_bfculled.color, f.light_source);
470 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
472 // Neighbor liquid levels (key = relative position)
473 // Includes current node
475 struct NeighborData {
480 NeighborData neighbor_data_matrix[27];
482 const u8 neighborflag_top_is_same_liquid = 0x01;
483 v3s16 neighbor_dirs[9] = {
494 for(u32 i=0; i<9; i++)
496 content_t content = CONTENT_AIR;
497 float level = -0.5 * BS;
500 v3s16 p2 = p + neighbor_dirs[i];
501 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
502 if(n2.getContent() != CONTENT_IGNORE)
504 content = n2.getContent();
506 if(n2.getContent() == c_source)
508 else if(n2.getContent() == c_flowing){
509 u8 liquid_level = (n2.param2&LIQUID_LEVEL_MASK);
510 if (liquid_level <= LIQUID_LEVEL_MAX+1-range)
513 liquid_level -= (LIQUID_LEVEL_MAX+1-range);
514 level = (-0.5 + ((float)liquid_level + 0.5) / (float)range) * BS;
517 // Check node above neighbor.
518 // NOTE: This doesn't get executed if neighbor
521 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
522 if(n2.getContent() == c_source ||
523 n2.getContent() == c_flowing)
524 flags |= neighborflag_top_is_same_liquid;
527 NeighborData &neighbor_data =
528 neighbor_data_matrix[NeighborToIndex(neighbor_dirs[i])];
530 neighbor_data.level = level;
531 neighbor_data.content = content;
532 neighbor_data.flags = flags;
535 // Corner heights (average between four liquids)
536 f32 corner_levels[4];
538 v3s16 halfdirs[4] = {
544 for(u32 i=0; i<4; i++)
546 v3s16 cornerdir = halfdirs[i];
547 float cornerlevel = 0;
550 for(u32 j=0; j<4; j++)
552 v3s16 neighbordir = cornerdir - halfdirs[j];
554 NeighborData &neighbor_data =
555 neighbor_data_matrix[NeighborToIndex(neighbordir)];
556 content_t content = neighbor_data.content;
557 // If top is liquid, draw starting from top of node
558 if (neighbor_data.flags & neighborflag_top_is_same_liquid)
560 cornerlevel = 0.5*BS;
564 // Source is always the same height
565 else if(content == c_source)
567 cornerlevel = 0.5 * BS;
571 // Flowing liquid has level information
572 else if(content == c_flowing)
574 cornerlevel += neighbor_data.level;
577 else if(content == CONTENT_AIR)
583 cornerlevel = -0.5*BS+0.2;
584 else if(valid_count > 0)
585 cornerlevel /= valid_count;
586 corner_levels[i] = cornerlevel;
593 v3s16 side_dirs[4] = {
599 s16 side_corners[4][2] = {
605 for(u32 i=0; i<4; i++)
607 v3s16 dir = side_dirs[i];
609 NeighborData& neighbor_data =
610 neighbor_data_matrix[NeighborToIndex(dir)];
612 If our topside is liquid and neighbor's topside
613 is liquid, don't draw side face
615 if (top_is_same_liquid &&
616 (neighbor_data.flags & neighborflag_top_is_same_liquid))
619 content_t neighbor_content = neighbor_data.content;
620 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
622 // Don't draw face if neighbor is blocking the view
623 if(n_feat.solidness == 2)
626 bool neighbor_is_same_liquid = (neighbor_content == c_source
627 || neighbor_content == c_flowing);
629 // Don't draw any faces if neighbor same is liquid and top is
631 if(neighbor_is_same_liquid == true
632 && top_is_same_liquid == false)
635 // Use backface culled material if neighbor doesn't have a
637 const TileSpec *current_tile = &tile_liquid;
638 video::SColor *c = &c1;
639 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) {
640 current_tile = &tile_liquid_bfculled;
644 video::S3DVertex vertices[4] =
646 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,1),
647 video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,1),
648 video::S3DVertex(BS/2,0,BS/2, 0,0,0, *c, 1,0),
649 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, *c, 0,0),
653 If our topside is liquid, set upper border of face
654 at upper border of node
656 if(top_is_same_liquid)
658 vertices[2].Pos.Y = 0.5*BS;
659 vertices[3].Pos.Y = 0.5*BS;
662 Otherwise upper position of face is corner levels
666 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
667 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
671 If neighbor is liquid, lower border of face is corner
674 if(neighbor_is_same_liquid)
676 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
677 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
680 If neighbor is not liquid, lower border of face is
685 vertices[0].Pos.Y = -0.5*BS;
686 vertices[1].Pos.Y = -0.5*BS;
689 for(s32 j=0; j<4; j++)
691 if(dir == v3s16(0,0,1))
692 vertices[j].Pos.rotateXZBy(0);
693 if(dir == v3s16(0,0,-1))
694 vertices[j].Pos.rotateXZBy(180);
695 if(dir == v3s16(-1,0,0))
696 vertices[j].Pos.rotateXZBy(90);
697 if(dir == v3s16(1,0,-0))
698 vertices[j].Pos.rotateXZBy(-90);
700 // Do this to not cause glitches when two liquids are
702 /*if(neighbor_is_same_liquid == false){
703 vertices[j].Pos.X *= 0.98;
704 vertices[j].Pos.Z *= 0.98;
707 vertices[j].Pos += intToFloat(p, BS);
710 u16 indices[] = {0,1,2,2,3,0};
711 // Add to mesh collector
712 collector.append(*current_tile, vertices, 4, indices, 6);
716 Generate top side, if appropriate
719 if(top_is_same_liquid == false)
721 video::S3DVertex vertices[4] =
723 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c1, 0,1),
724 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c1, 1,1),
725 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c1, 1,0),
726 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c1, 0,0),
729 // To get backface culling right, the vertices need to go
730 // clockwise around the front of the face. And we happened to
731 // calculate corner levels in exact reverse order.
732 s32 corner_resolve[4] = {3,2,1,0};
734 for(s32 i=0; i<4; i++)
736 //vertices[i].Pos.Y += liquid_level;
737 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
738 s32 j = corner_resolve[i];
739 vertices[i].Pos.Y += corner_levels[j];
740 vertices[i].Pos += intToFloat(p, BS);
743 // Default downwards-flowing texture animation goes from
744 // -Z towards +Z, thus the direction is +Z.
745 // Rotate texture to make animation go in flow direction
746 // Positive if liquid moves towards +Z
747 f32 dz = (corner_levels[side_corners[3][0]] +
748 corner_levels[side_corners[3][1]]) -
749 (corner_levels[side_corners[2][0]] +
750 corner_levels[side_corners[2][1]]);
751 // Positive if liquid moves towards +X
752 f32 dx = (corner_levels[side_corners[1][0]] +
753 corner_levels[side_corners[1][1]]) -
754 (corner_levels[side_corners[0][0]] +
755 corner_levels[side_corners[0][1]]);
756 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG ;
757 v2f tcoord_center(0.5, 0.5);
758 v2f tcoord_translate(
759 blockpos_nodes.Z + z,
760 blockpos_nodes.X + x);
761 tcoord_translate.rotateBy(tcoord_angle);
762 tcoord_translate.X -= floor(tcoord_translate.X);
763 tcoord_translate.Y -= floor(tcoord_translate.Y);
765 for(s32 i=0; i<4; i++)
767 vertices[i].TCoords.rotateBy(
770 vertices[i].TCoords += tcoord_translate;
773 v2f t = vertices[0].TCoords;
774 vertices[0].TCoords = vertices[2].TCoords;
775 vertices[2].TCoords = t;
777 u16 indices[] = {0,1,2,2,3,0};
778 // Add to mesh collector
779 collector.append(tile_liquid, vertices, 4, indices, 6);
784 TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
786 u16 l = getInteriorLight(n, 1, nodedef);
787 video::SColor c = encode_light_and_color(l, tile.color,
789 for(u32 j=0; j<6; j++)
791 // Check this neighbor
792 v3s16 dir = g_6dirs[j];
793 v3s16 n2p = blockpos_nodes + p + dir;
794 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
795 // Don't make face if neighbor is of same type
796 if(n2.getContent() == n.getContent())
800 applyFacesShading(c2, v3f(dir.X, dir.Y, dir.Z));
804 video::S3DVertex vertices[4] = {
805 video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,1),
806 video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,1),
807 video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 0,0),
808 video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c2, 1,0),
811 // Rotations in the g_6dirs format
813 for(u16 i=0; i<4; i++)
814 vertices[i].Pos.rotateXZBy(0);
815 else if(j == 1) // Y+
816 for(u16 i=0; i<4; i++)
817 vertices[i].Pos.rotateYZBy(-90);
818 else if(j == 2) // X+
819 for(u16 i=0; i<4; i++)
820 vertices[i].Pos.rotateXZBy(-90);
821 else if(j == 3) // Z-
822 for(u16 i=0; i<4; i++)
823 vertices[i].Pos.rotateXZBy(180);
824 else if(j == 4) // Y-
825 for(u16 i=0; i<4; i++)
826 vertices[i].Pos.rotateYZBy(90);
827 else if(j == 5) // X-
828 for(u16 i=0; i<4; i++)
829 vertices[i].Pos.rotateXZBy(90);
831 for(u16 i=0; i<4; i++){
832 vertices[i].Pos += intToFloat(p, BS);
835 u16 indices[] = {0,1,2,2,3,0};
836 // Add to mesh collector
837 collector.append(tile, vertices, 4, indices, 6);
840 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
841 // This is always pre-converted to something else
842 FATAL_ERROR("NDT_GLASSLIKE_FRAMED_OPTIONAL not pre-converted as expected");
844 case NDT_GLASSLIKE_FRAMED:
846 static const v3s16 dirs[6] = {
855 u16 l = getInteriorLight(n, 1, nodedef);
858 for (i = 0; i < 6; i++)
859 tiles[i] = getNodeTile(n, p, dirs[i], data);
861 video::SColor tile0color = encode_light_and_color(l,
862 tiles[0].color, f.light_source);
863 video::SColor tile0colors[6];
864 for (i = 0; i < 6; i++)
865 tile0colors[i] = tile0color;
867 TileSpec glass_tiles[6];
868 video::SColor glasscolor[6];
869 if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
870 glass_tiles[0] = tiles[2];
871 glass_tiles[1] = tiles[3];
872 glass_tiles[2] = tiles[1];
873 glass_tiles[3] = tiles[1];
874 glass_tiles[4] = tiles[1];
875 glass_tiles[5] = tiles[1];
877 for (i = 0; i < 6; i++)
878 glass_tiles[i] = tiles[1];
880 for (i = 0; i < 6; i++)
881 glasscolor[i] = encode_light_and_color(l, glass_tiles[i].color,
884 u8 param2 = n.getParam2();
885 bool H_merge = ! bool(param2 & 128);
886 bool V_merge = ! bool(param2 & 64);
887 param2 = param2 & 63;
889 v3f pos = intToFloat(p, BS);
890 static const float a = BS / 2;
891 static const float g = a - 0.003;
892 static const float b = .876 * ( BS / 2 );
894 static const aabb3f frame_edges[12] = {
895 aabb3f( b, b,-a, a, a, a), // y+
896 aabb3f(-a, b,-a,-b, a, a), // y+
897 aabb3f( b,-a,-a, a,-b, a), // y-
898 aabb3f(-a,-a,-a,-b,-b, a), // y-
899 aabb3f( b,-a, b, a, a, a), // x+
900 aabb3f( b,-a,-a, a, a,-b), // x+
901 aabb3f(-a,-a, b,-b, a, a), // x-
902 aabb3f(-a,-a,-a,-b, a,-b), // x-
903 aabb3f(-a, b, b, a, a, a), // z+
904 aabb3f(-a,-a, b, a,-b, a), // z+
905 aabb3f(-a,-a,-a, a,-b,-b), // z-
906 aabb3f(-a, b,-a, a, a,-b) // z-
908 static const aabb3f glass_faces[6] = {
909 aabb3f(-g, g,-g, g, g, g), // y+
910 aabb3f(-g,-g,-g, g,-g, g), // y-
911 aabb3f( g,-g,-g, g, g, g), // x+
912 aabb3f(-g,-g,-g,-g, g, g), // x-
913 aabb3f(-g,-g, g, g, g, g), // z+
914 aabb3f(-g,-g,-g, g, g,-g) // z-
917 // table of node visible faces, 0 = invisible
918 int visible_faces[6] = {0,0,0,0,0,0};
920 // table of neighbours, 1 = same type, checked with g_26dirs
921 int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
923 // g_26dirs to check when only horizontal merge is allowed
924 int nb_H_dirs[8] = {0,2,3,5,10,11,12,13};
926 content_t current = n.getContent();
931 // neighbours checks for frames visibility
933 if (!H_merge && V_merge) {
934 n2p = blockpos_nodes + p + g_26dirs[1];
935 n2 = data->m_vmanip.getNodeNoEx(n2p);
936 n2c = n2.getContent();
937 if (n2c == current || n2c == CONTENT_IGNORE)
939 n2p = blockpos_nodes + p + g_26dirs[4];
940 n2 = data->m_vmanip.getNodeNoEx(n2p);
941 n2c = n2.getContent();
942 if (n2c == current || n2c == CONTENT_IGNORE)
944 } else if (H_merge && !V_merge) {
945 for(i = 0; i < 8; i++) {
946 n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]];
947 n2 = data->m_vmanip.getNodeNoEx(n2p);
948 n2c = n2.getContent();
949 if (n2c == current || n2c == CONTENT_IGNORE)
950 nb[nb_H_dirs[i]] = 1;
952 } else if (H_merge && V_merge) {
953 for(i = 0; i < 18; i++) {
954 n2p = blockpos_nodes + p + g_26dirs[i];
955 n2 = data->m_vmanip.getNodeNoEx(n2p);
956 n2c = n2.getContent();
957 if (n2c == current || n2c == CONTENT_IGNORE)
962 // faces visibility checks
965 visible_faces[0] = 1;
966 visible_faces[1] = 1;
968 for(i = 0; i < 2; i++) {
969 n2p = blockpos_nodes + p + dirs[i];
970 n2 = data->m_vmanip.getNodeNoEx(n2p);
971 n2c = n2.getContent();
973 visible_faces[i] = 1;
978 visible_faces[2] = 1;
979 visible_faces[3] = 1;
980 visible_faces[4] = 1;
981 visible_faces[5] = 1;
983 for(i = 2; i < 6; i++) {
984 n2p = blockpos_nodes + p + dirs[i];
985 n2 = data->m_vmanip.getNodeNoEx(n2p);
986 n2c = n2.getContent();
988 visible_faces[i] = 1;
992 static const u8 nb_triplet[12*3] = {
993 1,2, 7, 1,5, 6, 4,2,15, 4,5,14,
994 2,0,11, 2,3,13, 5,0,10, 5,3,12,
995 0,1, 8, 0,4,16, 3,4,17, 3,1, 9
998 f32 tx1, ty1, tz1, tx2, ty2, tz2;
1001 for(i = 0; i < 12; i++)
1004 if (nb[nb_triplet[i*3+2]])
1005 edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]];
1007 edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]];
1010 box = frame_edges[i];
1013 tx1 = (box.MinEdge.X / BS) + 0.5;
1014 ty1 = (box.MinEdge.Y / BS) + 0.5;
1015 tz1 = (box.MinEdge.Z / BS) + 0.5;
1016 tx2 = (box.MaxEdge.X / BS) + 0.5;
1017 ty2 = (box.MaxEdge.Y / BS) + 0.5;
1018 tz2 = (box.MaxEdge.Z / BS) + 0.5;
1020 tx1, 1-tz2, tx2, 1-tz1,
1022 tz1, 1-ty2, tz2, 1-ty1,
1023 1-tz2, 1-ty2, 1-tz1, 1-ty1,
1024 1-tx2, 1-ty2, 1-tx1, 1-ty1,
1025 tx1, 1-ty2, tx2, 1-ty1,
1027 makeCuboid(&collector, box, &tiles[0], 1, tile0colors,
1028 txc1, f.light_source);
1031 for(i = 0; i < 6; i++)
1033 if (!visible_faces[i])
1035 box = glass_faces[i];
1038 tx1 = (box.MinEdge.X / BS) + 0.5;
1039 ty1 = (box.MinEdge.Y / BS) + 0.5;
1040 tz1 = (box.MinEdge.Z / BS) + 0.5;
1041 tx2 = (box.MaxEdge.X / BS) + 0.5;
1042 ty2 = (box.MaxEdge.Y / BS) + 0.5;
1043 tz2 = (box.MaxEdge.Z / BS) + 0.5;
1045 tx1, 1-tz2, tx2, 1-tz1,
1047 tz1, 1-ty2, tz2, 1-ty1,
1048 1-tz2, 1-ty2, 1-tz1, 1-ty1,
1049 1-tx2, 1-ty2, 1-tx1, 1-ty1,
1050 tx1, 1-ty2, tx2, 1-ty1,
1052 makeCuboid(&collector, box, &glass_tiles[i], 1, glasscolor,
1053 txc2, f.light_source);
1056 if (param2 > 0 && f.special_tiles[0].texture) {
1057 // Interior volume level is in range 0 .. 63,
1058 // convert it to -0.5 .. 0.5
1059 float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0);
1060 TileSpec tile=getSpecialTile(f, n, 0);
1061 video::SColor special_color = encode_light_and_color(l,
1062 tile.color, f.light_source);
1063 TileSpec interior_tiles[6];
1064 for (i = 0; i < 6; i++)
1065 interior_tiles[i] = tile;
1067 float offset = 0.003;
1068 box = aabb3f(visible_faces[3] ? -b : -a + offset,
1069 visible_faces[1] ? -b : -a + offset,
1070 visible_faces[5] ? -b : -a + offset,
1071 visible_faces[2] ? b : a - offset,
1072 visible_faces[0] ? b * vlev : a * vlev - offset,
1073 visible_faces[4] ? b : a - offset);
1076 tx1 = (box.MinEdge.X / BS) + 0.5;
1077 ty1 = (box.MinEdge.Y / BS) + 0.5;
1078 tz1 = (box.MinEdge.Z / BS) + 0.5;
1079 tx2 = (box.MaxEdge.X / BS) + 0.5;
1080 ty2 = (box.MaxEdge.Y / BS) + 0.5;
1081 tz2 = (box.MaxEdge.Z / BS) + 0.5;
1083 tx1, 1-tz2, tx2, 1-tz1,
1085 tz1, 1-ty2, tz2, 1-ty1,
1086 1-tz2, 1-ty2, 1-tz1, 1-ty1,
1087 1-tx2, 1-ty2, 1-tx1, 1-ty1,
1088 tx1, 1-ty2, tx2, 1-ty1,
1090 makeCuboid(&collector, box, interior_tiles, 6, special_color,
1091 txc3, f.light_source);
1096 TileSpec tile_leaves = getNodeTile(n, p,
1097 v3s16(0,0,0), data);
1098 u16 l = getInteriorLight(n, 1, nodedef);
1099 video::SColor c = encode_light_and_color(l,
1100 tile_leaves.color, f.light_source);
1102 v3f pos = intToFloat(p, BS);
1103 aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
1106 makeCuboid(&collector, box, &tile_leaves, 1, c, NULL,
1109 case NDT_ALLFACES_OPTIONAL:
1110 // This is always pre-converted to something else
1111 FATAL_ERROR("NDT_ALLFACES_OPTIONAL not pre-converted");
1115 v3s16 dir = n.getWallMountedDir(nodedef);
1118 if(dir == v3s16(0,-1,0)){
1119 tileindex = 0; // floor
1120 } else if(dir == v3s16(0,1,0)){
1121 tileindex = 1; // ceiling
1122 // For backwards compatibility
1123 } else if(dir == v3s16(0,0,0)){
1124 tileindex = 0; // floor
1126 tileindex = 2; // side
1129 TileSpec tile = getNodeTileN(n, p, tileindex, data);
1130 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1131 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1133 u16 l = getInteriorLight(n, 1, nodedef);
1134 video::SColor c = encode_light_and_color(l, tile.color,
1137 float s = BS/2*f.visual_scale;
1138 // Wall at X+ of node
1139 video::S3DVertex vertices[4] =
1141 video::S3DVertex(-s,-s,0, 0,0,0, c, 0,1),
1142 video::S3DVertex( s,-s,0, 0,0,0, c, 1,1),
1143 video::S3DVertex( s, s,0, 0,0,0, c, 1,0),
1144 video::S3DVertex(-s, s,0, 0,0,0, c, 0,0),
1147 for(s32 i=0; i<4; i++)
1149 if(dir == v3s16(1,0,0))
1150 vertices[i].Pos.rotateXZBy(0);
1151 if(dir == v3s16(-1,0,0))
1152 vertices[i].Pos.rotateXZBy(180);
1153 if(dir == v3s16(0,0,1))
1154 vertices[i].Pos.rotateXZBy(90);
1155 if(dir == v3s16(0,0,-1))
1156 vertices[i].Pos.rotateXZBy(-90);
1157 if(dir == v3s16(0,-1,0))
1158 vertices[i].Pos.rotateXZBy(45);
1159 if(dir == v3s16(0,1,0))
1160 vertices[i].Pos.rotateXZBy(-45);
1162 vertices[i].Pos += intToFloat(p, BS);
1165 u16 indices[] = {0,1,2,2,3,0};
1166 // Add to mesh collector
1167 collector.append(tile, vertices, 4, indices, 6);
1171 TileSpec tile = getNodeTileN(n, p, 0, data);
1172 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1173 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1175 u16 l = getInteriorLight(n, 0, nodedef);
1176 video::SColor c = encode_light_and_color(l, tile.color,
1179 float d = (float)BS/16;
1180 float s = BS/2*f.visual_scale;
1181 // Wall at X+ of node
1182 video::S3DVertex vertices[4] =
1184 video::S3DVertex(BS/2-d, s, s, 0,0,0, c, 0,0),
1185 video::S3DVertex(BS/2-d, s, -s, 0,0,0, c, 1,0),
1186 video::S3DVertex(BS/2-d, -s, -s, 0,0,0, c, 1,1),
1187 video::S3DVertex(BS/2-d, -s, s, 0,0,0, c, 0,1),
1190 v3s16 dir = n.getWallMountedDir(nodedef);
1192 for(s32 i=0; i<4; i++)
1194 if(dir == v3s16(1,0,0))
1195 vertices[i].Pos.rotateXZBy(0);
1196 if(dir == v3s16(-1,0,0))
1197 vertices[i].Pos.rotateXZBy(180);
1198 if(dir == v3s16(0,0,1))
1199 vertices[i].Pos.rotateXZBy(90);
1200 if(dir == v3s16(0,0,-1))
1201 vertices[i].Pos.rotateXZBy(-90);
1202 if(dir == v3s16(0,-1,0))
1203 vertices[i].Pos.rotateXYBy(-90);
1204 if(dir == v3s16(0,1,0))
1205 vertices[i].Pos.rotateXYBy(90);
1207 vertices[i].Pos += intToFloat(p, BS);
1210 u16 indices[] = {0,1,2,2,3,0};
1211 // Add to mesh collector
1212 collector.append(tile, vertices, 4, indices, 6);
1216 PseudoRandom rng(x<<8 | z | y<<16);
1218 TileSpec tile = getNodeTileN(n, p, 0, data);
1219 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1221 u16 l = getInteriorLight(n, 1, nodedef);
1222 video::SColor c = encode_light_and_color(l, tile.color,
1225 float s = BS / 2 * f.visual_scale;
1226 // add sqrt(2) visual scale
1227 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x10) != 0))
1230 float random_offset_X = .0;
1231 float random_offset_Z = .0;
1232 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) {
1233 random_offset_X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1234 random_offset_Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1237 for (int j = 0; j < 4; j++) {
1238 video::S3DVertex vertices[4] =
1240 video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1),
1241 video::S3DVertex( s,-BS/2, 0, 0,0,0, c, 1,1),
1242 video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
1243 video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
1246 float rotate_degree = 0;
1248 if (f.param_type_2 == CPT2_DEGROTATE)
1249 rotate_degree = n.param2 * 2;
1250 if (f.param_type_2 != CPT2_MESHOPTIONS) {
1252 for (u16 i = 0; i < 4; i++)
1253 vertices[i].Pos.rotateXZBy(46 + rotate_degree);
1254 } else if (j == 1) {
1255 for (u16 i = 0; i < 4; i++)
1256 vertices[i].Pos.rotateXZBy(-44 + rotate_degree);
1259 p2mesh = n.param2 & 0x7;
1264 for (u16 i = 0; i < 4; i++)
1265 vertices[i].Pos.rotateXZBy(46);
1266 } else if (j == 1) {
1267 for (u16 i = 0; i < 4; i++)
1268 vertices[i].Pos.rotateXZBy(-44);
1274 for (u16 i = 0; i < 4; i++)
1275 vertices[i].Pos.rotateXZBy(91);
1276 } else if (j == 1) {
1277 for (u16 i = 0; i < 4; i++)
1278 vertices[i].Pos.rotateXZBy(1);
1284 for (u16 i = 0; i < 4; i++)
1285 vertices[i].Pos.rotateXZBy(121);
1286 } else if (j == 1) {
1287 for (u16 i = 0; i < 4; i++)
1288 vertices[i].Pos.rotateXZBy(241);
1289 } else { // (j == 2)
1290 for (u16 i = 0; i < 4; i++)
1291 vertices[i].Pos.rotateXZBy(1);
1298 for (u16 i = 0; i < 4; i++) {
1299 vertices[i].Pos.rotateXZBy(1);
1300 vertices[i].Pos.Z += BS / 4;
1304 for (u16 i = 0; i < 4; i++) {
1305 vertices[i].Pos.rotateXZBy(91);
1306 vertices[i].Pos.X += BS / 4;
1310 for (u16 i = 0; i < 4; i++) {
1311 vertices[i].Pos.rotateXZBy(181);
1312 vertices[i].Pos.Z -= BS / 4;
1316 for (u16 i = 0; i < 4; i++) {
1317 vertices[i].Pos.rotateXZBy(271);
1318 vertices[i].Pos.X -= BS / 4;
1324 // outward leaning #-like
1327 for (u16 i = 2; i < 4; i++)
1328 vertices[i].Pos.Z -= BS / 2;
1329 for (u16 i = 0; i < 4; i++)
1330 vertices[i].Pos.rotateXZBy(1);
1333 for (u16 i = 2; i < 4; i++)
1334 vertices[i].Pos.Z -= BS / 2;
1335 for (u16 i = 0; i < 4; i++)
1336 vertices[i].Pos.rotateXZBy(91);
1339 for (u16 i = 2; i < 4; i++)
1340 vertices[i].Pos.Z -= BS / 2;
1341 for (u16 i = 0; i < 4; i++)
1342 vertices[i].Pos.rotateXZBy(181);
1345 for (u16 i = 2; i < 4; i++)
1346 vertices[i].Pos.Z -= BS / 2;
1347 for (u16 i = 0; i < 4; i++)
1348 vertices[i].Pos.rotateXZBy(271);
1355 for (int i = 0; i < 4; i++) {
1356 vertices[i].Pos *= f.visual_scale;
1357 vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1);
1358 vertices[i].Pos += intToFloat(p, BS);
1359 // move to a random spot to avoid moire
1360 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) {
1361 vertices[i].Pos.X += random_offset_X;
1362 vertices[i].Pos.Z += random_offset_Z;
1364 // randomly move each face up/down
1365 if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x20) != 0)) {
1366 PseudoRandom yrng(j | x<<16 | z<<8 | y<<24 );
1367 vertices[i].Pos.Y -= BS * ((yrng.next() % 16 / 16.0) * 0.125);
1371 u16 indices[] = {0, 1, 2, 2, 3, 0};
1372 // Add to mesh collector
1373 collector.append(tile, vertices, 4, indices, 6);
1375 // stop adding faces for meshes with less than 4 faces
1376 if (f.param_type_2 == CPT2_MESHOPTIONS) {
1377 if (((p2mesh == 0) || (p2mesh == 1)) && (j == 1))
1379 else if ((p2mesh == 2) && (j == 2))
1381 } else if (j == 1) {
1389 TileSpec tile = getNodeTileN(n, p, 0, data);
1390 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1392 u16 l = getInteriorLight(n, 1, nodedef);
1393 video::SColor c = encode_light_and_color(l, tile.color,
1396 float s = BS / 2 * f.visual_scale;
1398 content_t current = n.getContent();
1403 static const v3s16 dirs[6] = {
1412 int doDraw[6] = {0, 0, 0, 0, 0, 0};
1414 bool drawAllFaces = true;
1416 // Check for adjacent nodes
1417 for (int i = 0; i < 6; i++) {
1418 n2p = blockpos_nodes + p + dirs[i];
1419 n2 = data->m_vmanip.getNodeNoEx(n2p);
1420 n2c = n2.getContent();
1421 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1424 drawAllFaces = false;
1429 for (int j = 0; j < 6; j++) {
1431 video::S3DVertex vertices[4] = {
1432 video::S3DVertex(-s, -BS / 2, 0, 0, 0, 0, c, 0, 1),
1433 video::S3DVertex( s, -BS / 2, 0, 0, 0, 0, c, 1, 1),
1434 video::S3DVertex( s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 1, 0),
1435 video::S3DVertex(-s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 0, 0),
1438 // Calculate which faces should be drawn, (top or sides)
1439 if (j == 0 && (drawAllFaces ||
1440 (doDraw[3] == 1 || doDraw[1] == 1))) {
1441 for (int i = 0; i < 4; i++) {
1442 vertices[i].Pos.rotateXZBy(90);
1443 vertices[i].Pos.rotateXYBy(-10);
1444 vertices[i].Pos.X -= 4.0;
1446 } else if (j == 1 && (drawAllFaces ||
1447 (doDraw[5] == 1 || doDraw[1] == 1))) {
1448 for (int i = 0; i < 4; i++) {
1449 vertices[i].Pos.rotateXZBy(180);
1450 vertices[i].Pos.rotateYZBy(10);
1451 vertices[i].Pos.Z -= 4.0;
1453 } else if (j == 2 && (drawAllFaces ||
1454 (doDraw[2] == 1 || doDraw[1] == 1))) {
1455 for (int i = 0; i < 4; i++) {
1456 vertices[i].Pos.rotateXZBy(270);
1457 vertices[i].Pos.rotateXYBy(10);
1458 vertices[i].Pos.X += 4.0;
1460 } else if (j == 3 && (drawAllFaces ||
1461 (doDraw[4] == 1 || doDraw[1] == 1))) {
1462 for (int i = 0; i < 4; i++) {
1463 vertices[i].Pos.rotateYZBy(-10);
1464 vertices[i].Pos.Z += 4.0;
1466 // Center cross-flames
1467 } else if (j == 4 && (drawAllFaces || doDraw[1] == 1)) {
1468 for (int i = 0; i < 4; i++) {
1469 vertices[i].Pos.rotateXZBy(45);
1471 } else if (j == 5 && (drawAllFaces || doDraw[1] == 1)) {
1472 for (int i = 0; i < 4; i++) {
1473 vertices[i].Pos.rotateXZBy(-45);
1475 // Render flames on bottom of node above
1476 } else if (j == 0 && doDraw[0] == 1 && doDraw[1] == 0) {
1477 for (int i = 0; i < 4; i++) {
1478 vertices[i].Pos.rotateYZBy(70);
1479 vertices[i].Pos.rotateXZBy(90);
1480 vertices[i].Pos.Y += 4.84;
1481 vertices[i].Pos.X -= 4.7;
1483 } else if (j == 1 && doDraw[0] == 1 && doDraw[1] == 0) {
1484 for (int i = 0; i < 4; i++) {
1485 vertices[i].Pos.rotateYZBy(70);
1486 vertices[i].Pos.rotateXZBy(180);
1487 vertices[i].Pos.Y += 4.84;
1488 vertices[i].Pos.Z -= 4.7;
1490 } else if (j == 2 && doDraw[0] == 1 && doDraw[1] == 0) {
1491 for (int i = 0; i < 4; i++) {
1492 vertices[i].Pos.rotateYZBy(70);
1493 vertices[i].Pos.rotateXZBy(270);
1494 vertices[i].Pos.Y += 4.84;
1495 vertices[i].Pos.X += 4.7;
1497 } else if (j == 3 && doDraw[0] == 1 && doDraw[1] == 0) {
1498 for (int i = 0; i < 4; i++) {
1499 vertices[i].Pos.rotateYZBy(70);
1500 vertices[i].Pos.Y += 4.84;
1501 vertices[i].Pos.Z += 4.7;
1504 // Skip faces that aren't adjacent to a node
1508 for (int i = 0; i < 4; i++) {
1509 vertices[i].Pos *= f.visual_scale;
1510 vertices[i].Pos += intToFloat(p, BS);
1513 u16 indices[] = {0, 1, 2, 2, 3, 0};
1514 // Add to mesh collector
1515 collector.append(tile, vertices, 4, indices, 6);
1520 TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1521 TileSpec tile_nocrack = tile;
1522 tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1524 // Put wood the right way around in the posts
1525 TileSpec tile_rot = tile;
1526 tile_rot.rotation = 1;
1528 u16 l = getInteriorLight(n, 1, nodedef);
1529 video::SColor c = encode_light_and_color(l, tile.color,
1532 const f32 post_rad=(f32)BS/8;
1533 const f32 bar_rad=(f32)BS/16;
1534 const f32 bar_len=(f32)(BS/2)-post_rad;
1536 v3f pos = intToFloat(p, BS);
1538 // The post - always present
1539 aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
1540 post.MinEdge += pos;
1541 post.MaxEdge += pos;
1543 6/16.,6/16.,10/16.,10/16.,
1544 6/16.,6/16.,10/16.,10/16.,
1549 makeCuboid(&collector, post, &tile_rot, 1, c, postuv,
1552 // Now a section of fence, +X, if there's a post there
1555 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1556 const ContentFeatures *f2 = &nodedef->get(n2);
1557 if(f2->drawtype == NDT_FENCELIKE)
1559 aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
1560 bar_len+BS/2,bar_rad+BS/4,bar_rad);
1564 0/16.,2/16.,16/16.,4/16.,
1565 0/16.,4/16.,16/16.,6/16.,
1566 6/16.,6/16.,8/16.,8/16.,
1567 10/16.,10/16.,12/16.,12/16.,
1568 0/16.,8/16.,16/16.,10/16.,
1569 0/16.,14/16.,16/16.,16/16.};
1570 makeCuboid(&collector, bar, &tile_nocrack, 1,
1571 c, xrailuv, f.light_source);
1572 bar.MinEdge.Y -= BS/2;
1573 bar.MaxEdge.Y -= BS/2;
1574 makeCuboid(&collector, bar, &tile_nocrack, 1,
1575 c, xrailuv, f.light_source);
1578 // Now a section of fence, +Z, if there's a post there
1581 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1582 f2 = &nodedef->get(n2);
1583 if(f2->drawtype == NDT_FENCELIKE)
1585 aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
1586 bar_rad,bar_rad+BS/4,bar_len+BS/2);
1590 3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
1591 4/16.,1/16.,6/16.,5/16., // for wood texture instead
1592 0/16.,9/16.,16/16.,11/16.,
1593 0/16.,6/16.,16/16.,8/16.,
1594 6/16.,6/16.,8/16.,8/16.,
1595 10/16.,10/16.,12/16.,12/16.};
1596 makeCuboid(&collector, bar, &tile_nocrack, 1,
1597 c, zrailuv, f.light_source);
1598 bar.MinEdge.Y -= BS/2;
1599 bar.MaxEdge.Y -= BS/2;
1600 makeCuboid(&collector, bar, &tile_nocrack, 1,
1601 c, zrailuv, f.light_source);
1606 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) */
1609 content_t thiscontent = n.getContent();
1610 std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
1611 int self_group = ((ItemGroupList) nodedef->get(n).groups)[groupname];
1614 for (s8 y0 = -1; y0 <= 1; y0++) {
1615 // Prevent from indexing never used coordinates
1616 for (s8 xz = -1; xz <= 1; xz++) {
1619 MapNode n_xy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x + xz, y + y0, z));
1620 MapNode n_zy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y + y0, z + xz));
1621 const ContentFeatures &def_xy = nodedef->get(n_xy);
1622 const ContentFeatures &def_zy = nodedef->get(n_zy);
1624 // Check if current node would connect with the rail
1625 is_rail_x[index] = ((def_xy.drawtype == NDT_RAILLIKE
1626 && ((ItemGroupList) def_xy.groups)[groupname] == self_group)
1627 || n_xy.getContent() == thiscontent);
1629 is_rail_z[index] = ((def_zy.drawtype == NDT_RAILLIKE
1630 && ((ItemGroupList) def_zy.groups)[groupname] == self_group)
1631 || n_zy.getContent() == thiscontent);
1636 bool is_rail_x_all[2]; // [0] = negative x, [1] = positive x coordinate from the current node position
1637 bool is_rail_z_all[2];
1638 is_rail_x_all[0] = is_rail_x[0] || is_rail_x[2] || is_rail_x[4];
1639 is_rail_x_all[1] = is_rail_x[1] || is_rail_x[3] || is_rail_x[5];
1640 is_rail_z_all[0] = is_rail_z[0] || is_rail_z[2] || is_rail_z[4];
1641 is_rail_z_all[1] = is_rail_z[1] || is_rail_z[3] || is_rail_z[5];
1643 // reasonable default, flat straight unrotated rail
1644 bool is_straight = true;
1645 int adjacencies = 0;
1649 // check for sloped rail
1650 if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5]) {
1651 adjacencies = 5; // 5 means sloped
1652 is_straight = true; // sloped is always straight
1654 // is really straight, rails on both sides
1655 is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
1656 adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
1659 switch (adjacencies) {
1661 if (is_rail_x_all[0] || is_rail_x_all[1])
1666 tileindex = 1; // curved
1667 if (is_rail_x_all[0] && is_rail_x_all[1])
1669 if (is_rail_z_all[0] && is_rail_z_all[1]) {
1673 else if (is_rail_x_all[0] && is_rail_z_all[0])
1675 else if (is_rail_x_all[0] && is_rail_z_all[1])
1677 else if (is_rail_x_all[1] && is_rail_z_all[1])
1681 // here is where the potential to 'switch' a junction is, but not implemented at present
1682 tileindex = 2; // t-junction
1683 if(!is_rail_x_all[1])
1685 if(!is_rail_z_all[0])
1687 if(!is_rail_z_all[1])
1691 tileindex = 3; // crossing
1705 TileSpec tile = getNodeTileN(n, p, tileindex, data);
1706 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1707 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1709 u16 l = getInteriorLight(n, 0, nodedef);
1710 video::SColor c = encode_light_and_color(l, tile.color,
1713 float d = (float)BS/64;
1717 if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5])
1718 g = 1; //Object is at a slope
1720 video::S3DVertex vertices[4] =
1722 video::S3DVertex(-s, -s+d, -s, 0, 0, 0, c, 0, 1),
1723 video::S3DVertex( s, -s+d, -s, 0, 0, 0, c, 1, 1),
1724 video::S3DVertex( s, g*s+d, s, 0, 0, 0, c, 1, 0),
1725 video::S3DVertex(-s, g*s+d, s, 0, 0, 0, c, 0, 0),
1728 for(s32 i=0; i<4; i++)
1731 vertices[i].Pos.rotateXZBy(angle);
1732 vertices[i].Pos += intToFloat(p, BS);
1735 u16 indices[] = {0,1,2,2,3,0};
1736 collector.append(tile, vertices, 4, indices, 6);
1740 static const v3s16 tile_dirs[6] = {
1749 u16 l = getInteriorLight(n, 1, nodedef);
1751 video::SColor colors[6];
1752 for(int j = 0; j < 6; j++) {
1753 // Handles facedir rotation for textures
1754 tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
1755 colors[j]= encode_light_and_color(l, tiles[j].color,
1759 v3f pos = intToFloat(p, BS);
1763 // locate possible neighboring nodes to connect to
1764 if (f.node_box.type == NODEBOX_CONNECTED) {
1768 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
1772 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
1776 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
1780 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
1784 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
1788 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
1791 std::vector<aabb3f> boxes;
1792 n.getNodeBoxes(nodedef, &boxes, neighbors);
1793 for(std::vector<aabb3f>::iterator
1795 i != boxes.end(); ++i)
1802 if (box.MinEdge.X > box.MaxEdge.X)
1805 box.MinEdge.X=box.MaxEdge.X;
1808 if (box.MinEdge.Y > box.MaxEdge.Y)
1811 box.MinEdge.Y=box.MaxEdge.Y;
1814 if (box.MinEdge.Z > box.MaxEdge.Z)
1817 box.MinEdge.Z=box.MaxEdge.Z;
1822 // Compute texture coords
1823 f32 tx1 = (box.MinEdge.X/BS)+0.5;
1824 f32 ty1 = (box.MinEdge.Y/BS)+0.5;
1825 f32 tz1 = (box.MinEdge.Z/BS)+0.5;
1826 f32 tx2 = (box.MaxEdge.X/BS)+0.5;
1827 f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
1828 f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
1831 tx1, 1-tz2, tx2, 1-tz1,
1835 tz1, 1-ty2, tz2, 1-ty1,
1837 1-tz2, 1-ty2, 1-tz1, 1-ty1,
1839 1-tx2, 1-ty2, 1-tx1, 1-ty1,
1841 tx1, 1-ty2, tx2, 1-ty1,
1843 makeCuboid(&collector, box, tiles, 6, colors, txc, f.light_source);
1848 v3f pos = intToFloat(p, BS);
1849 u16 l = getInteriorLight(n, 1, nodedef);
1851 if (f.param_type_2 == CPT2_FACEDIR ||
1852 f.param_type_2 == CPT2_COLORED_FACEDIR) {
1853 facedir = n.getFaceDir(nodedef);
1854 } else if (f.param_type_2 == CPT2_WALLMOUNTED ||
1855 f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1856 //convert wallmounted to 6dfacedir.
1857 //when cache enabled, it is already converted
1858 facedir = n.getWallMounted(nodedef);
1859 if (!enable_mesh_cache) {
1860 static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
1861 facedir = wm_to_6d[facedir];
1865 if (f.mesh_ptr[facedir]) {
1866 // use cached meshes
1867 for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) {
1868 const TileSpec &tile = getNodeTileN(n, p, j, data);
1869 scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j);
1870 collector.append(tile, (video::S3DVertex *)
1871 buf->getVertices(), buf->getVertexCount(),
1872 buf->getIndices(), buf->getIndexCount(), pos,
1873 encode_light_and_color(l, tile.color, f.light_source),
1876 } else if (f.mesh_ptr[0]) {
1877 // no cache, clone and rotate mesh
1878 scene::IMesh* mesh = cloneMesh(f.mesh_ptr[0]);
1879 rotateMeshBy6dFacedir(mesh, facedir);
1880 recalculateBoundingBox(mesh);
1881 meshmanip->recalculateNormals(mesh, true, false);
1882 for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) {
1883 const TileSpec &tile = getNodeTileN(n, p, j, data);
1884 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1885 collector.append(tile, (video::S3DVertex *)
1886 buf->getVertices(), buf->getVertexCount(),
1887 buf->getIndices(), buf->getIndexCount(), pos,
1888 encode_light_and_color(l, tile.color, f.light_source),