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, bool use_shaders):
47 m_blockpos(-1337,-1337,-1337),
48 m_crack_pos_relative(-1337, -1337, -1337),
49 m_highlighted_pos_relative(-1337, -1337, -1337),
50 m_smooth_lighting(false),
52 m_highlight_mesh_color(255, 255, 255, 255),
54 m_use_shaders(use_shaders)
57 void MeshMakeData::fill(MapBlock *block)
59 m_blockpos = block->getPos();
61 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
67 // Allocate this block + neighbors
69 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
70 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
71 m_vmanip.addArea(voxel_area);
74 //TimeTaker timer("copy central block data");
78 block->copyTo(m_vmanip);
81 //TimeTaker timer("copy neighbor block data");
85 Copy neighbors. This is lightning fast.
86 Copying only the borders would be *very* slow.
90 Map *map = block->getParent();
92 for(u16 i=0; i<26; i++)
94 const v3s16 &dir = g_26dirs[i];
95 v3s16 bp = m_blockpos + dir;
96 MapBlock *b = map->getBlockNoCreateNoEx(bp);
103 void MeshMakeData::fillSingleNode(MapNode *node)
105 m_blockpos = v3s16(0,0,0);
107 v3s16 blockpos_nodes = v3s16(0,0,0);
108 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
109 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
110 s32 volume = area.getVolume();
111 s32 our_node_index = area.index(1,1,1);
113 // Allocate this block + neighbors
115 m_vmanip.addArea(area);
118 MapNode *data = new MapNode[volume];
119 for(s32 i = 0; i < volume; i++)
121 if(i == our_node_index)
127 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
130 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
134 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
137 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
140 void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
142 m_show_hud = show_hud;
143 m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
146 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
148 m_smooth_lighting = smooth_lighting;
152 Light and vertex color functions
156 Calculate non-smooth lighting at interior of node.
159 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
160 INodeDefManager *ndef)
162 u8 light = n.getLight(bank, ndef);
166 light = undiminish_light(light);
171 light = diminish_light(light);
175 return decode_light(light);
179 Calculate non-smooth lighting at interior of node.
182 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
184 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
185 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
186 return day | (night << 8);
190 Calculate non-smooth lighting at face of node.
193 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
194 v3s16 face_dir, INodeDefManager *ndef)
197 u8 l1 = n.getLight(bank, ndef);
198 u8 l2 = n2.getLight(bank, ndef);
204 // Boost light level for light sources
205 u8 light_source = MYMAX(ndef->get(n).light_source,
206 ndef->get(n2).light_source);
207 if(light_source > light)
208 light = light_source;
210 return decode_light(light);
214 Calculate non-smooth lighting at face of node.
217 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
219 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
220 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
221 return day | (night << 8);
225 Calculate smooth lighting at the XYZ- corner of p.
228 static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
230 static const v3s16 dirs8[8] = {
241 INodeDefManager *ndef = data->m_gamedef->ndef();
243 u16 ambient_occlusion = 0;
245 u8 light_source_max = 0;
249 for (u32 i = 0; i < 8; i++)
251 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
253 // if it's CONTENT_IGNORE we can't do any light calculations
254 if (n.getContent() == CONTENT_IGNORE) {
258 const ContentFeatures &f = ndef->get(n);
259 if (f.light_source > light_source_max)
260 light_source_max = f.light_source;
261 // Check f.solidness because fast-style leaves look better this way
262 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
263 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
264 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
274 light_day /= light_count;
275 light_night /= light_count;
277 // Boost brightness around light sources
278 bool skip_ambient_occlusion_day = false;
279 if(decode_light(light_source_max) >= light_day) {
280 light_day = decode_light(light_source_max);
281 skip_ambient_occlusion_day = true;
284 bool skip_ambient_occlusion_night = false;
285 if(decode_light(light_source_max) >= light_night) {
286 light_night = decode_light(light_source_max);
287 skip_ambient_occlusion_night = true;
290 if (ambient_occlusion > 4)
292 static const float ao_gamma = rangelim(
293 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
295 // Table of gamma space multiply factors.
296 static const float light_amount[3] = {
297 powf(0.75, 1.0 / ao_gamma),
298 powf(0.5, 1.0 / ao_gamma),
299 powf(0.25, 1.0 / ao_gamma)
302 //calculate table index for gamma space multiplier
303 ambient_occlusion -= 5;
305 if (!skip_ambient_occlusion_day)
306 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
307 if (!skip_ambient_occlusion_night)
308 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
311 return light_day | (light_night << 8);
315 Calculate smooth lighting at the given corner of p.
318 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
320 if(corner.X == 1) p.X += 1;
321 // else corner.X == -1
322 if(corner.Y == 1) p.Y += 1;
323 // else corner.Y == -1
324 if(corner.Z == 1) p.Z += 1;
325 // else corner.Z == -1
327 return getSmoothLightCombined(p, data);
331 Converts from day + night color values (0..255)
332 and a given daynight_ratio to the final SColor shown on screen.
334 void finalColorBlend(video::SColor& result,
335 u8 day, u8 night, u32 daynight_ratio)
337 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
341 b += (day - night) / 13;
342 rg -= (day - night) / 23;
344 // Emphase blue a bit in darker places
345 // Each entry of this array represents a range of 8 blue levels
346 static const u8 emphase_blue_when_dark[32] = {
347 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
348 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
350 b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8];
351 b = irr::core::clamp(b, 0, 255);
353 // Artificial light is yellow-ish
354 static const u8 emphase_yellow_when_artificial[16] = {
355 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
357 rg += emphase_yellow_when_artificial[night/16];
358 rg = irr::core::clamp(rg, 0, 255);
366 Mesh generation helpers
370 vertex_dirs: v3s16[4]
372 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
375 If looked from outside the node towards the face, the corners are:
381 if(dir == v3s16(0,0,1))
383 // If looking towards z+, this is the face that is behind
384 // the center point, facing towards z+.
385 vertex_dirs[0] = v3s16(-1,-1, 1);
386 vertex_dirs[1] = v3s16( 1,-1, 1);
387 vertex_dirs[2] = v3s16( 1, 1, 1);
388 vertex_dirs[3] = v3s16(-1, 1, 1);
390 else if(dir == v3s16(0,0,-1))
393 vertex_dirs[0] = v3s16( 1,-1,-1);
394 vertex_dirs[1] = v3s16(-1,-1,-1);
395 vertex_dirs[2] = v3s16(-1, 1,-1);
396 vertex_dirs[3] = v3s16( 1, 1,-1);
398 else if(dir == v3s16(1,0,0))
401 vertex_dirs[0] = v3s16( 1,-1, 1);
402 vertex_dirs[1] = v3s16( 1,-1,-1);
403 vertex_dirs[2] = v3s16( 1, 1,-1);
404 vertex_dirs[3] = v3s16( 1, 1, 1);
406 else if(dir == v3s16(-1,0,0))
409 vertex_dirs[0] = v3s16(-1,-1,-1);
410 vertex_dirs[1] = v3s16(-1,-1, 1);
411 vertex_dirs[2] = v3s16(-1, 1, 1);
412 vertex_dirs[3] = v3s16(-1, 1,-1);
414 else if(dir == v3s16(0,1,0))
416 // faces towards Y+ (assume Z- as "down" in texture)
417 vertex_dirs[0] = v3s16( 1, 1,-1);
418 vertex_dirs[1] = v3s16(-1, 1,-1);
419 vertex_dirs[2] = v3s16(-1, 1, 1);
420 vertex_dirs[3] = v3s16( 1, 1, 1);
422 else if(dir == v3s16(0,-1,0))
424 // faces towards Y- (assume Z+ as "down" in texture)
425 vertex_dirs[0] = v3s16( 1,-1, 1);
426 vertex_dirs[1] = v3s16(-1,-1, 1);
427 vertex_dirs[2] = v3s16(-1,-1,-1);
428 vertex_dirs[3] = v3s16( 1,-1,-1);
435 video::S3DVertex vertices[4]; // Precalculated vertices
438 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
439 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
443 // Position is at the center of the cube.
452 v3s16 vertex_dirs[4];
453 getNodeVertexDirs(dir, vertex_dirs);
457 switch (tile.rotation)
463 vertex_dirs[0] = vertex_dirs[3];
464 vertex_dirs[3] = vertex_dirs[2];
465 vertex_dirs[2] = vertex_dirs[1];
475 vertex_dirs[0] = vertex_dirs[2];
478 vertex_dirs[1] = vertex_dirs[3];
489 vertex_dirs[0] = vertex_dirs[1];
490 vertex_dirs[1] = vertex_dirs[2];
491 vertex_dirs[2] = vertex_dirs[3];
501 vertex_dirs[0] = vertex_dirs[3];
502 vertex_dirs[3] = vertex_dirs[2];
503 vertex_dirs[2] = vertex_dirs[1];
515 vertex_dirs[0] = vertex_dirs[1];
516 vertex_dirs[1] = vertex_dirs[2];
517 vertex_dirs[2] = vertex_dirs[3];
529 vertex_dirs[0] = vertex_dirs[3];
530 vertex_dirs[3] = vertex_dirs[2];
531 vertex_dirs[2] = vertex_dirs[1];
543 vertex_dirs[0] = vertex_dirs[1];
544 vertex_dirs[1] = vertex_dirs[2];
545 vertex_dirs[2] = vertex_dirs[3];
567 for(u16 i=0; i<4; i++)
570 BS/2*vertex_dirs[i].X,
571 BS/2*vertex_dirs[i].Y,
572 BS/2*vertex_dirs[i].Z
576 for(u16 i=0; i<4; i++)
578 vertex_pos[i].X *= scale.X;
579 vertex_pos[i].Y *= scale.Y;
580 vertex_pos[i].Z *= scale.Z;
581 vertex_pos[i] += pos;
585 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
586 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
587 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
589 v3f normal(dir.X, dir.Y, dir.Z);
591 u8 alpha = tile.alpha;
593 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
594 MapBlock_LightColor(alpha, li0, light_source),
595 core::vector2d<f32>(x0+w*abs_scale, y0+h));
596 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
597 MapBlock_LightColor(alpha, li1, light_source),
598 core::vector2d<f32>(x0, y0+h));
599 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
600 MapBlock_LightColor(alpha, li2, light_source),
601 core::vector2d<f32>(x0, y0));
602 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
603 MapBlock_LightColor(alpha, li3, light_source),
604 core::vector2d<f32>(x0+w*abs_scale, y0));
607 dest.push_back(face);
611 Nodes make a face if contents differ and solidness differs.
614 1: Face uses m1's content
615 2: Face uses m2's content
616 equivalent: Whether the blocks share the same face (eg. water and glass)
618 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
620 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
621 INodeDefManager *ndef)
625 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
628 bool contents_differ = (m1 != m2);
630 const ContentFeatures &f1 = ndef->get(m1);
631 const ContentFeatures &f2 = ndef->get(m2);
633 // Contents don't differ for different forms of same liquid
634 if(f1.sameLiquid(f2))
635 contents_differ = false;
637 u8 c1 = f1.solidness;
638 u8 c2 = f2.solidness;
640 bool solidness_differs = (c1 != c2);
641 bool makes_face = contents_differ && solidness_differs;
643 if(makes_face == false)
647 c1 = f1.visual_solidness;
649 c2 = f2.visual_solidness;
653 // If same solidness, liquid takes precense
667 Gets nth node tile (0 <= n <= 5).
669 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
671 INodeDefManager *ndef = data->m_gamedef->ndef();
672 TileSpec spec = ndef->get(mn).tiles[tileindex];
673 // Apply temporary crack
674 if (p == data->m_crack_pos_relative)
675 spec.material_flags |= MATERIAL_FLAG_CRACK;
680 Gets node tile given a face direction.
682 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
684 INodeDefManager *ndef = data->m_gamedef->ndef();
686 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
687 // (0,0,1), (0,0,-1) or (0,0,0)
688 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
690 // Convert direction to single integer for table lookup
695 // 4 = invalid, treat as (0,0,0)
699 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
701 // Get rotation for things like chests
702 u8 facedir = mn.getFaceDir(ndef);
704 static const u16 dir_to_tile[24 * 16] =
706 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
707 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
708 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
709 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
710 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
712 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
713 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
714 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
715 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
717 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
718 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
719 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
720 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
722 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
723 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
724 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
725 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
727 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
728 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
729 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
730 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
732 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
733 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
734 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
735 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
738 u16 tile_index=facedir*16 + dir_i;
739 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
740 spec.rotation=dir_to_tile[tile_index + 1];
741 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
745 static void getTileInfo(
753 v3s16 &face_dir_corrected,
759 VoxelManipulator &vmanip = data->m_vmanip;
760 INodeDefManager *ndef = data->m_gamedef->ndef();
761 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
763 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
765 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
766 if (n0.getContent() == CONTENT_IGNORE ) {
770 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
773 bool equivalent = false;
774 u8 mf = face_contents(n0.getContent(), n1.getContent(),
787 tile = getNodeTile(n0, p, face_dir, data);
789 face_dir_corrected = face_dir;
790 light_source = ndef->get(n0).light_source;
794 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
795 p_corrected = p + face_dir;
796 face_dir_corrected = -face_dir;
797 light_source = ndef->get(n1).light_source;
800 // eg. water and glass
802 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
804 if(data->m_smooth_lighting == false)
806 lights[0] = lights[1] = lights[2] = lights[3] =
807 getFaceLight(n0, n1, face_dir, ndef);
811 v3s16 vertex_dirs[4];
812 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
813 for(u16 i=0; i<4; i++)
815 lights[i] = getSmoothLight(
816 blockpos_nodes + p_corrected,
817 vertex_dirs[i], data);
826 translate_dir: unit vector with only one of x, y or z
827 face_dir: unit vector with only one of x, y or z
829 static void updateFastFaceRow(
836 std::vector<FastFace> &dest)
840 u16 continuous_tiles_count = 0;
842 bool makes_face = false;
844 v3s16 face_dir_corrected;
845 u16 lights[4] = {0,0,0,0};
848 getTileInfo(data, p, face_dir,
849 makes_face, p_corrected, face_dir_corrected,
850 lights, tile, light_source);
852 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
854 // If tiling can be done, this is set to false in the next step
855 bool next_is_different = true;
859 bool next_makes_face = false;
860 v3s16 next_p_corrected;
861 v3s16 next_face_dir_corrected;
862 u16 next_lights[4] = {0,0,0,0};
864 u8 next_light_source = 0;
866 // If at last position, there is nothing to compare to and
867 // the face must be drawn anyway
868 if(j != MAP_BLOCKSIZE - 1)
870 p_next = p + translate_dir;
872 getTileInfo(data, p_next, face_dir,
873 next_makes_face, next_p_corrected,
874 next_face_dir_corrected, next_lights,
875 next_tile, next_light_source);
877 if(next_makes_face == makes_face
878 && next_p_corrected == p_corrected + translate_dir
879 && next_face_dir_corrected == face_dir_corrected
880 && next_lights[0] == lights[0]
881 && next_lights[1] == lights[1]
882 && next_lights[2] == lights[2]
883 && next_lights[3] == lights[3]
885 && tile.rotation == 0
886 && next_light_source == light_source)
888 next_is_different = false;
892 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
893 next_makes_face != makes_face ? 1 : 0);
894 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
895 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
896 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
897 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
898 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
899 (next_lights[0] != lights[0] ||
900 next_lights[0] != lights[0] ||
901 next_lights[0] != lights[0] ||
902 next_lights[0] != lights[0]) ? 1 : 0);
903 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
904 !(next_tile == tile) ? 1 : 0);
907 /*g_profiler->add("Meshgen: Total faces checked", 1);
909 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
912 g_profiler->add("Meshgen: diff: last position", 1);*/
915 continuous_tiles_count++;
917 if(next_is_different)
920 Create a face if there should be one
924 // Floating point conversion of the position vector
925 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
926 // Center point of face (kind of)
927 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
928 if(continuous_tiles_count != 1)
929 sp += translate_dir_f;
932 if(translate_dir.X != 0) {
933 scale.X = continuous_tiles_count;
935 if(translate_dir.Y != 0) {
936 scale.Y = continuous_tiles_count;
938 if(translate_dir.Z != 0) {
939 scale.Z = continuous_tiles_count;
942 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
943 sp, face_dir_corrected, scale, light_source,
946 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
947 for(int i = 1; i < continuous_tiles_count; i++){
948 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
952 continuous_tiles_count = 0;
954 makes_face = next_makes_face;
955 p_corrected = next_p_corrected;
956 face_dir_corrected = next_face_dir_corrected;
957 lights[0] = next_lights[0];
958 lights[1] = next_lights[1];
959 lights[2] = next_lights[2];
960 lights[3] = next_lights[3];
962 light_source = next_light_source;
969 static void updateAllFastFaceRows(MeshMakeData *data,
970 std::vector<FastFace> &dest)
973 Go through every y,z and get top(y+) faces in rows of x+
975 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
976 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
977 updateFastFaceRow(data,
981 v3s16(0,1,0), //face dir
988 Go through every x,y and get right(x+) faces in rows of z+
990 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
991 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
992 updateFastFaceRow(data,
996 v3s16(1,0,0), //face dir
1003 Go through every y,z and get back(z+) faces in rows of x+
1005 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1006 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1007 updateFastFaceRow(data,
1011 v3s16(0,0,1), //face dir
1022 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1023 m_mesh(new scene::SMesh()),
1024 m_gamedef(data->m_gamedef),
1025 m_animation_force_timer(0), // force initial animation
1027 m_crack_materials(),
1028 m_highlighted_materials(),
1029 m_last_daynight_ratio((u32) -1),
1032 m_enable_shaders = data->m_use_shaders;
1033 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1035 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1036 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1037 //TimeTaker timer1("MapBlockMesh()");
1039 std::vector<FastFace> fastfaces_new;
1042 We are including the faces of the trailing edges of the block.
1043 This means that when something changes, the caller must
1044 also update the meshes of the blocks at the leading edges.
1046 NOTE: This is the slowest part of this method.
1049 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1050 //TimeTaker timer2("updateAllFastFaceRows()");
1051 updateAllFastFaceRows(data, fastfaces_new);
1056 Convert FastFaces to MeshCollector
1059 MeshCollector collector;
1062 // avg 0ms (100ms spikes when loading textures the first time)
1063 // (NOTE: probably outdated)
1064 //TimeTaker timer2("MeshCollector building");
1066 for(u32 i=0; i<fastfaces_new.size(); i++)
1068 FastFace &f = fastfaces_new[i];
1070 const u16 indices[] = {0,1,2,2,3,0};
1071 const u16 indices_alternate[] = {0,1,3,2,3,1};
1073 if(f.tile.texture == NULL)
1076 const u16 *indices_p = indices;
1079 Revert triangles for nicer looking gradient if vertices
1080 1 and 3 have same color or 0 and 2 have different color.
1081 getRed() is the day color.
1083 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1084 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1085 indices_p = indices_alternate;
1087 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1092 Add special graphics:
1099 mapblock_mesh_generate_special(data, collector);
1101 m_highlight_mesh_color = data->m_highlight_mesh_color;
1104 Convert MeshCollector to SMesh
1106 ITextureSource *tsrc = m_gamedef->tsrc();
1107 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1109 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1111 PreMeshBuffer &p = collector.prebuffers[i];
1113 // Generate animation data
1115 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1117 // Find the texture name plus ^[crack:N:
1118 std::ostringstream os(std::ios::binary);
1119 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1120 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1121 os<<"o"; // use ^[cracko
1122 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1123 m_crack_materials.insert(std::make_pair(i, os.str()));
1124 // Replace tile texture with the cracked one
1125 p.tile.texture = tsrc->getTexture(
1127 &p.tile.texture_id);
1129 // - Texture animation
1130 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1132 // Add to MapBlockMesh in order to animate these tiles
1133 m_animation_tiles[i] = p.tile;
1134 m_animation_frames[i] = 0;
1135 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1136 // Get starting position from noise
1137 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1138 data->m_blockpos.X, data->m_blockpos.Y,
1139 data->m_blockpos.Z, 0));
1141 // Play all synchronized
1142 m_animation_frame_offsets[i] = 0;
1144 // Replace tile texture with the first animation frame
1145 FrameSpec animation_frame = p.tile.frames[0];
1146 p.tile.texture = animation_frame.texture;
1149 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1150 m_highlighted_materials.push_back(i);
1152 for(u32 j = 0; j < p.vertices.size(); j++)
1154 // Note applyFacesShading second parameter is precalculated sqrt
1155 // value for speed improvement
1156 // Skip it for lightsources and top faces.
1157 video::SColor &vc = p.vertices[j].Color;
1158 if (!vc.getBlue()) {
1159 if (p.vertices[j].Normal.Y < -0.5) {
1160 applyFacesShading (vc, 0.447213);
1161 } else if (p.vertices[j].Normal.X > 0.5) {
1162 applyFacesShading (vc, 0.670820);
1163 } else if (p.vertices[j].Normal.X < -0.5) {
1164 applyFacesShading (vc, 0.670820);
1165 } else if (p.vertices[j].Normal.Z > 0.5) {
1166 applyFacesShading (vc, 0.836660);
1167 } else if (p.vertices[j].Normal.Z < -0.5) {
1168 applyFacesShading (vc, 0.836660);
1171 if(!m_enable_shaders)
1173 // - Classic lighting (shaders handle this by themselves)
1174 // Set initial real color and store for later updates
1175 u8 day = vc.getRed();
1176 u8 night = vc.getGreen();
1177 finalColorBlend(vc, day, night, 1000);
1179 m_daynight_diffs[i][j] = std::make_pair(day, night);
1184 video::SMaterial material;
1185 material.setFlag(video::EMF_LIGHTING, false);
1186 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1187 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1188 material.setFlag(video::EMF_FOG_ENABLE, true);
1189 material.setTexture(0, p.tile.texture);
1191 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1192 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1194 if (m_enable_shaders) {
1195 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1196 p.tile.applyMaterialOptionsWithShaders(material);
1197 if (p.tile.normal_texture) {
1198 material.setTexture(1, p.tile.normal_texture);
1199 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1201 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1204 p.tile.applyMaterialOptions(material);
1208 // Create meshbuffer
1209 // This is a "Standard MeshBuffer",
1210 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1211 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1213 buf->Material = material;
1215 m_mesh->addMeshBuffer(buf);
1218 buf->append(&p.vertices[0], p.vertices.size(),
1219 &p.indices[0], p.indices.size());
1222 m_camera_offset = camera_offset;
1225 Do some stuff to the mesh
1228 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1233 // Usually 1-700 faces and 1-7 materials
1234 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1235 <<"and uses "<<m_mesh->getMeshBufferCount()
1236 <<" materials (meshbuffers)"<<std::endl;
1239 // Use VBO for mesh (this just would set this for ever buffer)
1240 // This will lead to infinite memory usage because or irrlicht.
1241 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1244 NOTE: If that is enabled, some kind of a queue to the main
1245 thread should be made which would call irrlicht to delete
1246 the hardware buffer and then delete the mesh
1250 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1252 // Check if animation is required for this mesh
1254 !m_crack_materials.empty() ||
1255 !m_daynight_diffs.empty() ||
1256 !m_animation_tiles.empty() ||
1257 !m_highlighted_materials.empty();
1260 MapBlockMesh::~MapBlockMesh()
1266 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1269 if(!m_has_animation)
1271 m_animation_force_timer = 100000;
1275 m_animation_force_timer = myrand_range(5, 100);
1278 if(crack != m_last_crack)
1280 for(std::map<u32, std::string>::iterator
1281 i = m_crack_materials.begin();
1282 i != m_crack_materials.end(); i++)
1284 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1285 std::string basename = i->second;
1287 // Create new texture name from original
1288 ITextureSource *tsrc = m_gamedef->getTextureSource();
1289 std::ostringstream os;
1290 os<<basename<<crack;
1291 u32 new_texture_id = 0;
1292 video::ITexture *new_texture =
1293 tsrc->getTexture(os.str(), &new_texture_id);
1294 buf->getMaterial().setTexture(0, new_texture);
1296 // If the current material is also animated,
1297 // update animation info
1298 std::map<u32, TileSpec>::iterator anim_iter =
1299 m_animation_tiles.find(i->first);
1300 if(anim_iter != m_animation_tiles.end()){
1301 TileSpec &tile = anim_iter->second;
1302 tile.texture = new_texture;
1303 tile.texture_id = new_texture_id;
1304 // force animation update
1305 m_animation_frames[i->first] = -1;
1309 m_last_crack = crack;
1312 // Texture animation
1313 for(std::map<u32, TileSpec>::iterator
1314 i = m_animation_tiles.begin();
1315 i != m_animation_tiles.end(); i++)
1317 const TileSpec &tile = i->second;
1318 // Figure out current frame
1319 int frameoffset = m_animation_frame_offsets[i->first];
1320 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1321 + frameoffset) % tile.animation_frame_count;
1322 // If frame doesn't change, skip
1323 if(frame == m_animation_frames[i->first])
1326 m_animation_frames[i->first] = frame;
1328 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1329 ITextureSource *tsrc = m_gamedef->getTextureSource();
1331 FrameSpec animation_frame = tile.frames[frame];
1332 buf->getMaterial().setTexture(0, animation_frame.texture);
1333 if (m_enable_shaders) {
1334 if (animation_frame.normal_texture) {
1335 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1336 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1338 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1343 // Day-night transition
1344 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1346 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1347 i = m_daynight_diffs.begin();
1348 i != m_daynight_diffs.end(); i++)
1350 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1351 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1352 for(std::map<u32, std::pair<u8, u8 > >::iterator
1353 j = i->second.begin();
1354 j != i->second.end(); j++)
1356 u32 vertexIndex = j->first;
1357 u8 day = j->second.first;
1358 u8 night = j->second.second;
1359 finalColorBlend(vertices[vertexIndex].Color,
1360 day, night, daynight_ratio);
1363 m_last_daynight_ratio = daynight_ratio;
1366 // Node highlighting
1367 if (m_enable_highlighting) {
1368 u8 day = m_highlight_mesh_color.getRed();
1369 u8 night = m_highlight_mesh_color.getGreen();
1371 finalColorBlend(hc, day, night, daynight_ratio);
1372 float sin_r = 0.07 * sin(1.5 * time);
1373 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1374 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1375 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1376 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1377 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1379 for(std::list<u32>::iterator
1380 i = m_highlighted_materials.begin();
1381 i != m_highlighted_materials.end(); i++)
1383 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1384 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1385 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1386 vertices[j].Color = hc;
1393 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1395 if (camera_offset != m_camera_offset) {
1396 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1397 m_camera_offset = camera_offset;
1405 void MeshCollector::append(const TileSpec &tile,
1406 const video::S3DVertex *vertices, u32 numVertices,
1407 const u16 *indices, u32 numIndices)
1409 if(numIndices > 65535)
1411 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1415 PreMeshBuffer *p = NULL;
1416 for(u32 i=0; i<prebuffers.size(); i++)
1418 PreMeshBuffer &pp = prebuffers[i];
1421 if(pp.indices.size() + numIndices > 65535)
1432 prebuffers.push_back(pp);
1433 p = &prebuffers[prebuffers.size()-1];
1436 u32 vertex_count = p->vertices.size();
1437 for(u32 i=0; i<numIndices; i++)
1439 u32 j = indices[i] + vertex_count;
1440 p->indices.push_back(j);
1442 for(u32 i=0; i<numVertices; i++)
1444 p->vertices.push_back(vertices[i]);
1449 MeshCollector - for meshnodes and converted drawtypes.
1452 void MeshCollector::append(const TileSpec &tile,
1453 const video::S3DVertex *vertices, u32 numVertices,
1454 const u16 *indices, u32 numIndices,
1455 v3f pos, video::SColor c)
1457 if(numIndices > 65535)
1459 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1463 PreMeshBuffer *p = NULL;
1464 for(u32 i=0; i<prebuffers.size(); i++)
1466 PreMeshBuffer &pp = prebuffers[i];
1469 if(pp.indices.size() + numIndices > 65535)
1480 prebuffers.push_back(pp);
1481 p = &prebuffers[prebuffers.size()-1];
1484 u32 vertex_count = p->vertices.size();
1485 for(u32 i=0; i<numIndices; i++)
1487 u32 j = indices[i] + vertex_count;
1488 p->indices.push_back(j);
1490 for(u32 i=0; i<numVertices; i++)
1492 video::S3DVertex vert = vertices[i];
1495 p->vertices.push_back(vert);