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 //return decode_light(undiminish_light(light_source));
198 if(light_source > light)
199 //return decode_light(light_source);
200 light = light_source;
202 return decode_light(light);
206 Calculate non-smooth lighting at face of node.
209 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
211 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
212 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
213 return day | (night << 8);
217 Calculate smooth lighting at the XYZ- corner of p.
220 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
222 static v3s16 dirs8[8] = {
233 INodeDefManager *ndef = data->m_gamedef->ndef();
235 u16 ambient_occlusion = 0;
238 u8 light_source_max = 0;
239 for(u32 i=0; i<8; i++)
241 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
243 // if it's CONTENT_IGNORE we can't do any light calculations
244 if (n.getContent() == CONTENT_IGNORE) {
248 const ContentFeatures &f = ndef->get(n);
249 if(f.light_source > light_source_max)
250 light_source_max = f.light_source;
251 // Check f.solidness because fast-style leaves look
253 if(f.param_type == CPT_LIGHT && f.solidness != 2)
255 light += decode_light(n.getLight(bank, ndef));
266 light /= light_count;
268 // Boost brightness around light sources
269 if(decode_light(light_source_max) >= light)
270 //return decode_light(undiminish_light(light_source_max));
271 return decode_light(light_source_max);
273 if(ambient_occlusion > 4)
275 //ambient_occlusion -= 4;
276 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
277 float light_amount = (8 - ambient_occlusion) / 4.0;
278 float light_f = (float)light / 255.0;
279 light_f = pow(light_f, 2.2f); // gamma -> linear space
280 light_f = light_f * light_amount;
281 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
284 light = 255.0 * light_f + 0.5;
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)
663 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.5) * translate_dir_f;
918 if(continuous_tiles_count != 1)
919 sp += translate_dir_f;
922 if(translate_dir.X != 0)
924 scale.X = continuous_tiles_count;
926 if(translate_dir.Y != 0)
928 scale.Y = continuous_tiles_count;
930 if(translate_dir.Z != 0)
932 scale.Z = continuous_tiles_count;
935 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
936 sp, face_dir_corrected, scale, light_source,
939 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
940 for(int i=1; i<continuous_tiles_count; i++){
941 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
945 continuous_tiles_count = 0;
947 makes_face = next_makes_face;
948 p_corrected = next_p_corrected;
949 face_dir_corrected = next_face_dir_corrected;
950 lights[0] = next_lights[0];
951 lights[1] = next_lights[1];
952 lights[2] = next_lights[2];
953 lights[3] = next_lights[3];
955 light_source = next_light_source;
962 static void updateAllFastFaceRows(MeshMakeData *data,
963 std::vector<FastFace> &dest)
966 Go through every y,z and get top(y+) faces in rows of x+
968 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
969 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
970 updateFastFaceRow(data,
974 v3s16(0,1,0), //face dir
981 Go through every x,y and get right(x+) faces in rows of z+
983 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
984 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
985 updateFastFaceRow(data,
989 v3s16(1,0,0), //face dir
996 Go through every y,z and get back(z+) faces in rows of x+
998 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
999 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1000 updateFastFaceRow(data,
1004 v3s16(0,0,1), //face dir
1015 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1016 m_mesh(new scene::SMesh()),
1017 m_gamedef(data->m_gamedef),
1018 m_animation_force_timer(0), // force initial animation
1020 m_crack_materials(),
1021 m_last_daynight_ratio((u32) -1),
1024 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1025 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1026 //TimeTaker timer1("MapBlockMesh()");
1028 std::vector<FastFace> fastfaces_new;
1031 We are including the faces of the trailing edges of the block.
1032 This means that when something changes, the caller must
1033 also update the meshes of the blocks at the leading edges.
1035 NOTE: This is the slowest part of this method.
1038 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1039 //TimeTaker timer2("updateAllFastFaceRows()");
1040 updateAllFastFaceRows(data, fastfaces_new);
1045 Convert FastFaces to MeshCollector
1048 MeshCollector collector;
1051 // avg 0ms (100ms spikes when loading textures the first time)
1052 // (NOTE: probably outdated)
1053 //TimeTaker timer2("MeshCollector building");
1055 for(u32 i=0; i<fastfaces_new.size(); i++)
1057 FastFace &f = fastfaces_new[i];
1059 const u16 indices[] = {0,1,2,2,3,0};
1060 const u16 indices_alternate[] = {0,1,3,2,3,1};
1062 if(f.tile.texture == NULL)
1065 const u16 *indices_p = indices;
1068 Revert triangles for nicer looking gradient if vertices
1069 1 and 3 have same color or 0 and 2 have different color.
1070 getRed() is the day color.
1072 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1073 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1074 indices_p = indices_alternate;
1076 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1081 Add special graphics:
1088 mapblock_mesh_generate_special(data, collector);
1092 Convert MeshCollector to SMesh
1094 ITextureSource *tsrc = m_gamedef->tsrc();
1095 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1097 bool enable_shaders = g_settings->getBool("enable_shaders");
1099 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1101 PreMeshBuffer &p = collector.prebuffers[i];
1102 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1103 <<", p.indices.size()="<<p.indices.size()
1106 // Generate animation data
1108 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1110 // Find the texture name plus ^[crack:N:
1111 std::ostringstream os(std::ios::binary);
1112 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1113 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1114 os<<"o"; // use ^[cracko
1115 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1116 m_crack_materials.insert(std::make_pair(i, os.str()));
1117 // Replace tile texture with the cracked one
1118 p.tile.texture = tsrc->getTexture(
1120 &p.tile.texture_id);
1122 // - Texture animation
1123 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1125 // Add to MapBlockMesh in order to animate these tiles
1126 m_animation_tiles[i] = p.tile;
1127 m_animation_frames[i] = 0;
1128 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1129 // Get starting position from noise
1130 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1131 data->m_blockpos.X, data->m_blockpos.Y,
1132 data->m_blockpos.Z, 0));
1134 // Play all synchronized
1135 m_animation_frame_offsets[i] = 0;
1137 // Replace tile texture with the first animation frame
1138 FrameSpec animation_frame = p.tile.frames.find(0)->second;
1139 p.tile.texture = animation_frame.texture;
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);
1163 // - Classic lighting (shaders handle this by themselves)
1164 // Set initial real color and store for later updates
1165 u8 day = vc.getRed();
1166 u8 night = vc.getGreen();
1167 finalColorBlend(vc, day, night, 1000);
1169 m_daynight_diffs[i][j] = std::make_pair(day, night);
1174 video::SMaterial material;
1175 material.setFlag(video::EMF_LIGHTING, false);
1176 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1177 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1178 material.setFlag(video::EMF_FOG_ENABLE, true);
1179 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1180 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1181 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1182 material.setTexture(0, p.tile.texture);
1184 if (enable_shaders) {
1185 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1186 p.tile.applyMaterialOptionsWithShaders(material);
1187 if (p.tile.normal_texture) {
1188 material.setTexture(1, p.tile.normal_texture);
1189 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1191 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1194 p.tile.applyMaterialOptions(material);
1197 // Create meshbuffer
1198 // This is a "Standard MeshBuffer",
1199 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1200 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1202 buf->Material = material;
1204 m_mesh->addMeshBuffer(buf);
1207 buf->append(&p.vertices[0], p.vertices.size(),
1208 &p.indices[0], p.indices.size());
1211 m_camera_offset = camera_offset;
1214 Do some stuff to the mesh
1217 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1222 // Usually 1-700 faces and 1-7 materials
1223 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1224 <<"and uses "<<m_mesh->getMeshBufferCount()
1225 <<" materials (meshbuffers)"<<std::endl;
1228 // Use VBO for mesh (this just would set this for ever buffer)
1229 // This will lead to infinite memory usage because or irrlicht.
1230 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1233 NOTE: If that is enabled, some kind of a queue to the main
1234 thread should be made which would call irrlicht to delete
1235 the hardware buffer and then delete the mesh
1239 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1241 // Check if animation is required for this mesh
1243 !m_crack_materials.empty() ||
1244 !m_daynight_diffs.empty() ||
1245 !m_animation_tiles.empty();
1248 MapBlockMesh::~MapBlockMesh()
1254 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1256 bool enable_shaders = g_settings->getBool("enable_shaders");
1258 if(!m_has_animation)
1260 m_animation_force_timer = 100000;
1264 m_animation_force_timer = myrand_range(5, 100);
1267 if(crack != m_last_crack)
1269 for(std::map<u32, std::string>::iterator
1270 i = m_crack_materials.begin();
1271 i != m_crack_materials.end(); i++)
1273 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1274 std::string basename = i->second;
1276 // Create new texture name from original
1277 ITextureSource *tsrc = m_gamedef->getTextureSource();
1278 std::ostringstream os;
1279 os<<basename<<crack;
1280 u32 new_texture_id = 0;
1281 video::ITexture *new_texture =
1282 tsrc->getTexture(os.str(), &new_texture_id);
1283 buf->getMaterial().setTexture(0, new_texture);
1285 // If the current material is also animated,
1286 // update animation info
1287 std::map<u32, TileSpec>::iterator anim_iter =
1288 m_animation_tiles.find(i->first);
1289 if(anim_iter != m_animation_tiles.end()){
1290 TileSpec &tile = anim_iter->second;
1291 tile.texture = new_texture;
1292 tile.texture_id = new_texture_id;
1293 // force animation update
1294 m_animation_frames[i->first] = -1;
1298 m_last_crack = crack;
1301 // Texture animation
1302 for(std::map<u32, TileSpec>::iterator
1303 i = m_animation_tiles.begin();
1304 i != m_animation_tiles.end(); i++)
1306 const TileSpec &tile = i->second;
1307 // Figure out current frame
1308 int frameoffset = m_animation_frame_offsets[i->first];
1309 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1310 + frameoffset) % tile.animation_frame_count;
1311 // If frame doesn't change, skip
1312 if(frame == m_animation_frames[i->first])
1315 m_animation_frames[i->first] = frame;
1317 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1318 ITextureSource *tsrc = m_gamedef->getTextureSource();
1320 FrameSpec animation_frame = tile.frames.find(frame)->second;
1321 buf->getMaterial().setTexture(0, animation_frame.texture);
1322 if (enable_shaders) {
1323 if (animation_frame.normal_texture) {
1324 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1325 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1327 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1332 // Day-night transition
1333 if(daynight_ratio != m_last_daynight_ratio)
1335 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1336 i = m_daynight_diffs.begin();
1337 i != m_daynight_diffs.end(); i++)
1339 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1340 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1341 for(std::map<u32, std::pair<u8, u8 > >::iterator
1342 j = i->second.begin();
1343 j != i->second.end(); j++)
1345 u32 vertexIndex = j->first;
1346 u8 day = j->second.first;
1347 u8 night = j->second.second;
1348 finalColorBlend(vertices[vertexIndex].Color,
1349 day, night, daynight_ratio);
1352 m_last_daynight_ratio = daynight_ratio;
1358 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1360 if (camera_offset != m_camera_offset) {
1361 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1362 m_camera_offset = camera_offset;
1370 void MeshCollector::append(const TileSpec &tile,
1371 const video::S3DVertex *vertices, u32 numVertices,
1372 const u16 *indices, u32 numIndices)
1374 if(numIndices > 65535)
1376 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1380 PreMeshBuffer *p = NULL;
1381 for(u32 i=0; i<prebuffers.size(); i++)
1383 PreMeshBuffer &pp = prebuffers[i];
1386 if(pp.indices.size() + numIndices > 65535)
1397 prebuffers.push_back(pp);
1398 p = &prebuffers[prebuffers.size()-1];
1401 u32 vertex_count = p->vertices.size();
1402 for(u32 i=0; i<numIndices; i++)
1404 u32 j = indices[i] + vertex_count;
1405 p->indices.push_back(j);
1407 for(u32 i=0; i<numVertices; i++)
1409 p->vertices.push_back(vertices[i]);