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_smooth_lighting(false),
53 void MeshMakeData::fill(MapBlock *block)
55 m_blockpos = block->getPos();
57 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
63 // Allocate this block + neighbors
65 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
66 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
69 //TimeTaker timer("copy central block data");
73 block->copyTo(m_vmanip);
76 //TimeTaker timer("copy neighbor block data");
80 Copy neighbors. This is lightning fast.
81 Copying only the borders would be *very* slow.
85 Map *map = block->getParent();
87 for(u16 i=0; i<26; i++)
89 const v3s16 &dir = g_26dirs[i];
90 v3s16 bp = m_blockpos + dir;
91 MapBlock *b = map->getBlockNoCreateNoEx(bp);
98 void MeshMakeData::fillSingleNode(MapNode *node)
100 m_blockpos = v3s16(0,0,0);
102 v3s16 blockpos_nodes = v3s16(0,0,0);
103 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
104 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
105 s32 volume = area.getVolume();
106 s32 our_node_index = area.index(1,1,1);
108 // Allocate this block + neighbors
110 m_vmanip.addArea(area);
113 MapNode *data = new MapNode[volume];
114 for(s32 i = 0; i < volume; i++)
116 if(i == our_node_index)
122 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
125 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
129 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
132 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
135 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
137 m_smooth_lighting = smooth_lighting;
141 Light and vertex color functions
145 Calculate non-smooth lighting at interior of node.
148 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
149 INodeDefManager *ndef)
151 u8 light = n.getLight(bank, ndef);
155 light = undiminish_light(light);
160 light = diminish_light(light);
164 return decode_light(light);
168 Calculate non-smooth lighting at interior of node.
171 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
173 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
174 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
175 return day | (night << 8);
179 Calculate non-smooth lighting at face of node.
182 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
183 v3s16 face_dir, INodeDefManager *ndef)
186 u8 l1 = n.getLight(bank, ndef);
187 u8 l2 = n2.getLight(bank, ndef);
193 // Boost light level for light sources
194 u8 light_source = MYMAX(ndef->get(n).light_source,
195 ndef->get(n2).light_source);
196 if(light_source > light)
197 light = light_source;
199 return decode_light(light);
203 Calculate non-smooth lighting at face of node.
206 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
208 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
209 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
210 return day | (night << 8);
214 Calculate smooth lighting at the XYZ- corner of p.
217 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
219 static v3s16 dirs8[8] = {
230 INodeDefManager *ndef = data->m_gamedef->ndef();
232 u16 ambient_occlusion = 0;
235 u8 light_source_max = 0;
236 for(u32 i=0; i<8; i++)
238 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
240 // if it's CONTENT_IGNORE we can't do any light calculations
241 if (n.getContent() == CONTENT_IGNORE) {
245 const ContentFeatures &f = ndef->get(n);
246 if(f.light_source > light_source_max)
247 light_source_max = f.light_source;
248 // Check f.solidness because fast-style leaves look
250 if(f.param_type == CPT_LIGHT && f.solidness != 2)
252 light += decode_light(n.getLight(bank, ndef));
263 light /= light_count;
265 // Boost brightness around light sources
266 if(decode_light(light_source_max) >= light)
267 //return decode_light(undiminish_light(light_source_max));
268 return decode_light(light_source_max);
270 if(ambient_occlusion > 4)
272 //calculate table index for gamma space multiplier
273 ambient_occlusion -= 5;
274 //table of precalculated gamma space multiply factors
275 //light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2)
276 const float light_amount[4] = {0.877424315, 0.729740053, 0.532520545, 0.0};
277 light = core::clamp(core::round32(light*light_amount[ambient_occlusion]), 0, 255);
284 Calculate smooth lighting at the XYZ- corner of p.
287 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
289 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
290 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
291 return day | (night << 8);
295 Calculate smooth lighting at the given corner of p.
298 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
300 if(corner.X == 1) p.X += 1;
301 else assert(corner.X == -1);
302 if(corner.Y == 1) p.Y += 1;
303 else assert(corner.Y == -1);
304 if(corner.Z == 1) p.Z += 1;
305 else assert(corner.Z == -1);
307 return getSmoothLight(p, data);
311 Converts from day + night color values (0..255)
312 and a given daynight_ratio to the final SColor shown on screen.
314 static void finalColorBlend(video::SColor& result,
315 u8 day, u8 night, u32 daynight_ratio)
317 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
321 b += (day - night) / 13;
322 rg -= (day - night) / 23;
324 // Emphase blue a bit in darker places
325 // Each entry of this array represents a range of 8 blue levels
326 static u8 emphase_blue_when_dark[32] = {
327 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
328 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
330 b += emphase_blue_when_dark[b / 8];
331 b = irr::core::clamp (b, 0, 255);
333 // Artificial light is yellow-ish
334 static u8 emphase_yellow_when_artificial[16] = {
335 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
337 rg += emphase_yellow_when_artificial[night/16];
338 rg = irr::core::clamp (rg, 0, 255);
346 Mesh generation helpers
350 vertex_dirs: v3s16[4]
352 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
355 If looked from outside the node towards the face, the corners are:
361 if(dir == v3s16(0,0,1))
363 // If looking towards z+, this is the face that is behind
364 // the center point, facing towards z+.
365 vertex_dirs[0] = v3s16(-1,-1, 1);
366 vertex_dirs[1] = v3s16( 1,-1, 1);
367 vertex_dirs[2] = v3s16( 1, 1, 1);
368 vertex_dirs[3] = v3s16(-1, 1, 1);
370 else if(dir == v3s16(0,0,-1))
373 vertex_dirs[0] = v3s16( 1,-1,-1);
374 vertex_dirs[1] = v3s16(-1,-1,-1);
375 vertex_dirs[2] = v3s16(-1, 1,-1);
376 vertex_dirs[3] = v3s16( 1, 1,-1);
378 else if(dir == v3s16(1,0,0))
381 vertex_dirs[0] = v3s16( 1,-1, 1);
382 vertex_dirs[1] = v3s16( 1,-1,-1);
383 vertex_dirs[2] = v3s16( 1, 1,-1);
384 vertex_dirs[3] = v3s16( 1, 1, 1);
386 else if(dir == v3s16(-1,0,0))
389 vertex_dirs[0] = v3s16(-1,-1,-1);
390 vertex_dirs[1] = v3s16(-1,-1, 1);
391 vertex_dirs[2] = v3s16(-1, 1, 1);
392 vertex_dirs[3] = v3s16(-1, 1,-1);
394 else if(dir == v3s16(0,1,0))
396 // faces towards Y+ (assume Z- as "down" in texture)
397 vertex_dirs[0] = v3s16( 1, 1,-1);
398 vertex_dirs[1] = v3s16(-1, 1,-1);
399 vertex_dirs[2] = v3s16(-1, 1, 1);
400 vertex_dirs[3] = v3s16( 1, 1, 1);
402 else if(dir == v3s16(0,-1,0))
404 // faces towards Y- (assume Z+ as "down" in texture)
405 vertex_dirs[0] = v3s16( 1,-1, 1);
406 vertex_dirs[1] = v3s16(-1,-1, 1);
407 vertex_dirs[2] = v3s16(-1,-1,-1);
408 vertex_dirs[3] = v3s16( 1,-1,-1);
415 video::S3DVertex vertices[4]; // Precalculated vertices
418 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
419 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
423 // Position is at the center of the cube.
432 v3s16 vertex_dirs[4];
433 getNodeVertexDirs(dir, vertex_dirs);
437 switch (tile.rotation)
443 vertex_dirs[0] = vertex_dirs[3];
444 vertex_dirs[3] = vertex_dirs[2];
445 vertex_dirs[2] = vertex_dirs[1];
455 vertex_dirs[0] = vertex_dirs[2];
458 vertex_dirs[1] = vertex_dirs[3];
469 vertex_dirs[0] = vertex_dirs[1];
470 vertex_dirs[1] = vertex_dirs[2];
471 vertex_dirs[2] = vertex_dirs[3];
481 vertex_dirs[0] = vertex_dirs[3];
482 vertex_dirs[3] = vertex_dirs[2];
483 vertex_dirs[2] = vertex_dirs[1];
495 vertex_dirs[0] = vertex_dirs[1];
496 vertex_dirs[1] = vertex_dirs[2];
497 vertex_dirs[2] = vertex_dirs[3];
509 vertex_dirs[0] = vertex_dirs[3];
510 vertex_dirs[3] = vertex_dirs[2];
511 vertex_dirs[2] = vertex_dirs[1];
523 vertex_dirs[0] = vertex_dirs[1];
524 vertex_dirs[1] = vertex_dirs[2];
525 vertex_dirs[2] = vertex_dirs[3];
547 for(u16 i=0; i<4; i++)
550 BS/2*vertex_dirs[i].X,
551 BS/2*vertex_dirs[i].Y,
552 BS/2*vertex_dirs[i].Z
556 for(u16 i=0; i<4; i++)
558 vertex_pos[i].X *= scale.X;
559 vertex_pos[i].Y *= scale.Y;
560 vertex_pos[i].Z *= scale.Z;
561 vertex_pos[i] += pos;
565 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
566 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
567 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
569 v3f normal(dir.X, dir.Y, dir.Z);
571 u8 alpha = tile.alpha;
573 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
574 MapBlock_LightColor(alpha, li0, light_source),
575 core::vector2d<f32>(x0+w*abs_scale, y0+h));
576 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
577 MapBlock_LightColor(alpha, li1, light_source),
578 core::vector2d<f32>(x0, y0+h));
579 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
580 MapBlock_LightColor(alpha, li2, light_source),
581 core::vector2d<f32>(x0, y0));
582 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
583 MapBlock_LightColor(alpha, li3, light_source),
584 core::vector2d<f32>(x0+w*abs_scale, y0));
587 dest.push_back(face);
591 Nodes make a face if contents differ and solidness differs.
594 1: Face uses m1's content
595 2: Face uses m2's content
596 equivalent: Whether the blocks share the same face (eg. water and glass)
598 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
600 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
601 INodeDefManager *ndef)
605 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
608 bool contents_differ = (m1 != m2);
610 const ContentFeatures &f1 = ndef->get(m1);
611 const ContentFeatures &f2 = ndef->get(m2);
613 // Contents don't differ for different forms of same liquid
614 if(f1.sameLiquid(f2))
615 contents_differ = false;
617 u8 c1 = f1.solidness;
618 u8 c2 = f2.solidness;
620 bool solidness_differs = (c1 != c2);
621 bool makes_face = contents_differ && solidness_differs;
623 if(makes_face == false)
627 c1 = f1.visual_solidness;
629 c2 = f2.visual_solidness;
633 // If same solidness, liquid takes precense
647 Gets nth node tile (0 <= n <= 5).
649 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
651 INodeDefManager *ndef = data->m_gamedef->ndef();
652 TileSpec spec = ndef->get(mn).tiles[tileindex];
653 // Apply temporary crack
654 if(p == data->m_crack_pos_relative)
656 spec.material_flags |= MATERIAL_FLAG_CRACK;
662 Gets node tile given a face direction.
664 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
666 INodeDefManager *ndef = data->m_gamedef->ndef();
668 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
669 // (0,0,1), (0,0,-1) or (0,0,0)
670 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
672 // Convert direction to single integer for table lookup
677 // 4 = invalid, treat as (0,0,0)
681 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
683 // Get rotation for things like chests
684 u8 facedir = mn.getFaceDir(ndef);
687 static const u16 dir_to_tile[24 * 16] =
689 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
690 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
691 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
692 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
693 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
695 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
696 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
697 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
698 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
700 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
701 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
702 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
703 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
705 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
706 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
707 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
708 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
710 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
711 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
712 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
713 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
715 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
716 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
717 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
718 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
721 u16 tile_index=facedir*16 + dir_i;
722 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
723 spec.rotation=dir_to_tile[tile_index + 1];
724 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
728 static void getTileInfo(
736 v3s16 &face_dir_corrected,
742 VoxelManipulator &vmanip = data->m_vmanip;
743 INodeDefManager *ndef = data->m_gamedef->ndef();
744 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
746 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
748 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
749 if (n0.getContent() == CONTENT_IGNORE ) {
753 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
756 bool equivalent = false;
757 u8 mf = face_contents(n0.getContent(), n1.getContent(),
770 tile = getNodeTile(n0, p, face_dir, data);
772 face_dir_corrected = face_dir;
773 light_source = ndef->get(n0).light_source;
777 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
778 p_corrected = p + face_dir;
779 face_dir_corrected = -face_dir;
780 light_source = ndef->get(n1).light_source;
783 // eg. water and glass
785 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
787 if(data->m_smooth_lighting == false)
789 lights[0] = lights[1] = lights[2] = lights[3] =
790 getFaceLight(n0, n1, face_dir, ndef);
794 v3s16 vertex_dirs[4];
795 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
796 for(u16 i=0; i<4; i++)
798 lights[i] = getSmoothLight(
799 blockpos_nodes + p_corrected,
800 vertex_dirs[i], data);
809 translate_dir: unit vector with only one of x, y or z
810 face_dir: unit vector with only one of x, y or z
812 static void updateFastFaceRow(
819 std::vector<FastFace> &dest)
823 u16 continuous_tiles_count = 0;
825 bool makes_face = false;
827 v3s16 face_dir_corrected;
828 u16 lights[4] = {0,0,0,0};
831 getTileInfo(data, p, face_dir,
832 makes_face, p_corrected, face_dir_corrected,
833 lights, tile, light_source);
835 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
837 // If tiling can be done, this is set to false in the next step
838 bool next_is_different = true;
842 bool next_makes_face = false;
843 v3s16 next_p_corrected;
844 v3s16 next_face_dir_corrected;
845 u16 next_lights[4] = {0,0,0,0};
847 u8 next_light_source = 0;
849 // If at last position, there is nothing to compare to and
850 // the face must be drawn anyway
851 if(j != MAP_BLOCKSIZE - 1)
853 p_next = p + translate_dir;
855 getTileInfo(data, p_next, face_dir,
856 next_makes_face, next_p_corrected,
857 next_face_dir_corrected, next_lights,
858 next_tile, next_light_source);
860 if(next_makes_face == makes_face
861 && next_p_corrected == p_corrected + translate_dir
862 && next_face_dir_corrected == face_dir_corrected
863 && next_lights[0] == lights[0]
864 && next_lights[1] == lights[1]
865 && next_lights[2] == lights[2]
866 && next_lights[3] == lights[3]
868 && tile.rotation == 0
869 && next_light_source == light_source)
871 next_is_different = false;
875 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
876 next_makes_face != makes_face ? 1 : 0);
877 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
878 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
879 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
880 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
881 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
882 (next_lights[0] != lights[0] ||
883 next_lights[0] != lights[0] ||
884 next_lights[0] != lights[0] ||
885 next_lights[0] != lights[0]) ? 1 : 0);
886 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
887 !(next_tile == tile) ? 1 : 0);
890 /*g_profiler->add("Meshgen: Total faces checked", 1);
892 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
895 g_profiler->add("Meshgen: diff: last position", 1);*/
898 continuous_tiles_count++;
900 if(next_is_different)
903 Create a face if there should be one
907 // Floating point conversion of the position vector
908 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
909 // Center point of face (kind of)
910 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
911 if(continuous_tiles_count != 1)
912 sp += translate_dir_f;
915 if(translate_dir.X != 0) {
916 scale.X = continuous_tiles_count;
918 if(translate_dir.Y != 0) {
919 scale.Y = continuous_tiles_count;
921 if(translate_dir.Z != 0) {
922 scale.Z = continuous_tiles_count;
925 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
926 sp, face_dir_corrected, scale, light_source,
929 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
930 for(int i = 1; i < continuous_tiles_count; i++){
931 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
935 continuous_tiles_count = 0;
937 makes_face = next_makes_face;
938 p_corrected = next_p_corrected;
939 face_dir_corrected = next_face_dir_corrected;
940 lights[0] = next_lights[0];
941 lights[1] = next_lights[1];
942 lights[2] = next_lights[2];
943 lights[3] = next_lights[3];
945 light_source = next_light_source;
952 static void updateAllFastFaceRows(MeshMakeData *data,
953 std::vector<FastFace> &dest)
956 Go through every y,z and get top(y+) faces in rows of x+
958 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
959 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
960 updateFastFaceRow(data,
964 v3s16(0,1,0), //face dir
971 Go through every x,y and get right(x+) faces in rows of z+
973 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
974 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
975 updateFastFaceRow(data,
979 v3s16(1,0,0), //face dir
986 Go through every y,z and get back(z+) faces in rows of x+
988 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
989 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
990 updateFastFaceRow(data,
994 v3s16(0,0,1), //face dir
1005 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1006 m_mesh(new scene::SMesh()),
1007 m_gamedef(data->m_gamedef),
1008 m_animation_force_timer(0), // force initial animation
1010 m_crack_materials(),
1011 m_last_daynight_ratio((u32) -1),
1014 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1015 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1016 //TimeTaker timer1("MapBlockMesh()");
1018 std::vector<FastFace> fastfaces_new;
1021 We are including the faces of the trailing edges of the block.
1022 This means that when something changes, the caller must
1023 also update the meshes of the blocks at the leading edges.
1025 NOTE: This is the slowest part of this method.
1028 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1029 //TimeTaker timer2("updateAllFastFaceRows()");
1030 updateAllFastFaceRows(data, fastfaces_new);
1035 Convert FastFaces to MeshCollector
1038 MeshCollector collector;
1041 // avg 0ms (100ms spikes when loading textures the first time)
1042 // (NOTE: probably outdated)
1043 //TimeTaker timer2("MeshCollector building");
1045 for(u32 i=0; i<fastfaces_new.size(); i++)
1047 FastFace &f = fastfaces_new[i];
1049 const u16 indices[] = {0,1,2,2,3,0};
1050 const u16 indices_alternate[] = {0,1,3,2,3,1};
1052 if(f.tile.texture == NULL)
1055 const u16 *indices_p = indices;
1058 Revert triangles for nicer looking gradient if vertices
1059 1 and 3 have same color or 0 and 2 have different color.
1060 getRed() is the day color.
1062 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1063 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1064 indices_p = indices_alternate;
1066 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1071 Add special graphics:
1078 mapblock_mesh_generate_special(data, collector);
1082 Convert MeshCollector to SMesh
1084 ITextureSource *tsrc = m_gamedef->tsrc();
1085 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1087 bool enable_shaders = g_settings->getBool("enable_shaders");
1089 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1091 PreMeshBuffer &p = collector.prebuffers[i];
1092 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1093 <<", p.indices.size()="<<p.indices.size()
1096 // Generate animation data
1098 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1100 // Find the texture name plus ^[crack:N:
1101 std::ostringstream os(std::ios::binary);
1102 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1103 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1104 os<<"o"; // use ^[cracko
1105 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1106 m_crack_materials.insert(std::make_pair(i, os.str()));
1107 // Replace tile texture with the cracked one
1108 p.tile.texture = tsrc->getTexture(
1110 &p.tile.texture_id);
1112 // - Texture animation
1113 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1115 // Add to MapBlockMesh in order to animate these tiles
1116 m_animation_tiles[i] = p.tile;
1117 m_animation_frames[i] = 0;
1118 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1119 // Get starting position from noise
1120 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1121 data->m_blockpos.X, data->m_blockpos.Y,
1122 data->m_blockpos.Z, 0));
1124 // Play all synchronized
1125 m_animation_frame_offsets[i] = 0;
1127 // Replace tile texture with the first animation frame
1128 FrameSpec animation_frame = p.tile.frames.find(0)->second;
1129 p.tile.texture = animation_frame.texture;
1132 for(u32 j = 0; j < p.vertices.size(); j++)
1134 // Note applyFacesShading second parameter is precalculated sqrt
1135 // value for speed improvement
1136 // Skip it for lightsources and top faces.
1137 video::SColor &vc = p.vertices[j].Color;
1138 if (!vc.getBlue()) {
1139 if (p.vertices[j].Normal.Y < -0.5) {
1140 applyFacesShading (vc, 0.447213);
1141 } else if (p.vertices[j].Normal.X > 0.5) {
1142 applyFacesShading (vc, 0.670820);
1143 } else if (p.vertices[j].Normal.X < -0.5) {
1144 applyFacesShading (vc, 0.670820);
1145 } else if (p.vertices[j].Normal.Z > 0.5) {
1146 applyFacesShading (vc, 0.836660);
1147 } else if (p.vertices[j].Normal.Z < -0.5) {
1148 applyFacesShading (vc, 0.836660);
1151 // - Classic lighting
1152 // Set initial real color and store for later updates
1153 u8 day = vc.getRed();
1154 u8 night = vc.getGreen();
1155 finalColorBlend(vc, day, night, 1000);
1157 m_daynight_diffs[i][j] = std::make_pair(day, night);
1161 video::SMaterial material;
1162 material.setFlag(video::EMF_LIGHTING, false);
1163 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1164 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1165 material.setFlag(video::EMF_FOG_ENABLE, true);
1166 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1167 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1168 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1169 material.setTexture(0, p.tile.texture);
1171 if (enable_shaders) {
1172 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1173 p.tile.applyMaterialOptionsWithShaders(material);
1174 if (p.tile.normal_texture) {
1175 material.setTexture(1, p.tile.normal_texture);
1176 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1178 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1181 p.tile.applyMaterialOptions(material);
1184 // Create meshbuffer
1185 // This is a "Standard MeshBuffer",
1186 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1187 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1189 buf->Material = material;
1191 m_mesh->addMeshBuffer(buf);
1194 buf->append(&p.vertices[0], p.vertices.size(),
1195 &p.indices[0], p.indices.size());
1198 m_camera_offset = camera_offset;
1201 Do some stuff to the mesh
1204 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1209 // Usually 1-700 faces and 1-7 materials
1210 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1211 <<"and uses "<<m_mesh->getMeshBufferCount()
1212 <<" materials (meshbuffers)"<<std::endl;
1215 // Use VBO for mesh (this just would set this for ever buffer)
1216 // This will lead to infinite memory usage because or irrlicht.
1217 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1220 NOTE: If that is enabled, some kind of a queue to the main
1221 thread should be made which would call irrlicht to delete
1222 the hardware buffer and then delete the mesh
1226 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1228 // Check if animation is required for this mesh
1230 !m_crack_materials.empty() ||
1231 !m_daynight_diffs.empty() ||
1232 !m_animation_tiles.empty();
1235 MapBlockMesh::~MapBlockMesh()
1241 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1243 bool enable_shaders = g_settings->getBool("enable_shaders");
1245 if(!m_has_animation)
1247 m_animation_force_timer = 100000;
1251 m_animation_force_timer = myrand_range(5, 100);
1254 if(crack != m_last_crack)
1256 for(std::map<u32, std::string>::iterator
1257 i = m_crack_materials.begin();
1258 i != m_crack_materials.end(); i++)
1260 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1261 std::string basename = i->second;
1263 // Create new texture name from original
1264 ITextureSource *tsrc = m_gamedef->getTextureSource();
1265 std::ostringstream os;
1266 os<<basename<<crack;
1267 u32 new_texture_id = 0;
1268 video::ITexture *new_texture =
1269 tsrc->getTexture(os.str(), &new_texture_id);
1270 buf->getMaterial().setTexture(0, new_texture);
1272 // If the current material is also animated,
1273 // update animation info
1274 std::map<u32, TileSpec>::iterator anim_iter =
1275 m_animation_tiles.find(i->first);
1276 if(anim_iter != m_animation_tiles.end()){
1277 TileSpec &tile = anim_iter->second;
1278 tile.texture = new_texture;
1279 tile.texture_id = new_texture_id;
1280 // force animation update
1281 m_animation_frames[i->first] = -1;
1285 m_last_crack = crack;
1288 // Texture animation
1289 for(std::map<u32, TileSpec>::iterator
1290 i = m_animation_tiles.begin();
1291 i != m_animation_tiles.end(); i++)
1293 const TileSpec &tile = i->second;
1294 // Figure out current frame
1295 int frameoffset = m_animation_frame_offsets[i->first];
1296 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1297 + frameoffset) % tile.animation_frame_count;
1298 // If frame doesn't change, skip
1299 if(frame == m_animation_frames[i->first])
1302 m_animation_frames[i->first] = frame;
1304 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1305 ITextureSource *tsrc = m_gamedef->getTextureSource();
1307 FrameSpec animation_frame = tile.frames.find(frame)->second;
1308 buf->getMaterial().setTexture(0, animation_frame.texture);
1309 if (enable_shaders) {
1310 if (animation_frame.normal_texture) {
1311 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1312 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1314 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1319 // Day-night transition
1320 if(daynight_ratio != m_last_daynight_ratio)
1322 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1323 i = m_daynight_diffs.begin();
1324 i != m_daynight_diffs.end(); i++)
1326 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1327 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1328 for(std::map<u32, std::pair<u8, u8 > >::iterator
1329 j = i->second.begin();
1330 j != i->second.end(); j++)
1332 u32 vertexIndex = j->first;
1333 u8 day = j->second.first;
1334 u8 night = j->second.second;
1335 finalColorBlend(vertices[vertexIndex].Color,
1336 day, night, daynight_ratio);
1339 m_last_daynight_ratio = daynight_ratio;
1345 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1347 if (camera_offset != m_camera_offset) {
1348 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1349 m_camera_offset = camera_offset;
1357 void MeshCollector::append(const TileSpec &tile,
1358 const video::S3DVertex *vertices, u32 numVertices,
1359 const u16 *indices, u32 numIndices)
1361 if(numIndices > 65535)
1363 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1367 PreMeshBuffer *p = NULL;
1368 for(u32 i=0; i<prebuffers.size(); i++)
1370 PreMeshBuffer &pp = prebuffers[i];
1373 if(pp.indices.size() + numIndices > 65535)
1384 prebuffers.push_back(pp);
1385 p = &prebuffers[prebuffers.size()-1];
1388 u32 vertex_count = p->vertices.size();
1389 for(u32 i=0; i<numIndices; i++)
1391 u32 j = indices[i] + vertex_count;
1392 p->indices.push_back(j);
1394 for(u32 i=0; i<numVertices; i++)
1396 p->vertices.push_back(vertices[i]);