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),
54 void MeshMakeData::fill(MapBlock *block)
56 m_blockpos = block->getPos();
58 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
64 // Allocate this block + neighbors
66 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
67 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
70 //TimeTaker timer("copy central block data");
74 block->copyTo(m_vmanip);
77 //TimeTaker timer("copy neighbor block data");
81 Copy neighbors. This is lightning fast.
82 Copying only the borders would be *very* slow.
86 Map *map = block->getParent();
88 for(u16 i=0; i<26; i++)
90 const v3s16 &dir = g_26dirs[i];
91 v3s16 bp = m_blockpos + dir;
92 MapBlock *b = map->getBlockNoCreateNoEx(bp);
99 void MeshMakeData::fillSingleNode(MapNode *node)
101 m_blockpos = v3s16(0,0,0);
103 v3s16 blockpos_nodes = v3s16(0,0,0);
104 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
105 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
106 s32 volume = area.getVolume();
107 s32 our_node_index = area.index(1,1,1);
109 // Allocate this block + neighbors
111 m_vmanip.addArea(area);
114 MapNode *data = new MapNode[volume];
115 for(s32 i = 0; i < volume; i++)
117 if(i == our_node_index)
123 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
126 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
130 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
133 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
136 void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
138 m_show_hud = show_hud;
139 m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
142 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
144 m_smooth_lighting = smooth_lighting;
148 Light and vertex color functions
152 Calculate non-smooth lighting at interior of node.
155 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
156 INodeDefManager *ndef)
158 u8 light = n.getLight(bank, ndef);
162 light = undiminish_light(light);
167 light = diminish_light(light);
171 return decode_light(light);
175 Calculate non-smooth lighting at interior of node.
178 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
180 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
181 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
182 return day | (night << 8);
186 Calculate non-smooth lighting at face of node.
189 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
190 v3s16 face_dir, INodeDefManager *ndef)
193 u8 l1 = n.getLight(bank, ndef);
194 u8 l2 = n2.getLight(bank, ndef);
200 // Boost light level for light sources
201 u8 light_source = MYMAX(ndef->get(n).light_source,
202 ndef->get(n2).light_source);
203 if(light_source > light)
204 light = light_source;
206 return decode_light(light);
210 Calculate non-smooth lighting at face of node.
213 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
215 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
216 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
217 return day | (night << 8);
221 Calculate smooth lighting at the XYZ- corner of p.
224 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
226 static v3s16 dirs8[8] = {
237 INodeDefManager *ndef = data->m_gamedef->ndef();
239 u16 ambient_occlusion = 0;
242 u8 light_source_max = 0;
243 for(u32 i=0; i<8; i++)
245 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
247 // if it's CONTENT_IGNORE we can't do any light calculations
248 if (n.getContent() == CONTENT_IGNORE) {
252 const ContentFeatures &f = ndef->get(n);
253 if(f.light_source > light_source_max)
254 light_source_max = f.light_source;
255 // Check f.solidness because fast-style leaves look
257 if(f.param_type == CPT_LIGHT && f.solidness != 2)
259 light += decode_light(n.getLight(bank, ndef));
270 light /= light_count;
272 // Boost brightness around light sources
273 if(decode_light(light_source_max) >= light)
274 //return decode_light(undiminish_light(light_source_max));
275 return decode_light(light_source_max);
277 if(ambient_occlusion > 4)
279 //calculate table index for gamma space multiplier
280 ambient_occlusion -= 5;
281 //table of precalculated gamma space multiply factors
282 //light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2)
283 const float light_amount[4] = {0.877424315, 0.729740053, 0.532520545, 0.0};
284 light = core::clamp(core::round32(light*light_amount[ambient_occlusion]), 0, 255);
291 Calculate smooth lighting at the XYZ- corner of p.
294 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
296 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
297 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
298 return day | (night << 8);
302 Calculate smooth lighting at the given corner of p.
305 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
307 if(corner.X == 1) p.X += 1;
308 else assert(corner.X == -1);
309 if(corner.Y == 1) p.Y += 1;
310 else assert(corner.Y == -1);
311 if(corner.Z == 1) p.Z += 1;
312 else assert(corner.Z == -1);
314 return getSmoothLight(p, data);
318 Converts from day + night color values (0..255)
319 and a given daynight_ratio to the final SColor shown on screen.
321 static void finalColorBlend(video::SColor& result,
322 u8 day, u8 night, u32 daynight_ratio)
324 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
328 b += (day - night) / 13;
329 rg -= (day - night) / 23;
331 // Emphase blue a bit in darker places
332 // Each entry of this array represents a range of 8 blue levels
333 static u8 emphase_blue_when_dark[32] = {
334 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
335 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
337 b += emphase_blue_when_dark[b / 8];
338 b = irr::core::clamp (b, 0, 255);
340 // Artificial light is yellow-ish
341 static u8 emphase_yellow_when_artificial[16] = {
342 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
344 rg += emphase_yellow_when_artificial[night/16];
345 rg = irr::core::clamp (rg, 0, 255);
353 Mesh generation helpers
357 vertex_dirs: v3s16[4]
359 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
362 If looked from outside the node towards the face, the corners are:
368 if(dir == v3s16(0,0,1))
370 // If looking towards z+, this is the face that is behind
371 // the center point, facing towards z+.
372 vertex_dirs[0] = v3s16(-1,-1, 1);
373 vertex_dirs[1] = v3s16( 1,-1, 1);
374 vertex_dirs[2] = v3s16( 1, 1, 1);
375 vertex_dirs[3] = v3s16(-1, 1, 1);
377 else if(dir == v3s16(0,0,-1))
380 vertex_dirs[0] = v3s16( 1,-1,-1);
381 vertex_dirs[1] = v3s16(-1,-1,-1);
382 vertex_dirs[2] = v3s16(-1, 1,-1);
383 vertex_dirs[3] = v3s16( 1, 1,-1);
385 else if(dir == v3s16(1,0,0))
388 vertex_dirs[0] = v3s16( 1,-1, 1);
389 vertex_dirs[1] = v3s16( 1,-1,-1);
390 vertex_dirs[2] = v3s16( 1, 1,-1);
391 vertex_dirs[3] = v3s16( 1, 1, 1);
393 else if(dir == v3s16(-1,0,0))
396 vertex_dirs[0] = v3s16(-1,-1,-1);
397 vertex_dirs[1] = v3s16(-1,-1, 1);
398 vertex_dirs[2] = v3s16(-1, 1, 1);
399 vertex_dirs[3] = v3s16(-1, 1,-1);
401 else if(dir == v3s16(0,1,0))
403 // faces towards Y+ (assume Z- as "down" in texture)
404 vertex_dirs[0] = v3s16( 1, 1,-1);
405 vertex_dirs[1] = v3s16(-1, 1,-1);
406 vertex_dirs[2] = v3s16(-1, 1, 1);
407 vertex_dirs[3] = v3s16( 1, 1, 1);
409 else if(dir == v3s16(0,-1,0))
411 // faces towards Y- (assume Z+ as "down" in texture)
412 vertex_dirs[0] = v3s16( 1,-1, 1);
413 vertex_dirs[1] = v3s16(-1,-1, 1);
414 vertex_dirs[2] = v3s16(-1,-1,-1);
415 vertex_dirs[3] = v3s16( 1,-1,-1);
422 video::S3DVertex vertices[4]; // Precalculated vertices
425 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
426 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
430 // Position is at the center of the cube.
439 v3s16 vertex_dirs[4];
440 getNodeVertexDirs(dir, vertex_dirs);
444 switch (tile.rotation)
450 vertex_dirs[0] = vertex_dirs[3];
451 vertex_dirs[3] = vertex_dirs[2];
452 vertex_dirs[2] = vertex_dirs[1];
462 vertex_dirs[0] = vertex_dirs[2];
465 vertex_dirs[1] = vertex_dirs[3];
476 vertex_dirs[0] = vertex_dirs[1];
477 vertex_dirs[1] = vertex_dirs[2];
478 vertex_dirs[2] = vertex_dirs[3];
488 vertex_dirs[0] = vertex_dirs[3];
489 vertex_dirs[3] = vertex_dirs[2];
490 vertex_dirs[2] = vertex_dirs[1];
502 vertex_dirs[0] = vertex_dirs[1];
503 vertex_dirs[1] = vertex_dirs[2];
504 vertex_dirs[2] = vertex_dirs[3];
516 vertex_dirs[0] = vertex_dirs[3];
517 vertex_dirs[3] = vertex_dirs[2];
518 vertex_dirs[2] = vertex_dirs[1];
530 vertex_dirs[0] = vertex_dirs[1];
531 vertex_dirs[1] = vertex_dirs[2];
532 vertex_dirs[2] = vertex_dirs[3];
554 for(u16 i=0; i<4; i++)
557 BS/2*vertex_dirs[i].X,
558 BS/2*vertex_dirs[i].Y,
559 BS/2*vertex_dirs[i].Z
563 for(u16 i=0; i<4; i++)
565 vertex_pos[i].X *= scale.X;
566 vertex_pos[i].Y *= scale.Y;
567 vertex_pos[i].Z *= scale.Z;
568 vertex_pos[i] += pos;
572 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
573 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
574 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
576 v3f normal(dir.X, dir.Y, dir.Z);
578 u8 alpha = tile.alpha;
580 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
581 MapBlock_LightColor(alpha, li0, light_source),
582 core::vector2d<f32>(x0+w*abs_scale, y0+h));
583 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
584 MapBlock_LightColor(alpha, li1, light_source),
585 core::vector2d<f32>(x0, y0+h));
586 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
587 MapBlock_LightColor(alpha, li2, light_source),
588 core::vector2d<f32>(x0, y0));
589 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
590 MapBlock_LightColor(alpha, li3, light_source),
591 core::vector2d<f32>(x0+w*abs_scale, y0));
594 dest.push_back(face);
598 Nodes make a face if contents differ and solidness differs.
601 1: Face uses m1's content
602 2: Face uses m2's content
603 equivalent: Whether the blocks share the same face (eg. water and glass)
605 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
607 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
608 INodeDefManager *ndef)
612 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
615 bool contents_differ = (m1 != m2);
617 const ContentFeatures &f1 = ndef->get(m1);
618 const ContentFeatures &f2 = ndef->get(m2);
620 // Contents don't differ for different forms of same liquid
621 if(f1.sameLiquid(f2))
622 contents_differ = false;
624 u8 c1 = f1.solidness;
625 u8 c2 = f2.solidness;
627 bool solidness_differs = (c1 != c2);
628 bool makes_face = contents_differ && solidness_differs;
630 if(makes_face == false)
634 c1 = f1.visual_solidness;
636 c2 = f2.visual_solidness;
640 // If same solidness, liquid takes precense
654 Gets nth node tile (0 <= n <= 5).
656 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
658 INodeDefManager *ndef = data->m_gamedef->ndef();
659 TileSpec spec = ndef->get(mn).tiles[tileindex];
660 // Apply temporary crack
661 if (p == data->m_crack_pos_relative)
662 spec.material_flags |= MATERIAL_FLAG_CRACK;
667 Gets node tile given a face direction.
669 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
671 INodeDefManager *ndef = data->m_gamedef->ndef();
673 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
674 // (0,0,1), (0,0,-1) or (0,0,0)
675 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
677 // Convert direction to single integer for table lookup
682 // 4 = invalid, treat as (0,0,0)
686 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
688 // Get rotation for things like chests
689 u8 facedir = mn.getFaceDir(ndef);
692 static const u16 dir_to_tile[24 * 16] =
694 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
695 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
696 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
697 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
698 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
700 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
701 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
702 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
703 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
705 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
706 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
707 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
708 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
710 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
711 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
712 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
713 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
715 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
716 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
717 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
718 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
720 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
721 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
722 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
723 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
726 u16 tile_index=facedir*16 + dir_i;
727 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
728 spec.rotation=dir_to_tile[tile_index + 1];
729 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
733 static void getTileInfo(
741 v3s16 &face_dir_corrected,
747 VoxelManipulator &vmanip = data->m_vmanip;
748 INodeDefManager *ndef = data->m_gamedef->ndef();
749 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
751 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
753 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
754 if (n0.getContent() == CONTENT_IGNORE ) {
758 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
761 bool equivalent = false;
762 u8 mf = face_contents(n0.getContent(), n1.getContent(),
775 tile = getNodeTile(n0, p, face_dir, data);
777 face_dir_corrected = face_dir;
778 light_source = ndef->get(n0).light_source;
782 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
783 p_corrected = p + face_dir;
784 face_dir_corrected = -face_dir;
785 light_source = ndef->get(n1).light_source;
788 // eg. water and glass
790 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
792 if(data->m_smooth_lighting == false)
794 lights[0] = lights[1] = lights[2] = lights[3] =
795 getFaceLight(n0, n1, face_dir, ndef);
799 v3s16 vertex_dirs[4];
800 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
801 for(u16 i=0; i<4; i++)
803 lights[i] = getSmoothLight(
804 blockpos_nodes + p_corrected,
805 vertex_dirs[i], data);
814 translate_dir: unit vector with only one of x, y or z
815 face_dir: unit vector with only one of x, y or z
817 static void updateFastFaceRow(
824 std::vector<FastFace> &dest)
828 u16 continuous_tiles_count = 0;
830 bool makes_face = false;
832 v3s16 face_dir_corrected;
833 u16 lights[4] = {0,0,0,0};
836 getTileInfo(data, p, face_dir,
837 makes_face, p_corrected, face_dir_corrected,
838 lights, tile, light_source);
840 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
842 // If tiling can be done, this is set to false in the next step
843 bool next_is_different = true;
847 bool next_makes_face = false;
848 v3s16 next_p_corrected;
849 v3s16 next_face_dir_corrected;
850 u16 next_lights[4] = {0,0,0,0};
852 u8 next_light_source = 0;
854 // If at last position, there is nothing to compare to and
855 // the face must be drawn anyway
856 if(j != MAP_BLOCKSIZE - 1)
858 p_next = p + translate_dir;
860 getTileInfo(data, p_next, face_dir,
861 next_makes_face, next_p_corrected,
862 next_face_dir_corrected, next_lights,
863 next_tile, next_light_source);
865 if(next_makes_face == makes_face
866 && next_p_corrected == p_corrected + translate_dir
867 && next_face_dir_corrected == face_dir_corrected
868 && next_lights[0] == lights[0]
869 && next_lights[1] == lights[1]
870 && next_lights[2] == lights[2]
871 && next_lights[3] == lights[3]
873 && tile.rotation == 0
874 && next_light_source == light_source)
876 next_is_different = false;
880 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
881 next_makes_face != makes_face ? 1 : 0);
882 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
883 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
884 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
885 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
886 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
887 (next_lights[0] != lights[0] ||
888 next_lights[0] != lights[0] ||
889 next_lights[0] != lights[0] ||
890 next_lights[0] != lights[0]) ? 1 : 0);
891 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
892 !(next_tile == tile) ? 1 : 0);
895 /*g_profiler->add("Meshgen: Total faces checked", 1);
897 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
900 g_profiler->add("Meshgen: diff: last position", 1);*/
903 continuous_tiles_count++;
905 if(next_is_different)
908 Create a face if there should be one
912 // Floating point conversion of the position vector
913 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
914 // Center point of face (kind of)
915 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
916 if(continuous_tiles_count != 1)
917 sp += translate_dir_f;
920 if(translate_dir.X != 0) {
921 scale.X = continuous_tiles_count;
923 if(translate_dir.Y != 0) {
924 scale.Y = continuous_tiles_count;
926 if(translate_dir.Z != 0) {
927 scale.Z = continuous_tiles_count;
930 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
931 sp, face_dir_corrected, scale, light_source,
934 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
935 for(int i = 1; i < continuous_tiles_count; i++){
936 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
940 continuous_tiles_count = 0;
942 makes_face = next_makes_face;
943 p_corrected = next_p_corrected;
944 face_dir_corrected = next_face_dir_corrected;
945 lights[0] = next_lights[0];
946 lights[1] = next_lights[1];
947 lights[2] = next_lights[2];
948 lights[3] = next_lights[3];
950 light_source = next_light_source;
957 static void updateAllFastFaceRows(MeshMakeData *data,
958 std::vector<FastFace> &dest)
961 Go through every y,z and get top(y+) faces in rows of x+
963 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
964 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
965 updateFastFaceRow(data,
969 v3s16(0,1,0), //face dir
976 Go through every x,y and get right(x+) faces in rows of z+
978 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
979 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
980 updateFastFaceRow(data,
984 v3s16(1,0,0), //face dir
991 Go through every y,z and get back(z+) faces in rows of x+
993 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
994 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
995 updateFastFaceRow(data,
999 v3s16(0,0,1), //face dir
1010 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1011 m_mesh(new scene::SMesh()),
1012 m_gamedef(data->m_gamedef),
1013 m_animation_force_timer(0), // force initial animation
1015 m_crack_materials(),
1016 m_highlighted_materials(),
1017 m_last_daynight_ratio((u32) -1),
1020 m_enable_shaders = g_settings->getBool("enable_shaders");
1021 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1023 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1024 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1025 //TimeTaker timer1("MapBlockMesh()");
1027 std::vector<FastFace> fastfaces_new;
1030 We are including the faces of the trailing edges of the block.
1031 This means that when something changes, the caller must
1032 also update the meshes of the blocks at the leading edges.
1034 NOTE: This is the slowest part of this method.
1037 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1038 //TimeTaker timer2("updateAllFastFaceRows()");
1039 updateAllFastFaceRows(data, fastfaces_new);
1044 Convert FastFaces to MeshCollector
1047 MeshCollector collector;
1050 // avg 0ms (100ms spikes when loading textures the first time)
1051 // (NOTE: probably outdated)
1052 //TimeTaker timer2("MeshCollector building");
1054 for(u32 i=0; i<fastfaces_new.size(); i++)
1056 FastFace &f = fastfaces_new[i];
1058 const u16 indices[] = {0,1,2,2,3,0};
1059 const u16 indices_alternate[] = {0,1,3,2,3,1};
1061 if(f.tile.texture == NULL)
1064 const u16 *indices_p = indices;
1067 Revert triangles for nicer looking gradient if vertices
1068 1 and 3 have same color or 0 and 2 have different color.
1069 getRed() is the day color.
1071 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1072 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1073 indices_p = indices_alternate;
1075 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1080 Add special graphics:
1087 mapblock_mesh_generate_special(data, collector);
1089 m_highlight_mesh_color = data->m_highlight_mesh_color;
1092 Convert MeshCollector to SMesh
1094 ITextureSource *tsrc = m_gamedef->tsrc();
1095 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1097 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1099 PreMeshBuffer &p = collector.prebuffers[i];
1101 // Generate animation data
1103 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1105 // Find the texture name plus ^[crack:N:
1106 std::ostringstream os(std::ios::binary);
1107 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1108 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1109 os<<"o"; // use ^[cracko
1110 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1111 m_crack_materials.insert(std::make_pair(i, os.str()));
1112 // Replace tile texture with the cracked one
1113 p.tile.texture = tsrc->getTexture(
1115 &p.tile.texture_id);
1117 // - Texture animation
1118 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1120 // Add to MapBlockMesh in order to animate these tiles
1121 m_animation_tiles[i] = p.tile;
1122 m_animation_frames[i] = 0;
1123 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1124 // Get starting position from noise
1125 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1126 data->m_blockpos.X, data->m_blockpos.Y,
1127 data->m_blockpos.Z, 0));
1129 // Play all synchronized
1130 m_animation_frame_offsets[i] = 0;
1132 // Replace tile texture with the first animation frame
1133 FrameSpec animation_frame = p.tile.frames.find(0)->second;
1134 p.tile.texture = animation_frame.texture;
1137 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1138 m_highlighted_materials.push_back(i);
1140 for(u32 j = 0; j < p.vertices.size(); j++)
1142 // Note applyFacesShading second parameter is precalculated sqrt
1143 // value for speed improvement
1144 // Skip it for lightsources and top faces.
1145 video::SColor &vc = p.vertices[j].Color;
1146 if (!vc.getBlue()) {
1147 if (p.vertices[j].Normal.Y < -0.5) {
1148 applyFacesShading (vc, 0.447213);
1149 } else if (p.vertices[j].Normal.X > 0.5) {
1150 applyFacesShading (vc, 0.670820);
1151 } else if (p.vertices[j].Normal.X < -0.5) {
1152 applyFacesShading (vc, 0.670820);
1153 } else if (p.vertices[j].Normal.Z > 0.5) {
1154 applyFacesShading (vc, 0.836660);
1155 } else if (p.vertices[j].Normal.Z < -0.5) {
1156 applyFacesShading (vc, 0.836660);
1159 // - Classic lighting
1160 // Set initial real color and store for later updates
1161 u8 day = vc.getRed();
1162 u8 night = vc.getGreen();
1163 finalColorBlend(vc, day, night, 1000);
1164 m_daynight_diffs[i][j] = std::make_pair(day, night);
1168 video::SMaterial material;
1169 material.setFlag(video::EMF_LIGHTING, false);
1170 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1171 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1172 material.setFlag(video::EMF_FOG_ENABLE, true);
1173 material.setTexture(0, p.tile.texture);
1175 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1176 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1178 if (m_enable_shaders) {
1179 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1180 p.tile.applyMaterialOptionsWithShaders(material);
1181 if (p.tile.normal_texture) {
1182 material.setTexture(1, p.tile.normal_texture);
1183 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1185 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1188 p.tile.applyMaterialOptions(material);
1192 // Create meshbuffer
1193 // This is a "Standard MeshBuffer",
1194 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1195 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1197 buf->Material = material;
1199 m_mesh->addMeshBuffer(buf);
1202 buf->append(&p.vertices[0], p.vertices.size(),
1203 &p.indices[0], p.indices.size());
1206 m_camera_offset = camera_offset;
1209 Do some stuff to the mesh
1212 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1217 // Usually 1-700 faces and 1-7 materials
1218 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1219 <<"and uses "<<m_mesh->getMeshBufferCount()
1220 <<" materials (meshbuffers)"<<std::endl;
1223 // Use VBO for mesh (this just would set this for ever buffer)
1224 // This will lead to infinite memory usage because or irrlicht.
1225 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1228 NOTE: If that is enabled, some kind of a queue to the main
1229 thread should be made which would call irrlicht to delete
1230 the hardware buffer and then delete the mesh
1234 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1236 // Check if animation is required for this mesh
1238 !m_crack_materials.empty() ||
1239 !m_daynight_diffs.empty() ||
1240 !m_animation_tiles.empty() ||
1241 !m_highlighted_materials.empty();
1244 MapBlockMesh::~MapBlockMesh()
1250 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1253 if(!m_has_animation)
1255 m_animation_force_timer = 100000;
1259 m_animation_force_timer = myrand_range(5, 100);
1262 if(crack != m_last_crack)
1264 for(std::map<u32, std::string>::iterator
1265 i = m_crack_materials.begin();
1266 i != m_crack_materials.end(); i++)
1268 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1269 std::string basename = i->second;
1271 // Create new texture name from original
1272 ITextureSource *tsrc = m_gamedef->getTextureSource();
1273 std::ostringstream os;
1274 os<<basename<<crack;
1275 u32 new_texture_id = 0;
1276 video::ITexture *new_texture =
1277 tsrc->getTexture(os.str(), &new_texture_id);
1278 buf->getMaterial().setTexture(0, new_texture);
1280 // If the current material is also animated,
1281 // update animation info
1282 std::map<u32, TileSpec>::iterator anim_iter =
1283 m_animation_tiles.find(i->first);
1284 if(anim_iter != m_animation_tiles.end()){
1285 TileSpec &tile = anim_iter->second;
1286 tile.texture = new_texture;
1287 tile.texture_id = new_texture_id;
1288 // force animation update
1289 m_animation_frames[i->first] = -1;
1293 m_last_crack = crack;
1296 // Texture animation
1297 for(std::map<u32, TileSpec>::iterator
1298 i = m_animation_tiles.begin();
1299 i != m_animation_tiles.end(); i++)
1301 const TileSpec &tile = i->second;
1302 // Figure out current frame
1303 int frameoffset = m_animation_frame_offsets[i->first];
1304 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1305 + frameoffset) % tile.animation_frame_count;
1306 // If frame doesn't change, skip
1307 if(frame == m_animation_frames[i->first])
1310 m_animation_frames[i->first] = frame;
1312 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1313 ITextureSource *tsrc = m_gamedef->getTextureSource();
1315 FrameSpec animation_frame = tile.frames.find(frame)->second;
1316 buf->getMaterial().setTexture(0, animation_frame.texture);
1317 if (m_enable_shaders) {
1318 if (animation_frame.normal_texture) {
1319 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1320 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1322 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1327 // Day-night transition
1328 if(daynight_ratio != m_last_daynight_ratio)
1330 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1331 i = m_daynight_diffs.begin();
1332 i != m_daynight_diffs.end(); i++)
1334 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1335 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1336 for(std::map<u32, std::pair<u8, u8 > >::iterator
1337 j = i->second.begin();
1338 j != i->second.end(); j++)
1340 u32 vertexIndex = j->first;
1341 u8 day = j->second.first;
1342 u8 night = j->second.second;
1343 finalColorBlend(vertices[vertexIndex].Color,
1344 day, night, daynight_ratio);
1347 m_last_daynight_ratio = daynight_ratio;
1350 // Node highlighting
1351 if (m_enable_highlighting) {
1352 u8 day = m_highlight_mesh_color.getRed();
1353 u8 night = m_highlight_mesh_color.getGreen();
1355 finalColorBlend(hc, day, night, daynight_ratio);
1356 float sin_r = 0.07 * sin(1.5 * time);
1357 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1358 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1359 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1360 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1361 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1363 for(std::list<u32>::iterator
1364 i = m_highlighted_materials.begin();
1365 i != m_highlighted_materials.end(); i++)
1367 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1368 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1369 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1370 vertices[j].Color = hc;
1377 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1379 if (camera_offset != m_camera_offset) {
1380 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1381 m_camera_offset = camera_offset;
1389 void MeshCollector::append(const TileSpec &tile,
1390 const video::S3DVertex *vertices, u32 numVertices,
1391 const u16 *indices, u32 numIndices)
1393 if(numIndices > 65535)
1395 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1399 PreMeshBuffer *p = NULL;
1400 for(u32 i=0; i<prebuffers.size(); i++)
1402 PreMeshBuffer &pp = prebuffers[i];
1405 if(pp.indices.size() + numIndices > 65535)
1416 prebuffers.push_back(pp);
1417 p = &prebuffers[prebuffers.size()-1];
1420 u32 vertex_count = p->vertices.size();
1421 for(u32 i=0; i<numIndices; i++)
1423 u32 j = indices[i] + vertex_count;
1424 p->indices.push_back(j);
1426 for(u32 i=0; i<numVertices; i++)
1428 p->vertices.push_back(vertices[i]);