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 "mapblock_mesh.h"
24 #include "main.h" // for g_profiler
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
35 static void applyFacesShading(video::SColor& color, float factor)
37 color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
38 color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
45 MeshMakeData::MeshMakeData(IGameDef *gamedef):
47 m_blockpos(-1337,-1337,-1337),
48 m_crack_pos_relative(-1337, -1337, -1337),
49 m_highlighted_pos_relative(-1337, -1337, -1337),
50 m_smooth_lighting(false),
52 m_highlight_mesh_color(255, 255, 255, 255),
56 void MeshMakeData::fill(MapBlock *block)
58 m_blockpos = block->getPos();
60 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
66 // Allocate this block + neighbors
68 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
69 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
72 //TimeTaker timer("copy central block data");
76 block->copyTo(m_vmanip);
79 //TimeTaker timer("copy neighbor block data");
83 Copy neighbors. This is lightning fast.
84 Copying only the borders would be *very* slow.
88 Map *map = block->getParent();
90 for(u16 i=0; i<26; i++)
92 const v3s16 &dir = g_26dirs[i];
93 v3s16 bp = m_blockpos + dir;
94 MapBlock *b = map->getBlockNoCreateNoEx(bp);
101 void MeshMakeData::fillSingleNode(MapNode *node)
103 m_blockpos = v3s16(0,0,0);
105 v3s16 blockpos_nodes = v3s16(0,0,0);
106 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
107 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
108 s32 volume = area.getVolume();
109 s32 our_node_index = area.index(1,1,1);
111 // Allocate this block + neighbors
113 m_vmanip.addArea(area);
116 MapNode *data = new MapNode[volume];
117 for(s32 i = 0; i < volume; i++)
119 if(i == our_node_index)
125 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
128 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
132 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
135 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
138 void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
140 m_show_hud = show_hud;
141 m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
144 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
146 m_smooth_lighting = smooth_lighting;
150 Light and vertex color functions
154 Calculate non-smooth lighting at interior of node.
157 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
158 INodeDefManager *ndef)
160 u8 light = n.getLight(bank, ndef);
164 light = undiminish_light(light);
169 light = diminish_light(light);
173 return decode_light(light);
177 Calculate non-smooth lighting at interior of node.
180 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
182 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
183 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
184 return day | (night << 8);
188 Calculate non-smooth lighting at face of node.
191 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
192 v3s16 face_dir, INodeDefManager *ndef)
195 u8 l1 = n.getLight(bank, ndef);
196 u8 l2 = n2.getLight(bank, ndef);
202 // Boost light level for light sources
203 u8 light_source = MYMAX(ndef->get(n).light_source,
204 ndef->get(n2).light_source);
205 if(light_source > light)
206 light = light_source;
208 return decode_light(light);
212 Calculate non-smooth lighting at face of node.
215 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
217 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
218 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
219 return day | (night << 8);
223 Calculate smooth lighting at the XYZ- corner of p.
226 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
228 static v3s16 dirs8[8] = {
239 INodeDefManager *ndef = data->m_gamedef->ndef();
241 u16 ambient_occlusion = 0;
244 u8 light_source_max = 0;
245 for(u32 i=0; i<8; i++)
247 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
249 // if it's CONTENT_IGNORE we can't do any light calculations
250 if (n.getContent() == CONTENT_IGNORE) {
254 const ContentFeatures &f = ndef->get(n);
255 if(f.light_source > light_source_max)
256 light_source_max = f.light_source;
257 // Check f.solidness because fast-style leaves look
259 if(f.param_type == CPT_LIGHT && f.solidness != 2)
261 light += decode_light(n.getLight(bank, ndef));
272 light /= light_count;
274 // Boost brightness around light sources
275 if(decode_light(light_source_max) >= light)
276 //return decode_light(undiminish_light(light_source_max));
277 return decode_light(light_source_max);
279 if(ambient_occlusion > 4)
281 //calculate table index for gamma space multiplier
282 ambient_occlusion -= 5;
283 //table of precalculated gamma space multiply factors
284 //light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2)
285 const float light_amount[4] = {0.877424315, 0.729740053, 0.532520545, 0.0};
286 light = core::clamp(core::round32(light*light_amount[ambient_occlusion]), 0, 255);
293 Calculate smooth lighting at the XYZ- corner of p.
296 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
298 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
299 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
300 return day | (night << 8);
304 Calculate smooth lighting at the given corner of p.
307 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
309 if(corner.X == 1) p.X += 1;
310 else assert(corner.X == -1);
311 if(corner.Y == 1) p.Y += 1;
312 else assert(corner.Y == -1);
313 if(corner.Z == 1) p.Z += 1;
314 else assert(corner.Z == -1);
316 return getSmoothLight(p, data);
320 Converts from day + night color values (0..255)
321 and a given daynight_ratio to the final SColor shown on screen.
323 void finalColorBlend(video::SColor& result,
324 u8 day, u8 night, u32 daynight_ratio)
326 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
330 b += (day - night) / 13;
331 rg -= (day - night) / 23;
333 // Emphase blue a bit in darker places
334 // Each entry of this array represents a range of 8 blue levels
335 static const u8 emphase_blue_when_dark[32] = {
336 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
337 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
339 b += emphase_blue_when_dark[b / 8];
340 b = irr::core::clamp (b, 0, 255);
342 // Artificial light is yellow-ish
343 static const u8 emphase_yellow_when_artificial[16] = {
344 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
346 rg += emphase_yellow_when_artificial[night/16];
347 rg = irr::core::clamp (rg, 0, 255);
355 Mesh generation helpers
359 vertex_dirs: v3s16[4]
361 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
364 If looked from outside the node towards the face, the corners are:
370 if(dir == v3s16(0,0,1))
372 // If looking towards z+, this is the face that is behind
373 // the center point, facing towards z+.
374 vertex_dirs[0] = v3s16(-1,-1, 1);
375 vertex_dirs[1] = v3s16( 1,-1, 1);
376 vertex_dirs[2] = v3s16( 1, 1, 1);
377 vertex_dirs[3] = v3s16(-1, 1, 1);
379 else if(dir == v3s16(0,0,-1))
382 vertex_dirs[0] = v3s16( 1,-1,-1);
383 vertex_dirs[1] = v3s16(-1,-1,-1);
384 vertex_dirs[2] = v3s16(-1, 1,-1);
385 vertex_dirs[3] = v3s16( 1, 1,-1);
387 else if(dir == v3s16(1,0,0))
390 vertex_dirs[0] = v3s16( 1,-1, 1);
391 vertex_dirs[1] = v3s16( 1,-1,-1);
392 vertex_dirs[2] = v3s16( 1, 1,-1);
393 vertex_dirs[3] = v3s16( 1, 1, 1);
395 else if(dir == v3s16(-1,0,0))
398 vertex_dirs[0] = v3s16(-1,-1,-1);
399 vertex_dirs[1] = v3s16(-1,-1, 1);
400 vertex_dirs[2] = v3s16(-1, 1, 1);
401 vertex_dirs[3] = v3s16(-1, 1,-1);
403 else if(dir == v3s16(0,1,0))
405 // faces towards Y+ (assume Z- as "down" in texture)
406 vertex_dirs[0] = v3s16( 1, 1,-1);
407 vertex_dirs[1] = v3s16(-1, 1,-1);
408 vertex_dirs[2] = v3s16(-1, 1, 1);
409 vertex_dirs[3] = v3s16( 1, 1, 1);
411 else if(dir == v3s16(0,-1,0))
413 // faces towards Y- (assume Z+ as "down" in texture)
414 vertex_dirs[0] = v3s16( 1,-1, 1);
415 vertex_dirs[1] = v3s16(-1,-1, 1);
416 vertex_dirs[2] = v3s16(-1,-1,-1);
417 vertex_dirs[3] = v3s16( 1,-1,-1);
424 video::S3DVertex vertices[4]; // Precalculated vertices
427 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
428 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
432 // Position is at the center of the cube.
441 v3s16 vertex_dirs[4];
442 getNodeVertexDirs(dir, vertex_dirs);
446 switch (tile.rotation)
452 vertex_dirs[0] = vertex_dirs[3];
453 vertex_dirs[3] = vertex_dirs[2];
454 vertex_dirs[2] = vertex_dirs[1];
464 vertex_dirs[0] = vertex_dirs[2];
467 vertex_dirs[1] = vertex_dirs[3];
478 vertex_dirs[0] = vertex_dirs[1];
479 vertex_dirs[1] = vertex_dirs[2];
480 vertex_dirs[2] = vertex_dirs[3];
490 vertex_dirs[0] = vertex_dirs[3];
491 vertex_dirs[3] = vertex_dirs[2];
492 vertex_dirs[2] = vertex_dirs[1];
504 vertex_dirs[0] = vertex_dirs[1];
505 vertex_dirs[1] = vertex_dirs[2];
506 vertex_dirs[2] = vertex_dirs[3];
518 vertex_dirs[0] = vertex_dirs[3];
519 vertex_dirs[3] = vertex_dirs[2];
520 vertex_dirs[2] = vertex_dirs[1];
532 vertex_dirs[0] = vertex_dirs[1];
533 vertex_dirs[1] = vertex_dirs[2];
534 vertex_dirs[2] = vertex_dirs[3];
556 for(u16 i=0; i<4; i++)
559 BS/2*vertex_dirs[i].X,
560 BS/2*vertex_dirs[i].Y,
561 BS/2*vertex_dirs[i].Z
565 for(u16 i=0; i<4; i++)
567 vertex_pos[i].X *= scale.X;
568 vertex_pos[i].Y *= scale.Y;
569 vertex_pos[i].Z *= scale.Z;
570 vertex_pos[i] += pos;
574 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
575 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
576 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
578 v3f normal(dir.X, dir.Y, dir.Z);
580 u8 alpha = tile.alpha;
582 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
583 MapBlock_LightColor(alpha, li0, light_source),
584 core::vector2d<f32>(x0+w*abs_scale, y0+h));
585 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
586 MapBlock_LightColor(alpha, li1, light_source),
587 core::vector2d<f32>(x0, y0+h));
588 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
589 MapBlock_LightColor(alpha, li2, light_source),
590 core::vector2d<f32>(x0, y0));
591 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
592 MapBlock_LightColor(alpha, li3, light_source),
593 core::vector2d<f32>(x0+w*abs_scale, y0));
596 dest.push_back(face);
600 Nodes make a face if contents differ and solidness differs.
603 1: Face uses m1's content
604 2: Face uses m2's content
605 equivalent: Whether the blocks share the same face (eg. water and glass)
607 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
609 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
610 INodeDefManager *ndef)
614 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
617 bool contents_differ = (m1 != m2);
619 const ContentFeatures &f1 = ndef->get(m1);
620 const ContentFeatures &f2 = ndef->get(m2);
622 // Contents don't differ for different forms of same liquid
623 if(f1.sameLiquid(f2))
624 contents_differ = false;
626 u8 c1 = f1.solidness;
627 u8 c2 = f2.solidness;
629 bool solidness_differs = (c1 != c2);
630 bool makes_face = contents_differ && solidness_differs;
632 if(makes_face == false)
636 c1 = f1.visual_solidness;
638 c2 = f2.visual_solidness;
642 // If same solidness, liquid takes precense
656 Gets nth node tile (0 <= n <= 5).
658 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
660 INodeDefManager *ndef = data->m_gamedef->ndef();
661 TileSpec spec = ndef->get(mn).tiles[tileindex];
662 // Apply temporary crack
663 if (p == data->m_crack_pos_relative)
664 spec.material_flags |= MATERIAL_FLAG_CRACK;
669 Gets node tile given a face direction.
671 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
673 INodeDefManager *ndef = data->m_gamedef->ndef();
675 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
676 // (0,0,1), (0,0,-1) or (0,0,0)
677 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
679 // Convert direction to single integer for table lookup
684 // 4 = invalid, treat as (0,0,0)
688 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
690 // Get rotation for things like chests
691 u8 facedir = mn.getFaceDir(ndef);
694 static const u16 dir_to_tile[24 * 16] =
696 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
697 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
698 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
699 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
700 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
702 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
703 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
704 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
705 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
707 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
708 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
709 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
710 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
712 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
713 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
714 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
715 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
717 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
718 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
719 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
720 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
722 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
723 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
724 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
725 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
728 u16 tile_index=facedir*16 + dir_i;
729 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
730 spec.rotation=dir_to_tile[tile_index + 1];
731 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
735 static void getTileInfo(
743 v3s16 &face_dir_corrected,
749 VoxelManipulator &vmanip = data->m_vmanip;
750 INodeDefManager *ndef = data->m_gamedef->ndef();
751 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
753 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
755 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
756 if (n0.getContent() == CONTENT_IGNORE ) {
760 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
763 bool equivalent = false;
764 u8 mf = face_contents(n0.getContent(), n1.getContent(),
777 tile = getNodeTile(n0, p, face_dir, data);
779 face_dir_corrected = face_dir;
780 light_source = ndef->get(n0).light_source;
784 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
785 p_corrected = p + face_dir;
786 face_dir_corrected = -face_dir;
787 light_source = ndef->get(n1).light_source;
790 // eg. water and glass
792 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
794 if(data->m_smooth_lighting == false)
796 lights[0] = lights[1] = lights[2] = lights[3] =
797 getFaceLight(n0, n1, face_dir, ndef);
801 v3s16 vertex_dirs[4];
802 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
803 for(u16 i=0; i<4; i++)
805 lights[i] = getSmoothLight(
806 blockpos_nodes + p_corrected,
807 vertex_dirs[i], data);
816 translate_dir: unit vector with only one of x, y or z
817 face_dir: unit vector with only one of x, y or z
819 static void updateFastFaceRow(
826 std::vector<FastFace> &dest)
830 u16 continuous_tiles_count = 0;
832 bool makes_face = false;
834 v3s16 face_dir_corrected;
835 u16 lights[4] = {0,0,0,0};
838 getTileInfo(data, p, face_dir,
839 makes_face, p_corrected, face_dir_corrected,
840 lights, tile, light_source);
842 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
844 // If tiling can be done, this is set to false in the next step
845 bool next_is_different = true;
849 bool next_makes_face = false;
850 v3s16 next_p_corrected;
851 v3s16 next_face_dir_corrected;
852 u16 next_lights[4] = {0,0,0,0};
854 u8 next_light_source = 0;
856 // If at last position, there is nothing to compare to and
857 // the face must be drawn anyway
858 if(j != MAP_BLOCKSIZE - 1)
860 p_next = p + translate_dir;
862 getTileInfo(data, p_next, face_dir,
863 next_makes_face, next_p_corrected,
864 next_face_dir_corrected, next_lights,
865 next_tile, next_light_source);
867 if(next_makes_face == makes_face
868 && next_p_corrected == p_corrected + translate_dir
869 && next_face_dir_corrected == face_dir_corrected
870 && next_lights[0] == lights[0]
871 && next_lights[1] == lights[1]
872 && next_lights[2] == lights[2]
873 && next_lights[3] == lights[3]
875 && tile.rotation == 0
876 && next_light_source == light_source)
878 next_is_different = false;
882 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
883 next_makes_face != makes_face ? 1 : 0);
884 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
885 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
886 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
887 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
888 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
889 (next_lights[0] != lights[0] ||
890 next_lights[0] != lights[0] ||
891 next_lights[0] != lights[0] ||
892 next_lights[0] != lights[0]) ? 1 : 0);
893 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
894 !(next_tile == tile) ? 1 : 0);
897 /*g_profiler->add("Meshgen: Total faces checked", 1);
899 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
902 g_profiler->add("Meshgen: diff: last position", 1);*/
905 continuous_tiles_count++;
907 if(next_is_different)
910 Create a face if there should be one
914 // Floating point conversion of the position vector
915 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
916 // Center point of face (kind of)
917 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
918 if(continuous_tiles_count != 1)
919 sp += translate_dir_f;
922 if(translate_dir.X != 0) {
923 scale.X = continuous_tiles_count;
925 if(translate_dir.Y != 0) {
926 scale.Y = continuous_tiles_count;
928 if(translate_dir.Z != 0) {
929 scale.Z = continuous_tiles_count;
932 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
933 sp, face_dir_corrected, scale, light_source,
936 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
937 for(int i = 1; i < continuous_tiles_count; i++){
938 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
942 continuous_tiles_count = 0;
944 makes_face = next_makes_face;
945 p_corrected = next_p_corrected;
946 face_dir_corrected = next_face_dir_corrected;
947 lights[0] = next_lights[0];
948 lights[1] = next_lights[1];
949 lights[2] = next_lights[2];
950 lights[3] = next_lights[3];
952 light_source = next_light_source;
959 static void updateAllFastFaceRows(MeshMakeData *data,
960 std::vector<FastFace> &dest)
963 Go through every y,z and get top(y+) faces in rows of x+
965 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
966 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
967 updateFastFaceRow(data,
971 v3s16(0,1,0), //face dir
978 Go through every x,y and get right(x+) faces in rows of z+
980 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
981 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
982 updateFastFaceRow(data,
986 v3s16(1,0,0), //face dir
993 Go through every y,z and get back(z+) faces in rows of x+
995 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
996 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
997 updateFastFaceRow(data,
1001 v3s16(0,0,1), //face dir
1012 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1013 m_mesh(new scene::SMesh()),
1014 m_gamedef(data->m_gamedef),
1015 m_animation_force_timer(0), // force initial animation
1017 m_crack_materials(),
1018 m_highlighted_materials(),
1019 m_last_daynight_ratio((u32) -1),
1022 m_enable_shaders = g_settings->getBool("enable_shaders");
1023 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1025 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1026 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1027 //TimeTaker timer1("MapBlockMesh()");
1029 std::vector<FastFace> fastfaces_new;
1032 We are including the faces of the trailing edges of the block.
1033 This means that when something changes, the caller must
1034 also update the meshes of the blocks at the leading edges.
1036 NOTE: This is the slowest part of this method.
1039 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1040 //TimeTaker timer2("updateAllFastFaceRows()");
1041 updateAllFastFaceRows(data, fastfaces_new);
1046 Convert FastFaces to MeshCollector
1049 MeshCollector collector;
1052 // avg 0ms (100ms spikes when loading textures the first time)
1053 // (NOTE: probably outdated)
1054 //TimeTaker timer2("MeshCollector building");
1056 for(u32 i=0; i<fastfaces_new.size(); i++)
1058 FastFace &f = fastfaces_new[i];
1060 const u16 indices[] = {0,1,2,2,3,0};
1061 const u16 indices_alternate[] = {0,1,3,2,3,1};
1063 if(f.tile.texture == NULL)
1066 const u16 *indices_p = indices;
1069 Revert triangles for nicer looking gradient if vertices
1070 1 and 3 have same color or 0 and 2 have different color.
1071 getRed() is the day color.
1073 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1074 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1075 indices_p = indices_alternate;
1077 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1082 Add special graphics:
1089 mapblock_mesh_generate_special(data, collector);
1091 m_highlight_mesh_color = data->m_highlight_mesh_color;
1094 Convert MeshCollector to SMesh
1096 ITextureSource *tsrc = m_gamedef->tsrc();
1097 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1099 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1101 PreMeshBuffer &p = collector.prebuffers[i];
1103 // Generate animation data
1105 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1107 // Find the texture name plus ^[crack:N:
1108 std::ostringstream os(std::ios::binary);
1109 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1110 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1111 os<<"o"; // use ^[cracko
1112 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1113 m_crack_materials.insert(std::make_pair(i, os.str()));
1114 // Replace tile texture with the cracked one
1115 p.tile.texture = tsrc->getTexture(
1117 &p.tile.texture_id);
1119 // - Texture animation
1120 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1122 // Add to MapBlockMesh in order to animate these tiles
1123 m_animation_tiles[i] = p.tile;
1124 m_animation_frames[i] = 0;
1125 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1126 // Get starting position from noise
1127 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1128 data->m_blockpos.X, data->m_blockpos.Y,
1129 data->m_blockpos.Z, 0));
1131 // Play all synchronized
1132 m_animation_frame_offsets[i] = 0;
1134 // Replace tile texture with the first animation frame
1135 FrameSpec animation_frame = p.tile.frames.find(0)->second;
1136 p.tile.texture = animation_frame.texture;
1139 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1140 m_highlighted_materials.push_back(i);
1142 for(u32 j = 0; j < p.vertices.size(); j++)
1144 // Note applyFacesShading second parameter is precalculated sqrt
1145 // value for speed improvement
1146 // Skip it for lightsources and top faces.
1147 video::SColor &vc = p.vertices[j].Color;
1148 if (!vc.getBlue()) {
1149 if (p.vertices[j].Normal.Y < -0.5) {
1150 applyFacesShading (vc, 0.447213);
1151 } else if (p.vertices[j].Normal.X > 0.5) {
1152 applyFacesShading (vc, 0.670820);
1153 } else if (p.vertices[j].Normal.X < -0.5) {
1154 applyFacesShading (vc, 0.670820);
1155 } else if (p.vertices[j].Normal.Z > 0.5) {
1156 applyFacesShading (vc, 0.836660);
1157 } else if (p.vertices[j].Normal.Z < -0.5) {
1158 applyFacesShading (vc, 0.836660);
1161 // - Classic lighting
1162 // Set initial real color and store for later updates
1163 u8 day = vc.getRed();
1164 u8 night = vc.getGreen();
1165 finalColorBlend(vc, day, night, 1000);
1166 m_daynight_diffs[i][j] = std::make_pair(day, night);
1170 video::SMaterial material;
1171 material.setFlag(video::EMF_LIGHTING, false);
1172 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1173 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1174 material.setFlag(video::EMF_FOG_ENABLE, true);
1175 material.setTexture(0, p.tile.texture);
1177 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1178 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1180 if (m_enable_shaders) {
1181 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1182 p.tile.applyMaterialOptionsWithShaders(material);
1183 if (p.tile.normal_texture) {
1184 material.setTexture(1, p.tile.normal_texture);
1185 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1187 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1190 p.tile.applyMaterialOptions(material);
1194 // Create meshbuffer
1195 // This is a "Standard MeshBuffer",
1196 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1197 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1199 buf->Material = material;
1201 m_mesh->addMeshBuffer(buf);
1204 buf->append(&p.vertices[0], p.vertices.size(),
1205 &p.indices[0], p.indices.size());
1208 m_camera_offset = camera_offset;
1211 Do some stuff to the mesh
1214 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1219 // Usually 1-700 faces and 1-7 materials
1220 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1221 <<"and uses "<<m_mesh->getMeshBufferCount()
1222 <<" materials (meshbuffers)"<<std::endl;
1225 // Use VBO for mesh (this just would set this for ever buffer)
1226 // This will lead to infinite memory usage because or irrlicht.
1227 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1230 NOTE: If that is enabled, some kind of a queue to the main
1231 thread should be made which would call irrlicht to delete
1232 the hardware buffer and then delete the mesh
1236 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1238 // Check if animation is required for this mesh
1240 !m_crack_materials.empty() ||
1241 !m_daynight_diffs.empty() ||
1242 !m_animation_tiles.empty() ||
1243 !m_highlighted_materials.empty();
1246 MapBlockMesh::~MapBlockMesh()
1252 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1255 if(!m_has_animation)
1257 m_animation_force_timer = 100000;
1261 m_animation_force_timer = myrand_range(5, 100);
1264 if(crack != m_last_crack)
1266 for(std::map<u32, std::string>::iterator
1267 i = m_crack_materials.begin();
1268 i != m_crack_materials.end(); i++)
1270 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1271 std::string basename = i->second;
1273 // Create new texture name from original
1274 ITextureSource *tsrc = m_gamedef->getTextureSource();
1275 std::ostringstream os;
1276 os<<basename<<crack;
1277 u32 new_texture_id = 0;
1278 video::ITexture *new_texture =
1279 tsrc->getTexture(os.str(), &new_texture_id);
1280 buf->getMaterial().setTexture(0, new_texture);
1282 // If the current material is also animated,
1283 // update animation info
1284 std::map<u32, TileSpec>::iterator anim_iter =
1285 m_animation_tiles.find(i->first);
1286 if(anim_iter != m_animation_tiles.end()){
1287 TileSpec &tile = anim_iter->second;
1288 tile.texture = new_texture;
1289 tile.texture_id = new_texture_id;
1290 // force animation update
1291 m_animation_frames[i->first] = -1;
1295 m_last_crack = crack;
1298 // Texture animation
1299 for(std::map<u32, TileSpec>::iterator
1300 i = m_animation_tiles.begin();
1301 i != m_animation_tiles.end(); i++)
1303 const TileSpec &tile = i->second;
1304 // Figure out current frame
1305 int frameoffset = m_animation_frame_offsets[i->first];
1306 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1307 + frameoffset) % tile.animation_frame_count;
1308 // If frame doesn't change, skip
1309 if(frame == m_animation_frames[i->first])
1312 m_animation_frames[i->first] = frame;
1314 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1315 ITextureSource *tsrc = m_gamedef->getTextureSource();
1317 FrameSpec animation_frame = tile.frames.find(frame)->second;
1318 buf->getMaterial().setTexture(0, animation_frame.texture);
1319 if (m_enable_shaders) {
1320 if (animation_frame.normal_texture) {
1321 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1322 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1324 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1329 // Day-night transition
1330 if(daynight_ratio != m_last_daynight_ratio)
1332 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1333 i = m_daynight_diffs.begin();
1334 i != m_daynight_diffs.end(); i++)
1336 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1337 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1338 for(std::map<u32, std::pair<u8, u8 > >::iterator
1339 j = i->second.begin();
1340 j != i->second.end(); j++)
1342 u32 vertexIndex = j->first;
1343 u8 day = j->second.first;
1344 u8 night = j->second.second;
1345 finalColorBlend(vertices[vertexIndex].Color,
1346 day, night, daynight_ratio);
1349 m_last_daynight_ratio = daynight_ratio;
1352 // Node highlighting
1353 if (m_enable_highlighting) {
1354 u8 day = m_highlight_mesh_color.getRed();
1355 u8 night = m_highlight_mesh_color.getGreen();
1357 finalColorBlend(hc, day, night, daynight_ratio);
1358 float sin_r = 0.07 * sin(1.5 * time);
1359 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1360 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1361 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1362 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1363 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1365 for(std::list<u32>::iterator
1366 i = m_highlighted_materials.begin();
1367 i != m_highlighted_materials.end(); i++)
1369 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1370 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1371 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1372 vertices[j].Color = hc;
1379 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1381 if (camera_offset != m_camera_offset) {
1382 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1383 m_camera_offset = camera_offset;
1391 void MeshCollector::append(const TileSpec &tile,
1392 const video::S3DVertex *vertices, u32 numVertices,
1393 const u16 *indices, u32 numIndices)
1395 if(numIndices > 65535)
1397 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1401 PreMeshBuffer *p = NULL;
1402 for(u32 i=0; i<prebuffers.size(); i++)
1404 PreMeshBuffer &pp = prebuffers[i];
1407 if(pp.indices.size() + numIndices > 65535)
1418 prebuffers.push_back(pp);
1419 p = &prebuffers[prebuffers.size()-1];
1422 u32 vertex_count = p->vertices.size();
1423 for(u32 i=0; i<numIndices; i++)
1425 u32 j = indices[i] + vertex_count;
1426 p->indices.push_back(j);
1428 for(u32 i=0; i<numVertices; i++)
1430 p->vertices.push_back(vertices[i]);
1435 MeshCollector - for meshnodes and converted drawtypes.
1438 void MeshCollector::append(const TileSpec &tile,
1439 const video::S3DVertex *vertices, u32 numVertices,
1440 const u16 *indices, u32 numIndices,
1441 v3f pos, video::SColor c)
1443 if(numIndices > 65535)
1445 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1449 PreMeshBuffer *p = NULL;
1450 for(u32 i=0; i<prebuffers.size(); i++)
1452 PreMeshBuffer &pp = prebuffers[i];
1455 if(pp.indices.size() + numIndices > 65535)
1466 prebuffers.push_back(pp);
1467 p = &prebuffers[prebuffers.size()-1];
1470 u32 vertex_count = p->vertices.size();
1471 for(u32 i=0; i<numIndices; i++)
1473 u32 j = indices[i] + vertex_count;
1474 p->indices.push_back(j);
1476 for(u32 i=0; i<numVertices; i++)
1478 video::S3DVertex vert = vertices[i];
1481 p->vertices.push_back(vert);