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>
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 void makeCuboid(MeshCollector
*collector
, const aabb3f
&box
,
47 TileSpec
*tiles
, int tilecount
, video::SColor
&c
, const f32
* txc
)
49 assert(tilecount
>= 1 && tilecount
<= 6); // pre-condition
51 v3f min
= box
.MinEdge
;
52 v3f max
= box
.MaxEdge
;
57 static const f32 txc_default
[24] = {
68 video::S3DVertex vertices
[24] =
71 video::S3DVertex(min
.X
,max
.Y
,max
.Z
, 0,1,0, c
, txc
[0],txc
[1]),
72 video::S3DVertex(max
.X
,max
.Y
,max
.Z
, 0,1,0, c
, txc
[2],txc
[1]),
73 video::S3DVertex(max
.X
,max
.Y
,min
.Z
, 0,1,0, c
, txc
[2],txc
[3]),
74 video::S3DVertex(min
.X
,max
.Y
,min
.Z
, 0,1,0, c
, txc
[0],txc
[3]),
76 video::S3DVertex(min
.X
,min
.Y
,min
.Z
, 0,-1,0, c
, txc
[4],txc
[5]),
77 video::S3DVertex(max
.X
,min
.Y
,min
.Z
, 0,-1,0, c
, txc
[6],txc
[5]),
78 video::S3DVertex(max
.X
,min
.Y
,max
.Z
, 0,-1,0, c
, txc
[6],txc
[7]),
79 video::S3DVertex(min
.X
,min
.Y
,max
.Z
, 0,-1,0, c
, txc
[4],txc
[7]),
81 video::S3DVertex(max
.X
,max
.Y
,min
.Z
, 1,0,0, c
, txc
[ 8],txc
[9]),
82 video::S3DVertex(max
.X
,max
.Y
,max
.Z
, 1,0,0, c
, txc
[10],txc
[9]),
83 video::S3DVertex(max
.X
,min
.Y
,max
.Z
, 1,0,0, c
, txc
[10],txc
[11]),
84 video::S3DVertex(max
.X
,min
.Y
,min
.Z
, 1,0,0, c
, txc
[ 8],txc
[11]),
86 video::S3DVertex(min
.X
,max
.Y
,max
.Z
, -1,0,0, c
, txc
[12],txc
[13]),
87 video::S3DVertex(min
.X
,max
.Y
,min
.Z
, -1,0,0, c
, txc
[14],txc
[13]),
88 video::S3DVertex(min
.X
,min
.Y
,min
.Z
, -1,0,0, c
, txc
[14],txc
[15]),
89 video::S3DVertex(min
.X
,min
.Y
,max
.Z
, -1,0,0, c
, txc
[12],txc
[15]),
91 video::S3DVertex(max
.X
,max
.Y
,max
.Z
, 0,0,1, c
, txc
[16],txc
[17]),
92 video::S3DVertex(min
.X
,max
.Y
,max
.Z
, 0,0,1, c
, txc
[18],txc
[17]),
93 video::S3DVertex(min
.X
,min
.Y
,max
.Z
, 0,0,1, c
, txc
[18],txc
[19]),
94 video::S3DVertex(max
.X
,min
.Y
,max
.Z
, 0,0,1, c
, txc
[16],txc
[19]),
96 video::S3DVertex(min
.X
,max
.Y
,min
.Z
, 0,0,-1, c
, txc
[20],txc
[21]),
97 video::S3DVertex(max
.X
,max
.Y
,min
.Z
, 0,0,-1, c
, txc
[22],txc
[21]),
98 video::S3DVertex(max
.X
,min
.Y
,min
.Z
, 0,0,-1, c
, txc
[22],txc
[23]),
99 video::S3DVertex(min
.X
,min
.Y
,min
.Z
, 0,0,-1, c
, txc
[20],txc
[23]),
102 for(int i
= 0; i
< 6; i
++)
104 switch (tiles
[MYMIN(i
, tilecount
-1)].rotation
)
109 for (int x
= 0; x
< 4; x
++)
110 vertices
[i
*4+x
].TCoords
.rotateBy(90,irr::core::vector2df(0, 0));
113 for (int x
= 0; x
< 4; x
++)
114 vertices
[i
*4+x
].TCoords
.rotateBy(180,irr::core::vector2df(0, 0));
117 for (int x
= 0; x
< 4; x
++)
118 vertices
[i
*4+x
].TCoords
.rotateBy(270,irr::core::vector2df(0, 0));
121 for (int x
= 0; x
< 4; x
++){
122 vertices
[i
*4+x
].TCoords
.X
= 1.0 - vertices
[i
*4+x
].TCoords
.X
;
123 vertices
[i
*4+x
].TCoords
.rotateBy(90,irr::core::vector2df(0, 0));
127 for (int x
= 0; x
< 4; x
++){
128 vertices
[i
*4+x
].TCoords
.X
= 1.0 - vertices
[i
*4+x
].TCoords
.X
;
129 vertices
[i
*4+x
].TCoords
.rotateBy(270,irr::core::vector2df(0, 0));
133 for (int x
= 0; x
< 4; x
++){
134 vertices
[i
*4+x
].TCoords
.Y
= 1.0 - vertices
[i
*4+x
].TCoords
.Y
;
135 vertices
[i
*4+x
].TCoords
.rotateBy(90,irr::core::vector2df(0, 0));
139 for (int x
= 0; x
< 4; x
++){
140 vertices
[i
*4+x
].TCoords
.Y
= 1.0 - vertices
[i
*4+x
].TCoords
.Y
;
141 vertices
[i
*4+x
].TCoords
.rotateBy(270,irr::core::vector2df(0, 0));
145 for (int x
= 0; x
< 4; x
++){
146 vertices
[i
*4+x
].TCoords
.X
= 1.0 - vertices
[i
*4+x
].TCoords
.X
;
150 for (int x
= 0; x
< 4; x
++){
151 vertices
[i
*4+x
].TCoords
.Y
= 1.0 - vertices
[i
*4+x
].TCoords
.Y
;
158 u16 indices
[] = {0,1,2,2,3,0};
159 // Add to mesh collector
160 for (s32 j
= 0; j
< 24; j
+= 4) {
161 int tileindex
= MYMIN(j
/ 4, tilecount
- 1);
162 collector
->append(tiles
[tileindex
], vertices
+ j
, 4, indices
, 6);
167 TODO: Fix alpha blending for special nodes
168 Currently only the last element rendered is blended correct
170 void mapblock_mesh_generate_special(MeshMakeData
*data
,
171 MeshCollector
&collector
)
173 INodeDefManager
*nodedef
= data
->m_gamedef
->ndef();
174 ITextureSource
*tsrc
= data
->m_gamedef
->tsrc();
175 scene::ISceneManager
* smgr
= data
->m_gamedef
->getSceneManager();
176 scene::IMeshManipulator
* meshmanip
= smgr
->getMeshManipulator();
179 //TimeTaker timer("mapblock_mesh_generate_special()");
184 bool enable_mesh_cache
= g_settings
->getBool("enable_mesh_cache");
185 bool new_style_water
= g_settings
->getBool("new_style_water");
187 float node_liquid_level
= 1.0;
189 node_liquid_level
= 0.85;
191 v3s16 blockpos_nodes
= data
->m_blockpos
*MAP_BLOCKSIZE
;
193 for(s16 z
= 0; z
< MAP_BLOCKSIZE
; z
++)
194 for(s16 y
= 0; y
< MAP_BLOCKSIZE
; y
++)
195 for(s16 x
= 0; x
< MAP_BLOCKSIZE
; x
++)
199 MapNode n
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p
);
200 const ContentFeatures
&f
= nodedef
->get(n
);
202 // Only solidness=0 stuff is drawn here
208 infostream
<< "Got " << f
.drawtype
<< std::endl
;
209 FATAL_ERROR("Unknown drawtype");
216 Add water sources to mesh if using new style
218 TileSpec tile_liquid
= f
.special_tiles
[0];
219 TileSpec tile_liquid_bfculled
= getNodeTile(n
, p
, v3s16(0,0,0), data
);
221 bool top_is_same_liquid
= false;
222 MapNode ntop
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ v3s16(x
,y
+1,z
));
223 content_t c_flowing
= nodedef
->getId(f
.liquid_alternative_flowing
);
224 content_t c_source
= nodedef
->getId(f
.liquid_alternative_source
);
225 if(ntop
.getContent() == c_flowing
|| ntop
.getContent() == c_source
)
226 top_is_same_liquid
= true;
228 u16 l
= getInteriorLight(n
, 0, nodedef
);
229 video::SColor c
= MapBlock_LightColor(f
.alpha
, l
, f
.light_source
);
234 v3s16 side_dirs
[4] = {
240 for(u32 i
=0; i
<4; i
++)
242 v3s16 dir
= side_dirs
[i
];
244 MapNode neighbor
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p
+ dir
);
245 content_t neighbor_content
= neighbor
.getContent();
246 const ContentFeatures
&n_feat
= nodedef
->get(neighbor_content
);
247 MapNode n_top
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p
+ dir
+ v3s16(0,1,0));
248 content_t n_top_c
= n_top
.getContent();
250 if(neighbor_content
== CONTENT_IGNORE
)
254 If our topside is liquid and neighbor's topside
255 is liquid, don't draw side face
257 if(top_is_same_liquid
&& (n_top_c
== c_flowing
||
258 n_top_c
== c_source
|| n_top_c
== CONTENT_IGNORE
))
261 // Don't draw face if neighbor is blocking the view
262 if(n_feat
.solidness
== 2)
265 bool neighbor_is_same_liquid
= (neighbor_content
== c_source
266 || neighbor_content
== c_flowing
);
268 // Don't draw any faces if neighbor same is liquid and top is
270 if(neighbor_is_same_liquid
&& !top_is_same_liquid
)
273 // Use backface culled material if neighbor doesn't have a
275 const TileSpec
*current_tile
= &tile_liquid
;
276 if(n_feat
.solidness
!= 0 || n_feat
.visual_solidness
!= 0)
277 current_tile
= &tile_liquid_bfculled
;
279 video::S3DVertex vertices
[4] =
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),
288 If our topside is liquid, set upper border of face
289 at upper border of node
291 if(top_is_same_liquid
)
293 vertices
[2].Pos
.Y
= 0.5*BS
;
294 vertices
[3].Pos
.Y
= 0.5*BS
;
297 Otherwise upper position of face is liquid level
301 vertices
[2].Pos
.Y
= (node_liquid_level
-0.5)*BS
;
302 vertices
[3].Pos
.Y
= (node_liquid_level
-0.5)*BS
;
305 If neighbor is liquid, lower border of face is liquid level
307 if(neighbor_is_same_liquid
)
309 vertices
[0].Pos
.Y
= (node_liquid_level
-0.5)*BS
;
310 vertices
[1].Pos
.Y
= (node_liquid_level
-0.5)*BS
;
313 If neighbor is not liquid, lower border of face is
318 vertices
[0].Pos
.Y
= -0.5*BS
;
319 vertices
[1].Pos
.Y
= -0.5*BS
;
322 for(s32 j
=0; j
<4; j
++)
324 if(dir
== v3s16(0,0,1))
325 vertices
[j
].Pos
.rotateXZBy(0);
326 if(dir
== v3s16(0,0,-1))
327 vertices
[j
].Pos
.rotateXZBy(180);
328 if(dir
== v3s16(-1,0,0))
329 vertices
[j
].Pos
.rotateXZBy(90);
330 if(dir
== v3s16(1,0,-0))
331 vertices
[j
].Pos
.rotateXZBy(-90);
333 // Do this to not cause glitches when two liquids are
335 /*if(neighbor_is_same_liquid == false){
336 vertices[j].Pos.X *= 0.98;
337 vertices[j].Pos.Z *= 0.98;
340 vertices
[j
].Pos
+= intToFloat(p
, BS
);
343 u16 indices
[] = {0,1,2,2,3,0};
344 // Add to mesh collector
345 collector
.append(*current_tile
, vertices
, 4, indices
, 6);
351 if(top_is_same_liquid
)
354 video::S3DVertex vertices
[4] =
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),
362 v3f
offset(p
.X
*BS
, p
.Y
*BS
+ (-0.5+node_liquid_level
)*BS
, p
.Z
*BS
);
363 for(s32 i
=0; i
<4; i
++)
365 vertices
[i
].Pos
+= offset
;
368 u16 indices
[] = {0,1,2,2,3,0};
369 // Add to mesh collector
370 collector
.append(tile_liquid
, vertices
, 4, indices
, 6);
372 case NDT_FLOWINGLIQUID
:
375 Add flowing liquid to mesh
377 TileSpec tile_liquid
= f
.special_tiles
[0];
378 TileSpec tile_liquid_bfculled
= f
.special_tiles
[1];
380 bool top_is_same_liquid
= false;
381 MapNode ntop
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ v3s16(x
,y
+1,z
));
382 content_t c_flowing
= nodedef
->getId(f
.liquid_alternative_flowing
);
383 content_t c_source
= nodedef
->getId(f
.liquid_alternative_source
);
384 if(ntop
.getContent() == c_flowing
|| ntop
.getContent() == c_source
)
385 top_is_same_liquid
= true;
388 // If this liquid emits light and doesn't contain light, draw
389 // it at what it emits, for an increased effect
390 u8 light_source
= nodedef
->get(n
).light_source
;
391 if(light_source
!= 0){
392 l
= decode_light(light_source
);
395 // Use the light of the node on top if possible
396 else if(nodedef
->get(ntop
).param_type
== CPT_LIGHT
)
397 l
= getInteriorLight(ntop
, 0, nodedef
);
398 // Otherwise use the light of this node (the liquid)
400 l
= getInteriorLight(n
, 0, nodedef
);
401 video::SColor c
= MapBlock_LightColor(f
.alpha
, l
, f
.light_source
);
403 u8 range
= rangelim(nodedef
->get(c_flowing
).liquid_range
, 1, 8);
405 // Neighbor liquid levels (key = relative position)
406 // Includes current node
407 std::map
<v3s16
, f32
> neighbor_levels
;
408 std::map
<v3s16
, content_t
> neighbor_contents
;
409 std::map
<v3s16
, u8
> neighbor_flags
;
410 const u8 neighborflag_top_is_same_liquid
= 0x01;
411 v3s16 neighbor_dirs
[9] = {
422 for(u32 i
=0; i
<9; i
++)
424 content_t content
= CONTENT_AIR
;
425 float level
= -0.5 * BS
;
428 v3s16 p2
= p
+ neighbor_dirs
[i
];
429 MapNode n2
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p2
);
430 if(n2
.getContent() != CONTENT_IGNORE
)
432 content
= n2
.getContent();
434 if(n2
.getContent() == c_source
)
435 level
= (-0.5+node_liquid_level
) * BS
;
436 else if(n2
.getContent() == c_flowing
){
437 u8 liquid_level
= (n2
.param2
&LIQUID_LEVEL_MASK
);
438 if (liquid_level
<= LIQUID_LEVEL_MAX
+1-range
)
441 liquid_level
-= (LIQUID_LEVEL_MAX
+1-range
);
442 level
= (-0.5 + ((float)liquid_level
+ 0.5) / (float)range
* node_liquid_level
) * BS
;
445 // Check node above neighbor.
446 // NOTE: This doesn't get executed if neighbor
449 n2
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p2
);
450 if(n2
.getContent() == c_source
||
451 n2
.getContent() == c_flowing
)
452 flags
|= neighborflag_top_is_same_liquid
;
455 neighbor_levels
[neighbor_dirs
[i
]] = level
;
456 neighbor_contents
[neighbor_dirs
[i
]] = content
;
457 neighbor_flags
[neighbor_dirs
[i
]] = flags
;
460 // Corner heights (average between four liquids)
461 f32 corner_levels
[4];
463 v3s16 halfdirs
[4] = {
469 for(u32 i
=0; i
<4; i
++)
471 v3s16 cornerdir
= halfdirs
[i
];
472 float cornerlevel
= 0;
475 for(u32 j
=0; j
<4; j
++)
477 v3s16 neighbordir
= cornerdir
- halfdirs
[j
];
478 content_t content
= neighbor_contents
[neighbordir
];
479 // If top is liquid, draw starting from top of node
480 if(neighbor_flags
[neighbordir
] &
481 neighborflag_top_is_same_liquid
)
483 cornerlevel
= 0.5*BS
;
487 // Source is always the same height
488 else if(content
== c_source
)
490 cornerlevel
= (-0.5+node_liquid_level
)*BS
;
494 // Flowing liquid has level information
495 else if(content
== c_flowing
)
497 cornerlevel
+= neighbor_levels
[neighbordir
];
500 else if(content
== CONTENT_AIR
)
506 cornerlevel
= -0.5*BS
+0.2;
507 else if(valid_count
> 0)
508 cornerlevel
/= valid_count
;
509 corner_levels
[i
] = cornerlevel
;
516 v3s16 side_dirs
[4] = {
522 s16 side_corners
[4][2] = {
528 for(u32 i
=0; i
<4; i
++)
530 v3s16 dir
= side_dirs
[i
];
533 If our topside is liquid and neighbor's topside
534 is liquid, don't draw side face
536 if(top_is_same_liquid
&&
537 neighbor_flags
[dir
] & neighborflag_top_is_same_liquid
)
540 content_t neighbor_content
= neighbor_contents
[dir
];
541 const ContentFeatures
&n_feat
= nodedef
->get(neighbor_content
);
543 // Don't draw face if neighbor is blocking the view
544 if(n_feat
.solidness
== 2)
547 bool neighbor_is_same_liquid
= (neighbor_content
== c_source
548 || neighbor_content
== c_flowing
);
550 // Don't draw any faces if neighbor same is liquid and top is
552 if(neighbor_is_same_liquid
== true
553 && top_is_same_liquid
== false)
556 // Use backface culled material if neighbor doesn't have a
558 const TileSpec
*current_tile
= &tile_liquid
;
559 if(n_feat
.solidness
!= 0 || n_feat
.visual_solidness
!= 0)
560 current_tile
= &tile_liquid_bfculled
;
562 video::S3DVertex vertices
[4] =
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),
571 If our topside is liquid, set upper border of face
572 at upper border of node
574 if(top_is_same_liquid
)
576 vertices
[2].Pos
.Y
= 0.5*BS
;
577 vertices
[3].Pos
.Y
= 0.5*BS
;
580 Otherwise upper position of face is corner levels
584 vertices
[2].Pos
.Y
= corner_levels
[side_corners
[i
][0]];
585 vertices
[3].Pos
.Y
= corner_levels
[side_corners
[i
][1]];
589 If neighbor is liquid, lower border of face is corner
592 if(neighbor_is_same_liquid
)
594 vertices
[0].Pos
.Y
= corner_levels
[side_corners
[i
][1]];
595 vertices
[1].Pos
.Y
= corner_levels
[side_corners
[i
][0]];
598 If neighbor is not liquid, lower border of face is
603 vertices
[0].Pos
.Y
= -0.5*BS
;
604 vertices
[1].Pos
.Y
= -0.5*BS
;
607 for(s32 j
=0; j
<4; j
++)
609 if(dir
== v3s16(0,0,1))
610 vertices
[j
].Pos
.rotateXZBy(0);
611 if(dir
== v3s16(0,0,-1))
612 vertices
[j
].Pos
.rotateXZBy(180);
613 if(dir
== v3s16(-1,0,0))
614 vertices
[j
].Pos
.rotateXZBy(90);
615 if(dir
== v3s16(1,0,-0))
616 vertices
[j
].Pos
.rotateXZBy(-90);
618 // Do this to not cause glitches when two liquids are
620 /*if(neighbor_is_same_liquid == false){
621 vertices[j].Pos.X *= 0.98;
622 vertices[j].Pos.Z *= 0.98;
625 vertices
[j
].Pos
+= intToFloat(p
, BS
);
628 u16 indices
[] = {0,1,2,2,3,0};
629 // Add to mesh collector
630 collector
.append(*current_tile
, vertices
, 4, indices
, 6);
634 Generate top side, if appropriate
637 if(top_is_same_liquid
== false)
639 video::S3DVertex vertices
[4] =
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),
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 s32 corner_resolve
[4] = {3,2,1,0};
652 for(s32 i
=0; i
<4; i
++)
654 //vertices[i].Pos.Y += liquid_level;
655 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
656 s32 j
= corner_resolve
[i
];
657 vertices
[i
].Pos
.Y
+= corner_levels
[j
];
658 vertices
[i
].Pos
+= intToFloat(p
, BS
);
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 f32 dz
= (corner_levels
[side_corners
[3][0]] +
666 corner_levels
[side_corners
[3][1]]) -
667 (corner_levels
[side_corners
[2][0]] +
668 corner_levels
[side_corners
[2][1]]);
669 // Positive if liquid moves towards +X
670 f32 dx
= (corner_levels
[side_corners
[1][0]] +
671 corner_levels
[side_corners
[1][1]]) -
672 (corner_levels
[side_corners
[0][0]] +
673 corner_levels
[side_corners
[0][1]]);
674 f32 tcoord_angle
= atan2(dz
, dx
) * core::RADTODEG
;
675 v2f
tcoord_center(0.5, 0.5);
676 v2f
tcoord_translate(
677 blockpos_nodes
.Z
+ z
,
678 blockpos_nodes
.X
+ x
);
679 tcoord_translate
.rotateBy(tcoord_angle
);
680 tcoord_translate
.X
-= floor(tcoord_translate
.X
);
681 tcoord_translate
.Y
-= floor(tcoord_translate
.Y
);
683 for(s32 i
=0; i
<4; i
++)
685 vertices
[i
].TCoords
.rotateBy(
688 vertices
[i
].TCoords
+= tcoord_translate
;
691 v2f t
= vertices
[0].TCoords
;
692 vertices
[0].TCoords
= vertices
[2].TCoords
;
693 vertices
[2].TCoords
= t
;
695 u16 indices
[] = {0,1,2,2,3,0};
696 // Add to mesh collector
697 collector
.append(tile_liquid
, vertices
, 4, indices
, 6);
702 TileSpec tile
= getNodeTile(n
, p
, v3s16(0,0,0), data
);
704 u16 l
= getInteriorLight(n
, 1, nodedef
);
705 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
707 for(u32 j
=0; j
<6; j
++)
709 // Check this neighbor
710 v3s16 dir
= g_6dirs
[j
];
711 v3s16 n2p
= blockpos_nodes
+ p
+ dir
;
712 MapNode n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
713 // Don't make face if neighbor is of same type
714 if(n2
.getContent() == n
.getContent())
718 video::S3DVertex vertices
[4] = {
719 video::S3DVertex(-BS
/2,-BS
/2,BS
/2, dir
.X
,dir
.Y
,dir
.Z
, c
, 1,1),
720 video::S3DVertex(BS
/2,-BS
/2,BS
/2, dir
.X
,dir
.Y
,dir
.Z
, c
, 0,1),
721 video::S3DVertex(BS
/2,BS
/2,BS
/2, dir
.X
,dir
.Y
,dir
.Z
, c
, 0,0),
722 video::S3DVertex(-BS
/2,BS
/2,BS
/2, dir
.X
,dir
.Y
,dir
.Z
, c
, 1,0),
725 // Rotations in the g_6dirs format
727 for(u16 i
=0; i
<4; i
++)
728 vertices
[i
].Pos
.rotateXZBy(0);
729 else if(j
== 1) // Y+
730 for(u16 i
=0; i
<4; i
++)
731 vertices
[i
].Pos
.rotateYZBy(-90);
732 else if(j
== 2) // X+
733 for(u16 i
=0; i
<4; i
++)
734 vertices
[i
].Pos
.rotateXZBy(-90);
735 else if(j
== 3) // Z-
736 for(u16 i
=0; i
<4; i
++)
737 vertices
[i
].Pos
.rotateXZBy(180);
738 else if(j
== 4) // Y-
739 for(u16 i
=0; i
<4; i
++)
740 vertices
[i
].Pos
.rotateYZBy(90);
741 else if(j
== 5) // X-
742 for(u16 i
=0; i
<4; i
++)
743 vertices
[i
].Pos
.rotateXZBy(90);
745 for(u16 i
=0; i
<4; i
++){
746 vertices
[i
].Pos
+= intToFloat(p
, BS
);
749 u16 indices
[] = {0,1,2,2,3,0};
750 // Add to mesh collector
751 collector
.append(tile
, vertices
, 4, indices
, 6);
754 case NDT_GLASSLIKE_FRAMED_OPTIONAL
:
755 // This is always pre-converted to something else
756 FATAL_ERROR("NDT_GLASSLIKE_FRAMED_OPTIONAL not pre-converted as expected");
758 case NDT_GLASSLIKE_FRAMED
:
760 static const v3s16 dirs
[6] = {
771 for (i
= 0; i
< 6; i
++)
772 tiles
[i
] = getNodeTile(n
, p
, dirs
[i
], data
);
774 TileSpec glass_tiles
[6];
775 if (tiles
[1].texture
&& tiles
[2].texture
&& tiles
[3].texture
) {
776 glass_tiles
[0] = tiles
[2];
777 glass_tiles
[1] = tiles
[3];
778 glass_tiles
[2] = tiles
[1];
779 glass_tiles
[3] = tiles
[1];
780 glass_tiles
[4] = tiles
[1];
781 glass_tiles
[5] = tiles
[1];
783 for (i
= 0; i
< 6; i
++)
784 glass_tiles
[i
] = tiles
[1];
787 u8 param2
= n
.getParam2();
788 bool H_merge
= ! bool(param2
& 128);
789 bool V_merge
= ! bool(param2
& 64);
790 param2
= param2
& 63;
792 u16 l
= getInteriorLight(n
, 1, nodedef
);
793 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
794 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 );
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-
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-
822 // table of node visible faces, 0 = invisible
823 int visible_faces
[6] = {0,0,0,0,0,0};
825 // table of neighbours, 1 = same type, checked with g_26dirs
826 int nb
[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
828 // g_26dirs to check when only horizontal merge is allowed
829 int nb_H_dirs
[8] = {0,2,3,5,10,11,12,13};
831 content_t current
= n
.getContent();
836 // neighbours checks for frames visibility
838 if (!H_merge
&& V_merge
) {
839 n2p
= blockpos_nodes
+ p
+ g_26dirs
[1];
840 n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
841 n2c
= n2
.getContent();
842 if (n2c
== current
|| n2c
== CONTENT_IGNORE
)
844 n2p
= blockpos_nodes
+ p
+ g_26dirs
[4];
845 n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
846 n2c
= n2
.getContent();
847 if (n2c
== current
|| n2c
== CONTENT_IGNORE
)
849 } else if (H_merge
&& !V_merge
) {
850 for(i
= 0; i
< 8; i
++) {
851 n2p
= blockpos_nodes
+ p
+ g_26dirs
[nb_H_dirs
[i
]];
852 n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
853 n2c
= n2
.getContent();
854 if (n2c
== current
|| n2c
== CONTENT_IGNORE
)
855 nb
[nb_H_dirs
[i
]] = 1;
857 } else if (H_merge
&& V_merge
) {
858 for(i
= 0; i
< 18; i
++) {
859 n2p
= blockpos_nodes
+ p
+ g_26dirs
[i
];
860 n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
861 n2c
= n2
.getContent();
862 if (n2c
== current
|| n2c
== CONTENT_IGNORE
)
867 // faces visibility checks
870 visible_faces
[0] = 1;
871 visible_faces
[1] = 1;
873 for(i
= 0; i
< 2; i
++) {
874 n2p
= blockpos_nodes
+ p
+ dirs
[i
];
875 n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
876 n2c
= n2
.getContent();
878 visible_faces
[i
] = 1;
883 visible_faces
[2] = 1;
884 visible_faces
[3] = 1;
885 visible_faces
[4] = 1;
886 visible_faces
[5] = 1;
888 for(i
= 2; i
< 6; i
++) {
889 n2p
= blockpos_nodes
+ p
+ dirs
[i
];
890 n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
891 n2c
= n2
.getContent();
893 visible_faces
[i
] = 1;
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
903 f32 tx1
, ty1
, tz1
, tx2
, ty2
, tz2
;
906 for(i
= 0; i
< 12; i
++)
909 if (nb
[nb_triplet
[i
*3+2]])
910 edge_invisible
= nb
[nb_triplet
[i
*3]] & nb
[nb_triplet
[i
*3+1]];
912 edge_invisible
= nb
[nb_triplet
[i
*3]] ^ nb
[nb_triplet
[i
*3+1]];
915 box
= frame_edges
[i
];
918 tx1
= (box
.MinEdge
.X
/ BS
) + 0.5;
919 ty1
= (box
.MinEdge
.Y
/ BS
) + 0.5;
920 tz1
= (box
.MinEdge
.Z
/ BS
) + 0.5;
921 tx2
= (box
.MaxEdge
.X
/ BS
) + 0.5;
922 ty2
= (box
.MaxEdge
.Y
/ BS
) + 0.5;
923 tz2
= (box
.MaxEdge
.Z
/ BS
) + 0.5;
925 tx1
, 1-tz2
, tx2
, 1-tz1
,
927 tz1
, 1-ty2
, tz2
, 1-ty1
,
928 1-tz2
, 1-ty2
, 1-tz1
, 1-ty1
,
929 1-tx2
, 1-ty2
, 1-tx1
, 1-ty1
,
930 tx1
, 1-ty2
, tx2
, 1-ty1
,
932 makeCuboid(&collector
, box
, &tiles
[0], 1, c
, txc1
);
935 for(i
= 0; i
< 6; i
++)
937 if (!visible_faces
[i
])
939 box
= glass_faces
[i
];
942 tx1
= (box
.MinEdge
.X
/ BS
) + 0.5;
943 ty1
= (box
.MinEdge
.Y
/ BS
) + 0.5;
944 tz1
= (box
.MinEdge
.Z
/ BS
) + 0.5;
945 tx2
= (box
.MaxEdge
.X
/ BS
) + 0.5;
946 ty2
= (box
.MaxEdge
.Y
/ BS
) + 0.5;
947 tz2
= (box
.MaxEdge
.Z
/ BS
) + 0.5;
949 tx1
, 1-tz2
, tx2
, 1-tz1
,
951 tz1
, 1-ty2
, tz2
, 1-ty1
,
952 1-tz2
, 1-ty2
, 1-tz1
, 1-ty1
,
953 1-tx2
, 1-ty2
, 1-tx1
, 1-ty1
,
954 tx1
, 1-ty2
, tx2
, 1-ty1
,
956 makeCuboid(&collector
, box
, &glass_tiles
[i
], 1, c
, txc2
);
959 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 float vlev
= (((float)param2
/ 63.0 ) * 2.0 - 1.0);
963 TileSpec interior_tiles
[6];
964 for (i
= 0; i
< 6; i
++)
965 interior_tiles
[i
] = f
.special_tiles
[0];
966 float offset
= 0.003;
967 box
= aabb3f(visible_faces
[3] ? -b
: -a
+ offset
,
968 visible_faces
[1] ? -b
: -a
+ offset
,
969 visible_faces
[5] ? -b
: -a
+ offset
,
970 visible_faces
[2] ? b
: a
- offset
,
971 visible_faces
[0] ? b
* vlev
: a
* vlev
- offset
,
972 visible_faces
[4] ? b
: a
- offset
);
975 tx1
= (box
.MinEdge
.X
/ BS
) + 0.5;
976 ty1
= (box
.MinEdge
.Y
/ BS
) + 0.5;
977 tz1
= (box
.MinEdge
.Z
/ BS
) + 0.5;
978 tx2
= (box
.MaxEdge
.X
/ BS
) + 0.5;
979 ty2
= (box
.MaxEdge
.Y
/ BS
) + 0.5;
980 tz2
= (box
.MaxEdge
.Z
/ BS
) + 0.5;
982 tx1
, 1-tz2
, tx2
, 1-tz1
,
984 tz1
, 1-ty2
, tz2
, 1-ty1
,
985 1-tz2
, 1-ty2
, 1-tz1
, 1-ty1
,
986 1-tx2
, 1-ty2
, 1-tx1
, 1-ty1
,
987 tx1
, 1-ty2
, tx2
, 1-ty1
,
989 makeCuboid(&collector
, box
, interior_tiles
, 6, c
, txc3
);
994 TileSpec tile_leaves
= getNodeTile(n
, p
,
997 u16 l
= getInteriorLight(n
, 1, nodedef
);
998 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1000 v3f pos
= intToFloat(p
, BS
);
1001 aabb3f
box(-BS
/2,-BS
/2,-BS
/2,BS
/2,BS
/2,BS
/2);
1004 makeCuboid(&collector
, box
, &tile_leaves
, 1, c
, NULL
);
1006 case NDT_ALLFACES_OPTIONAL
:
1007 // This is always pre-converted to something else
1008 FATAL_ERROR("NDT_ALLFACES_OPTIONAL not pre-converted");
1012 v3s16 dir
= n
.getWallMountedDir(nodedef
);
1015 if(dir
== v3s16(0,-1,0)){
1016 tileindex
= 0; // floor
1017 } else if(dir
== v3s16(0,1,0)){
1018 tileindex
= 1; // ceiling
1019 // For backwards compatibility
1020 } else if(dir
== v3s16(0,0,0)){
1021 tileindex
= 0; // floor
1023 tileindex
= 2; // side
1026 TileSpec tile
= getNodeTileN(n
, p
, tileindex
, data
);
1027 tile
.material_flags
&= ~MATERIAL_FLAG_BACKFACE_CULLING
;
1028 tile
.material_flags
|= MATERIAL_FLAG_CRACK_OVERLAY
;
1030 u16 l
= getInteriorLight(n
, 1, nodedef
);
1031 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1033 float s
= BS
/2*f
.visual_scale
;
1034 // Wall at X+ of node
1035 video::S3DVertex vertices
[4] =
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),
1043 for(s32 i
=0; i
<4; i
++)
1045 if(dir
== v3s16(1,0,0))
1046 vertices
[i
].Pos
.rotateXZBy(0);
1047 if(dir
== v3s16(-1,0,0))
1048 vertices
[i
].Pos
.rotateXZBy(180);
1049 if(dir
== v3s16(0,0,1))
1050 vertices
[i
].Pos
.rotateXZBy(90);
1051 if(dir
== v3s16(0,0,-1))
1052 vertices
[i
].Pos
.rotateXZBy(-90);
1053 if(dir
== v3s16(0,-1,0))
1054 vertices
[i
].Pos
.rotateXZBy(45);
1055 if(dir
== v3s16(0,1,0))
1056 vertices
[i
].Pos
.rotateXZBy(-45);
1058 vertices
[i
].Pos
+= intToFloat(p
, BS
);
1061 u16 indices
[] = {0,1,2,2,3,0};
1062 // Add to mesh collector
1063 collector
.append(tile
, vertices
, 4, indices
, 6);
1067 TileSpec tile
= getNodeTileN(n
, p
, 0, data
);
1068 tile
.material_flags
&= ~MATERIAL_FLAG_BACKFACE_CULLING
;
1069 tile
.material_flags
|= MATERIAL_FLAG_CRACK_OVERLAY
;
1071 u16 l
= getInteriorLight(n
, 0, nodedef
);
1072 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1074 float d
= (float)BS
/16;
1075 float s
= BS
/2*f
.visual_scale
;
1076 // Wall at X+ of node
1077 video::S3DVertex vertices
[4] =
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),
1085 v3s16 dir
= n
.getWallMountedDir(nodedef
);
1087 for(s32 i
=0; i
<4; i
++)
1089 if(dir
== v3s16(1,0,0))
1090 vertices
[i
].Pos
.rotateXZBy(0);
1091 if(dir
== v3s16(-1,0,0))
1092 vertices
[i
].Pos
.rotateXZBy(180);
1093 if(dir
== v3s16(0,0,1))
1094 vertices
[i
].Pos
.rotateXZBy(90);
1095 if(dir
== v3s16(0,0,-1))
1096 vertices
[i
].Pos
.rotateXZBy(-90);
1097 if(dir
== v3s16(0,-1,0))
1098 vertices
[i
].Pos
.rotateXYBy(-90);
1099 if(dir
== v3s16(0,1,0))
1100 vertices
[i
].Pos
.rotateXYBy(90);
1102 vertices
[i
].Pos
+= intToFloat(p
, BS
);
1105 u16 indices
[] = {0,1,2,2,3,0};
1106 // Add to mesh collector
1107 collector
.append(tile
, vertices
, 4, indices
, 6);
1111 TileSpec tile
= getNodeTileN(n
, p
, 0, data
);
1112 tile
.material_flags
|= MATERIAL_FLAG_CRACK_OVERLAY
;
1114 u16 l
= getInteriorLight(n
, 1, nodedef
);
1115 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1117 float s
= BS
/ 2 * f
.visual_scale
;
1119 for (int j
= 0; j
< 2; j
++)
1121 video::S3DVertex vertices
[4] =
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 video::S3DVertex( s
,-BS
/2 + s
*2,0, 0,0,0, c
, 1,0),
1126 video::S3DVertex(-s
,-BS
/2 + s
*2,0, 0,0,0, c
, 0,0),
1128 float rotate_degree
= 0;
1129 if (f
.param_type_2
== CPT2_DEGROTATE
)
1130 rotate_degree
= n
.param2
* 2;
1133 for(u16 i
= 0; i
< 4; i
++)
1134 vertices
[i
].Pos
.rotateXZBy(46 + rotate_degree
);
1135 } else if (j
== 1) {
1136 for(u16 i
= 0; i
< 4; i
++)
1137 vertices
[i
].Pos
.rotateXZBy(-44 + rotate_degree
);
1140 for (int i
= 0; i
< 4; i
++)
1142 vertices
[i
].Pos
*= f
.visual_scale
;
1143 vertices
[i
].Pos
.Y
+= BS
/2 * (f
.visual_scale
- 1);
1144 vertices
[i
].Pos
+= intToFloat(p
, BS
);
1147 u16 indices
[] = {0, 1, 2, 2, 3, 0};
1148 // Add to mesh collector
1149 collector
.append(tile
, vertices
, 4, indices
, 6);
1154 TileSpec tile
= getNodeTileN(n
, p
, 0, data
);
1155 tile
.material_flags
|= MATERIAL_FLAG_CRACK_OVERLAY
;
1157 u16 l
= getInteriorLight(n
, 1, nodedef
);
1158 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1160 float s
= BS
/ 2 * f
.visual_scale
;
1162 content_t current
= n
.getContent();
1167 static const v3s16 dirs
[6] = {
1176 int doDraw
[6] = {0, 0, 0, 0, 0, 0};
1178 bool drawAllFaces
= true;
1180 // Check for adjacent nodes
1181 for (int i
= 0; i
< 6; i
++) {
1182 n2p
= blockpos_nodes
+ p
+ dirs
[i
];
1183 n2
= data
->m_vmanip
.getNodeNoEx(n2p
);
1184 n2c
= n2
.getContent();
1185 if (n2c
!= CONTENT_IGNORE
&& n2c
!= CONTENT_AIR
&& n2c
!= current
) {
1188 drawAllFaces
= false;
1193 for (int j
= 0; j
< 6; j
++) {
1195 video::S3DVertex vertices
[4] = {
1196 video::S3DVertex(-s
, -BS
/ 2, 0, 0, 0, 0, c
, 0, 1),
1197 video::S3DVertex( s
, -BS
/ 2, 0, 0, 0, 0, c
, 1, 1),
1198 video::S3DVertex( s
, -BS
/ 2 + s
* 2, 0, 0, 0, 0, c
, 1, 0),
1199 video::S3DVertex(-s
, -BS
/ 2 + s
* 2, 0, 0, 0, 0, c
, 0, 0),
1202 // Calculate which faces should be drawn, (top or sides)
1203 if (j
== 0 && (drawAllFaces
||
1204 (doDraw
[3] == 1 || doDraw
[1] == 1))) {
1205 for (int i
= 0; i
< 4; i
++) {
1206 vertices
[i
].Pos
.rotateXZBy(90);
1207 vertices
[i
].Pos
.rotateXYBy(-10);
1208 vertices
[i
].Pos
.X
-= 4.0;
1210 } else if (j
== 1 && (drawAllFaces
||
1211 (doDraw
[5] == 1 || doDraw
[1] == 1))) {
1212 for (int i
= 0; i
< 4; i
++) {
1213 vertices
[i
].Pos
.rotateXZBy(180);
1214 vertices
[i
].Pos
.rotateYZBy(10);
1215 vertices
[i
].Pos
.Z
-= 4.0;
1217 } else if (j
== 2 && (drawAllFaces
||
1218 (doDraw
[2] == 1 || doDraw
[1] == 1))) {
1219 for (int i
= 0; i
< 4; i
++) {
1220 vertices
[i
].Pos
.rotateXZBy(270);
1221 vertices
[i
].Pos
.rotateXYBy(10);
1222 vertices
[i
].Pos
.X
+= 4.0;
1224 } else if (j
== 3 && (drawAllFaces
||
1225 (doDraw
[4] == 1 || doDraw
[1] == 1))) {
1226 for (int i
= 0; i
< 4; i
++) {
1227 vertices
[i
].Pos
.rotateYZBy(-10);
1228 vertices
[i
].Pos
.Z
+= 4.0;
1230 // Center cross-flames
1231 } else if (j
== 4 && (drawAllFaces
|| doDraw
[1] == 1)) {
1232 for (int i
= 0; i
< 4; i
++) {
1233 vertices
[i
].Pos
.rotateXZBy(45);
1235 } else if (j
== 5 && (drawAllFaces
|| doDraw
[1] == 1)) {
1236 for (int i
= 0; i
< 4; i
++) {
1237 vertices
[i
].Pos
.rotateXZBy(-45);
1239 // Render flames on bottom of node above
1240 } else if (j
== 0 && doDraw
[0] == 1 && doDraw
[1] == 0) {
1241 for (int i
= 0; i
< 4; i
++) {
1242 vertices
[i
].Pos
.rotateYZBy(70);
1243 vertices
[i
].Pos
.rotateXZBy(90);
1244 vertices
[i
].Pos
.Y
+= 4.84;
1245 vertices
[i
].Pos
.X
-= 4.7;
1247 } else if (j
== 1 && doDraw
[0] == 1 && doDraw
[1] == 0) {
1248 for (int i
= 0; i
< 4; i
++) {
1249 vertices
[i
].Pos
.rotateYZBy(70);
1250 vertices
[i
].Pos
.rotateXZBy(180);
1251 vertices
[i
].Pos
.Y
+= 4.84;
1252 vertices
[i
].Pos
.Z
-= 4.7;
1254 } else if (j
== 2 && doDraw
[0] == 1 && doDraw
[1] == 0) {
1255 for (int i
= 0; i
< 4; i
++) {
1256 vertices
[i
].Pos
.rotateYZBy(70);
1257 vertices
[i
].Pos
.rotateXZBy(270);
1258 vertices
[i
].Pos
.Y
+= 4.84;
1259 vertices
[i
].Pos
.X
+= 4.7;
1261 } else if (j
== 3 && doDraw
[0] == 1 && doDraw
[1] == 0) {
1262 for (int i
= 0; i
< 4; i
++) {
1263 vertices
[i
].Pos
.rotateYZBy(70);
1264 vertices
[i
].Pos
.Y
+= 4.84;
1265 vertices
[i
].Pos
.Z
+= 4.7;
1268 // Skip faces that aren't adjacent to a node
1272 for (int i
= 0; i
< 4; i
++) {
1273 vertices
[i
].Pos
*= f
.visual_scale
;
1274 vertices
[i
].Pos
+= intToFloat(p
, BS
);
1277 u16 indices
[] = {0, 1, 2, 2, 3, 0};
1278 // Add to mesh collector
1279 collector
.append(tile
, vertices
, 4, indices
, 6);
1284 TileSpec tile
= getNodeTile(n
, p
, v3s16(0,0,0), data
);
1285 TileSpec tile_nocrack
= tile
;
1286 tile_nocrack
.material_flags
&= ~MATERIAL_FLAG_CRACK
;
1288 // Put wood the right way around in the posts
1289 TileSpec tile_rot
= tile
;
1290 tile_rot
.rotation
= 1;
1292 u16 l
= getInteriorLight(n
, 1, nodedef
);
1293 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1295 const f32 post_rad
=(f32
)BS
/8;
1296 const f32 bar_rad
=(f32
)BS
/16;
1297 const f32 bar_len
=(f32
)(BS
/2)-post_rad
;
1299 v3f pos
= intToFloat(p
, BS
);
1301 // The post - always present
1302 aabb3f
post(-post_rad
,-BS
/2,-post_rad
,post_rad
,BS
/2,post_rad
);
1303 post
.MinEdge
+= pos
;
1304 post
.MaxEdge
+= pos
;
1306 6/16.,6/16.,10/16.,10/16.,
1307 6/16.,6/16.,10/16.,10/16.,
1312 makeCuboid(&collector
, post
, &tile_rot
, 1, c
, postuv
);
1314 // Now a section of fence, +X, if there's a post there
1317 MapNode n2
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p2
);
1318 const ContentFeatures
*f2
= &nodedef
->get(n2
);
1319 if(f2
->drawtype
== NDT_FENCELIKE
)
1321 aabb3f
bar(-bar_len
+BS
/2,-bar_rad
+BS
/4,-bar_rad
,
1322 bar_len
+BS
/2,bar_rad
+BS
/4,bar_rad
);
1326 0/16.,2/16.,16/16.,4/16.,
1327 0/16.,4/16.,16/16.,6/16.,
1328 6/16.,6/16.,8/16.,8/16.,
1329 10/16.,10/16.,12/16.,12/16.,
1330 0/16.,8/16.,16/16.,10/16.,
1331 0/16.,14/16.,16/16.,16/16.};
1332 makeCuboid(&collector
, bar
, &tile_nocrack
, 1,
1334 bar
.MinEdge
.Y
-= BS
/2;
1335 bar
.MaxEdge
.Y
-= BS
/2;
1336 makeCuboid(&collector
, bar
, &tile_nocrack
, 1,
1340 // Now a section of fence, +Z, if there's a post there
1343 n2
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p2
);
1344 f2
= &nodedef
->get(n2
);
1345 if(f2
->drawtype
== NDT_FENCELIKE
)
1347 aabb3f
bar(-bar_rad
,-bar_rad
+BS
/4,-bar_len
+BS
/2,
1348 bar_rad
,bar_rad
+BS
/4,bar_len
+BS
/2);
1352 3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
1353 4/16.,1/16.,6/16.,5/16., // for wood texture instead
1354 0/16.,9/16.,16/16.,11/16.,
1355 0/16.,6/16.,16/16.,8/16.,
1356 6/16.,6/16.,8/16.,8/16.,
1357 10/16.,10/16.,12/16.,12/16.};
1358 makeCuboid(&collector
, bar
, &tile_nocrack
, 1,
1360 bar
.MinEdge
.Y
-= BS
/2;
1361 bar
.MaxEdge
.Y
-= BS
/2;
1362 makeCuboid(&collector
, bar
, &tile_nocrack
, 1,
1368 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) */
1371 content_t thiscontent
= n
.getContent();
1372 std::string groupname
= "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
1373 int self_group
= ((ItemGroupList
) nodedef
->get(n
).groups
)[groupname
];
1376 for (s8 y0
= -1; y0
<= 1; y0
++) {
1377 // Prevent from indexing never used coordinates
1378 for (s8 xz
= -1; xz
<= 1; xz
++) {
1381 MapNode n_xy
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ v3s16(x
+ xz
, y
+ y0
, z
));
1382 MapNode n_zy
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ v3s16(x
, y
+ y0
, z
+ xz
));
1383 ContentFeatures def_xy
= nodedef
->get(n_xy
);
1384 ContentFeatures def_zy
= nodedef
->get(n_zy
);
1386 // Check if current node would connect with the rail
1387 is_rail_x
[index
] = ((def_xy
.drawtype
== NDT_RAILLIKE
1388 && ((ItemGroupList
) def_xy
.groups
)[groupname
] == self_group
)
1389 || n_xy
.getContent() == thiscontent
);
1391 is_rail_z
[index
] = ((def_zy
.drawtype
== NDT_RAILLIKE
1392 && ((ItemGroupList
) def_zy
.groups
)[groupname
] == self_group
)
1393 || n_zy
.getContent() == thiscontent
);
1398 bool is_rail_x_all
[2]; // [0] = negative x, [1] = positive x coordinate from the current node position
1399 bool is_rail_z_all
[2];
1400 is_rail_x_all
[0] = is_rail_x
[0] || is_rail_x
[2] || is_rail_x
[4];
1401 is_rail_x_all
[1] = is_rail_x
[1] || is_rail_x
[3] || is_rail_x
[5];
1402 is_rail_z_all
[0] = is_rail_z
[0] || is_rail_z
[2] || is_rail_z
[4];
1403 is_rail_z_all
[1] = is_rail_z
[1] || is_rail_z
[3] || is_rail_z
[5];
1405 // reasonable default, flat straight unrotated rail
1406 bool is_straight
= true;
1407 int adjacencies
= 0;
1411 // check for sloped rail
1412 if (is_rail_x
[4] || is_rail_x
[5] || is_rail_z
[4] || is_rail_z
[5]) {
1413 adjacencies
= 5; // 5 means sloped
1414 is_straight
= true; // sloped is always straight
1416 // is really straight, rails on both sides
1417 is_straight
= (is_rail_x_all
[0] && is_rail_x_all
[1]) || (is_rail_z_all
[0] && is_rail_z_all
[1]);
1418 adjacencies
= is_rail_x_all
[0] + is_rail_x_all
[1] + is_rail_z_all
[0] + is_rail_z_all
[1];
1421 switch (adjacencies
) {
1423 if (is_rail_x_all
[0] || is_rail_x_all
[1])
1428 tileindex
= 1; // curved
1429 if (is_rail_x_all
[0] && is_rail_x_all
[1])
1431 if (is_rail_z_all
[0] && is_rail_z_all
[1]) {
1435 else if (is_rail_x_all
[0] && is_rail_z_all
[0])
1437 else if (is_rail_x_all
[0] && is_rail_z_all
[1])
1439 else if (is_rail_x_all
[1] && is_rail_z_all
[1])
1443 // here is where the potential to 'switch' a junction is, but not implemented at present
1444 tileindex
= 2; // t-junction
1445 if(!is_rail_x_all
[1])
1447 if(!is_rail_z_all
[0])
1449 if(!is_rail_z_all
[1])
1453 tileindex
= 3; // crossing
1467 TileSpec tile
= getNodeTileN(n
, p
, tileindex
, data
);
1468 tile
.material_flags
&= ~MATERIAL_FLAG_BACKFACE_CULLING
;
1469 tile
.material_flags
|= MATERIAL_FLAG_CRACK_OVERLAY
;
1471 u16 l
= getInteriorLight(n
, 0, nodedef
);
1472 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1474 float d
= (float)BS
/64;
1478 if (is_rail_x
[4] || is_rail_x
[5] || is_rail_z
[4] || is_rail_z
[5])
1479 g
= 1; //Object is at a slope
1481 video::S3DVertex vertices
[4] =
1483 video::S3DVertex(-s
, -s
+d
,-s
, 0,0,0, c
,0,1),
1484 video::S3DVertex( s
, -s
+d
,-s
, 0,0,0, c
,1,1),
1485 video::S3DVertex( s
, g
*s
+d
, s
, 0,0,0, c
,1,0),
1486 video::S3DVertex(-s
, g
*s
+d
, s
, 0,0,0, c
,0,0),
1489 for(s32 i
=0; i
<4; i
++)
1492 vertices
[i
].Pos
.rotateXZBy(angle
);
1493 vertices
[i
].Pos
+= intToFloat(p
, BS
);
1496 u16 indices
[] = {0,1,2,2,3,0};
1497 collector
.append(tile
, vertices
, 4, indices
, 6);
1501 static const v3s16 tile_dirs
[6] = {
1511 u16 l
= getInteriorLight(n
, 1, nodedef
);
1512 video::SColor c
= MapBlock_LightColor(255, l
, f
.light_source
);
1514 v3f pos
= intToFloat(p
, BS
);
1516 std::vector
<aabb3f
> boxes
= n
.getNodeBoxes(nodedef
);
1517 for(std::vector
<aabb3f
>::iterator
1519 i
!= boxes
.end(); ++i
)
1521 for(int j
= 0; j
< 6; j
++)
1523 // Handles facedir rotation for textures
1524 tiles
[j
] = getNodeTile(n
, p
, tile_dirs
[j
], data
);
1531 if (box
.MinEdge
.X
> box
.MaxEdge
.X
)
1534 box
.MinEdge
.X
=box
.MaxEdge
.X
;
1537 if (box
.MinEdge
.Y
> box
.MaxEdge
.Y
)
1540 box
.MinEdge
.Y
=box
.MaxEdge
.Y
;
1543 if (box
.MinEdge
.Z
> box
.MaxEdge
.Z
)
1546 box
.MinEdge
.Z
=box
.MaxEdge
.Z
;
1551 // Compute texture coords
1552 f32 tx1
= (box
.MinEdge
.X
/BS
)+0.5;
1553 f32 ty1
= (box
.MinEdge
.Y
/BS
)+0.5;
1554 f32 tz1
= (box
.MinEdge
.Z
/BS
)+0.5;
1555 f32 tx2
= (box
.MaxEdge
.X
/BS
)+0.5;
1556 f32 ty2
= (box
.MaxEdge
.Y
/BS
)+0.5;
1557 f32 tz2
= (box
.MaxEdge
.Z
/BS
)+0.5;
1560 tx1
, 1-tz2
, tx2
, 1-tz1
,
1564 tz1
, 1-ty2
, tz2
, 1-ty1
,
1566 1-tz2
, 1-ty2
, 1-tz1
, 1-ty1
,
1568 1-tx2
, 1-ty2
, 1-tx1
, 1-ty1
,
1570 tx1
, 1-ty2
, tx2
, 1-ty1
,
1572 makeCuboid(&collector
, box
, tiles
, 6, c
, txc
);
1577 v3f pos
= intToFloat(p
, BS
);
1578 video::SColor c
= MapBlock_LightColor(255, getInteriorLight(n
, 1, nodedef
), f
.light_source
);
1581 if (f
.param_type_2
== CPT2_FACEDIR
) {
1582 facedir
= n
.getFaceDir(nodedef
);
1583 } else if (f
.param_type_2
== CPT2_WALLMOUNTED
) {
1584 //convert wallmounted to 6dfacedir.
1585 //when cache enabled, it is already converted
1586 facedir
= n
.getWallMounted(nodedef
);
1587 if (!enable_mesh_cache
) {
1588 static const u8 wm_to_6d
[6] = {20, 0, 16+1, 12+3, 8, 4+2};
1589 facedir
= wm_to_6d
[facedir
];
1593 if (f
.mesh_ptr
[facedir
]) {
1594 // use cached meshes
1595 for(u16 j
= 0; j
< f
.mesh_ptr
[0]->getMeshBufferCount(); j
++) {
1596 scene::IMeshBuffer
*buf
= f
.mesh_ptr
[facedir
]->getMeshBuffer(j
);
1597 collector
.append(getNodeTileN(n
, p
, j
, data
),
1598 (video::S3DVertex
*)buf
->getVertices(), buf
->getVertexCount(),
1599 buf
->getIndices(), buf
->getIndexCount(), pos
, c
);
1601 } else if (f
.mesh_ptr
[0]) {
1602 // no cache, clone and rotate mesh
1603 scene::IMesh
* mesh
= cloneMesh(f
.mesh_ptr
[0]);
1604 rotateMeshBy6dFacedir(mesh
, facedir
);
1605 recalculateBoundingBox(mesh
);
1606 meshmanip
->recalculateNormals(mesh
, true, false);
1607 for(u16 j
= 0; j
< mesh
->getMeshBufferCount(); j
++) {
1608 scene::IMeshBuffer
*buf
= mesh
->getMeshBuffer(j
);
1609 collector
.append(getNodeTileN(n
, p
, j
, data
),
1610 (video::S3DVertex
*)buf
->getVertices(), buf
->getVertexCount(),
1611 buf
->getIndices(), buf
->getIndexCount(), pos
, c
);
1620 Caused by incorrect alpha blending, selection mesh needs to be created as
1621 last element to ensure it gets blended correct over nodes with alpha channel
1623 // Create selection mesh
1624 v3s16 p
= data
->m_highlighted_pos_relative
;
1625 if (data
->m_show_hud
&&
1626 (p
.X
>= 0) && (p
.X
< MAP_BLOCKSIZE
) &&
1627 (p
.Y
>= 0) && (p
.Y
< MAP_BLOCKSIZE
) &&
1628 (p
.Z
>= 0) && (p
.Z
< MAP_BLOCKSIZE
)) {
1630 MapNode n
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p
);
1631 if(n
.getContent() != CONTENT_AIR
) {
1632 // Get selection mesh light level
1633 static const v3s16 dirs
[7] = {
1645 for (u8 i
= 0; i
< 7; i
++) {
1646 MapNode n1
= data
->m_vmanip
.getNodeNoEx(blockpos_nodes
+ p
+ dirs
[i
]);
1647 l1
= getInteriorLight(n1
, -4, nodedef
);
1651 video::SColor c
= MapBlock_LightColor(255, l
, 0);
1652 data
->m_highlight_mesh_color
= c
;
1653 std::vector
<aabb3f
> boxes
= n
.getSelectionBoxes(nodedef
);
1655 h_tile
.material_flags
|= MATERIAL_FLAG_HIGHLIGHTED
;
1656 h_tile
.texture
= tsrc
->getTextureForMesh("halo.png",&h_tile
.texture_id
);
1657 v3f pos
= intToFloat(p
, BS
);
1659 for (std::vector
<aabb3f
>::iterator i
= boxes
.begin();
1660 i
!= boxes
.end(); ++i
) {
1662 box
.MinEdge
+= v3f(-d
, -d
, -d
) + pos
;
1663 box
.MaxEdge
+= v3f(d
, d
, d
) + pos
;
1664 makeCuboid(&collector
, box
, &h_tile
, 1, c
, NULL
);