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 const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(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)
441 // Position is at the center of the cube.
450 v3s16 vertex_dirs[4];
451 getNodeVertexDirs(dir, vertex_dirs);
455 switch (tile.rotation)
461 vertex_dirs[0] = vertex_dirs[3];
462 vertex_dirs[3] = vertex_dirs[2];
463 vertex_dirs[2] = vertex_dirs[1];
473 vertex_dirs[0] = vertex_dirs[2];
476 vertex_dirs[1] = vertex_dirs[3];
487 vertex_dirs[0] = vertex_dirs[1];
488 vertex_dirs[1] = vertex_dirs[2];
489 vertex_dirs[2] = vertex_dirs[3];
499 vertex_dirs[0] = vertex_dirs[3];
500 vertex_dirs[3] = vertex_dirs[2];
501 vertex_dirs[2] = vertex_dirs[1];
513 vertex_dirs[0] = vertex_dirs[1];
514 vertex_dirs[1] = vertex_dirs[2];
515 vertex_dirs[2] = vertex_dirs[3];
527 vertex_dirs[0] = vertex_dirs[3];
528 vertex_dirs[3] = vertex_dirs[2];
529 vertex_dirs[2] = vertex_dirs[1];
541 vertex_dirs[0] = vertex_dirs[1];
542 vertex_dirs[1] = vertex_dirs[2];
543 vertex_dirs[2] = vertex_dirs[3];
565 for(u16 i=0; i<4; i++)
568 BS/2*vertex_dirs[i].X,
569 BS/2*vertex_dirs[i].Y,
570 BS/2*vertex_dirs[i].Z
574 for(u16 i=0; i<4; i++)
576 vertex_pos[i].X *= scale.X;
577 vertex_pos[i].Y *= scale.Y;
578 vertex_pos[i].Z *= scale.Z;
579 vertex_pos[i] += pos;
583 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
584 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
585 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
587 v3f normal(dir.X, dir.Y, dir.Z);
589 u8 alpha = tile.alpha;
591 dest.push_back(FastFace());
593 FastFace& face = *dest.rbegin();
595 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
596 MapBlock_LightColor(alpha, li0, light_source),
597 core::vector2d<f32>(x0+w*abs_scale, y0+h));
598 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
599 MapBlock_LightColor(alpha, li1, light_source),
600 core::vector2d<f32>(x0, y0+h));
601 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
602 MapBlock_LightColor(alpha, li2, light_source),
603 core::vector2d<f32>(x0, y0));
604 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
605 MapBlock_LightColor(alpha, li3, light_source),
606 core::vector2d<f32>(x0+w*abs_scale, y0));
612 Nodes make a face if contents differ and solidness differs.
615 1: Face uses m1's content
616 2: Face uses m2's content
617 equivalent: Whether the blocks share the same face (eg. water and glass)
619 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
621 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
622 INodeDefManager *ndef)
626 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
629 bool contents_differ = (m1 != m2);
631 const ContentFeatures &f1 = ndef->get(m1);
632 const ContentFeatures &f2 = ndef->get(m2);
634 // Contents don't differ for different forms of same liquid
635 if(f1.sameLiquid(f2))
636 contents_differ = false;
638 u8 c1 = f1.solidness;
639 u8 c2 = f2.solidness;
641 bool solidness_differs = (c1 != c2);
642 bool makes_face = contents_differ && solidness_differs;
644 if(makes_face == false)
648 c1 = f1.visual_solidness;
650 c2 = f2.visual_solidness;
654 // If same solidness, liquid takes precense
668 Gets nth node tile (0 <= n <= 5).
670 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
672 INodeDefManager *ndef = data->m_gamedef->ndef();
673 TileSpec spec = ndef->get(mn).tiles[tileindex];
674 // Apply temporary crack
675 if (p == data->m_crack_pos_relative)
676 spec.material_flags |= MATERIAL_FLAG_CRACK;
681 Gets node tile given a face direction.
683 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
685 INodeDefManager *ndef = data->m_gamedef->ndef();
687 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
688 // (0,0,1), (0,0,-1) or (0,0,0)
689 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
691 // Convert direction to single integer for table lookup
696 // 4 = invalid, treat as (0,0,0)
700 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
702 // Get rotation for things like chests
703 u8 facedir = mn.getFaceDir(ndef);
705 static const u16 dir_to_tile[24 * 16] =
707 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
708 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
709 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
710 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
711 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
713 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
714 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
715 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
716 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
718 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
719 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
720 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
721 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
723 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
724 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
725 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
726 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
728 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
729 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
730 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
731 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
733 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
734 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
735 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
736 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
739 u16 tile_index=facedir*16 + dir_i;
740 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
741 spec.rotation=dir_to_tile[tile_index + 1];
742 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
746 static void getTileInfo(
750 const v3s16 &face_dir,
754 v3s16 &face_dir_corrected,
760 VoxelManipulator &vmanip = data->m_vmanip;
761 INodeDefManager *ndef = data->m_gamedef->ndef();
762 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
764 MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
766 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
767 if (n0.getContent() == CONTENT_IGNORE) {
772 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
774 if (n1.getContent() == CONTENT_IGNORE) {
780 bool equivalent = false;
781 u8 mf = face_contents(n0.getContent(), n1.getContent(),
794 tile = getNodeTile(n0, p, face_dir, data);
796 face_dir_corrected = face_dir;
797 light_source = ndef->get(n0).light_source;
801 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
802 p_corrected = p + face_dir;
803 face_dir_corrected = -face_dir;
804 light_source = ndef->get(n1).light_source;
807 // eg. water and glass
809 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
811 if(data->m_smooth_lighting == false)
813 lights[0] = lights[1] = lights[2] = lights[3] =
814 getFaceLight(n0, n1, face_dir, ndef);
818 v3s16 vertex_dirs[4];
819 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
820 for(u16 i=0; i<4; i++)
822 lights[i] = getSmoothLight(
823 blockpos_nodes + p_corrected,
824 vertex_dirs[i], data);
833 translate_dir: unit vector with only one of x, y or z
834 face_dir: unit vector with only one of x, y or z
836 static void updateFastFaceRow(
843 std::vector<FastFace> &dest)
847 u16 continuous_tiles_count = 0;
849 bool makes_face = false;
851 v3s16 face_dir_corrected;
852 u16 lights[4] = {0,0,0,0};
855 getTileInfo(data, p, face_dir,
856 makes_face, p_corrected, face_dir_corrected,
857 lights, tile, light_source);
859 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
861 // If tiling can be done, this is set to false in the next step
862 bool next_is_different = true;
866 bool next_makes_face = false;
867 v3s16 next_p_corrected;
868 v3s16 next_face_dir_corrected;
869 u16 next_lights[4] = {0,0,0,0};
871 u8 next_light_source = 0;
873 // If at last position, there is nothing to compare to and
874 // the face must be drawn anyway
875 if(j != MAP_BLOCKSIZE - 1)
877 p_next = p + translate_dir;
879 getTileInfo(data, p_next, face_dir,
880 next_makes_face, next_p_corrected,
881 next_face_dir_corrected, next_lights,
882 next_tile, next_light_source);
884 if(next_makes_face == makes_face
885 && next_p_corrected == p_corrected + translate_dir
886 && next_face_dir_corrected == face_dir_corrected
887 && next_lights[0] == lights[0]
888 && next_lights[1] == lights[1]
889 && next_lights[2] == lights[2]
890 && next_lights[3] == lights[3]
892 && tile.rotation == 0
893 && next_light_source == light_source)
895 next_is_different = false;
899 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
900 next_makes_face != makes_face ? 1 : 0);
901 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
902 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
903 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
904 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
905 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
906 (next_lights[0] != lights[0] ||
907 next_lights[0] != lights[0] ||
908 next_lights[0] != lights[0] ||
909 next_lights[0] != lights[0]) ? 1 : 0);
910 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
911 !(next_tile == tile) ? 1 : 0);
914 /*g_profiler->add("Meshgen: Total faces checked", 1);
916 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
919 g_profiler->add("Meshgen: diff: last position", 1);*/
922 continuous_tiles_count++;
924 if(next_is_different)
927 Create a face if there should be one
931 // Floating point conversion of the position vector
932 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
933 // Center point of face (kind of)
934 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
935 if(continuous_tiles_count != 1)
936 sp += translate_dir_f;
939 if(translate_dir.X != 0) {
940 scale.X = continuous_tiles_count;
942 if(translate_dir.Y != 0) {
943 scale.Y = continuous_tiles_count;
945 if(translate_dir.Z != 0) {
946 scale.Z = continuous_tiles_count;
949 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
950 sp, face_dir_corrected, scale, light_source,
953 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
954 for(int i = 1; i < continuous_tiles_count; i++){
955 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
959 continuous_tiles_count = 0;
961 makes_face = next_makes_face;
962 p_corrected = next_p_corrected;
963 face_dir_corrected = next_face_dir_corrected;
964 lights[0] = next_lights[0];
965 lights[1] = next_lights[1];
966 lights[2] = next_lights[2];
967 lights[3] = next_lights[3];
969 light_source = next_light_source;
976 static void updateAllFastFaceRows(MeshMakeData *data,
977 std::vector<FastFace> &dest)
980 Go through every y,z and get top(y+) faces in rows of x+
982 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
983 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
984 updateFastFaceRow(data,
988 v3s16(0,1,0), //face dir
995 Go through every x,y and get right(x+) faces in rows of z+
997 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
998 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
999 updateFastFaceRow(data,
1003 v3s16(1,0,0), //face dir
1010 Go through every y,z and get back(z+) faces in rows of x+
1012 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1013 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1014 updateFastFaceRow(data,
1018 v3s16(0,0,1), //face dir
1029 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1030 m_mesh(new scene::SMesh()),
1031 m_gamedef(data->m_gamedef),
1032 m_animation_force_timer(0), // force initial animation
1034 m_crack_materials(),
1035 m_highlighted_materials(),
1036 m_last_daynight_ratio((u32) -1),
1039 m_enable_shaders = data->m_use_shaders;
1040 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1042 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1043 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1044 //TimeTaker timer1("MapBlockMesh()");
1046 std::vector<FastFace> fastfaces_new;
1047 fastfaces_new.reserve(512);
1050 We are including the faces of the trailing edges of the block.
1051 This means that when something changes, the caller must
1052 also update the meshes of the blocks at the leading edges.
1054 NOTE: This is the slowest part of this method.
1057 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1058 //TimeTaker timer2("updateAllFastFaceRows()");
1059 updateAllFastFaceRows(data, fastfaces_new);
1064 Convert FastFaces to MeshCollector
1067 MeshCollector collector;
1070 // avg 0ms (100ms spikes when loading textures the first time)
1071 // (NOTE: probably outdated)
1072 //TimeTaker timer2("MeshCollector building");
1074 for(u32 i=0; i<fastfaces_new.size(); i++)
1076 FastFace &f = fastfaces_new[i];
1078 const u16 indices[] = {0,1,2,2,3,0};
1079 const u16 indices_alternate[] = {0,1,3,2,3,1};
1081 if(f.tile.texture == NULL)
1084 const u16 *indices_p = indices;
1087 Revert triangles for nicer looking gradient if vertices
1088 1 and 3 have same color or 0 and 2 have different color.
1089 getRed() is the day color.
1091 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1092 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1093 indices_p = indices_alternate;
1095 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1100 Add special graphics:
1107 mapblock_mesh_generate_special(data, collector);
1109 m_highlight_mesh_color = data->m_highlight_mesh_color;
1112 Convert MeshCollector to SMesh
1114 ITextureSource *tsrc = m_gamedef->tsrc();
1115 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1117 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1119 PreMeshBuffer &p = collector.prebuffers[i];
1121 // Generate animation data
1123 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1125 // Find the texture name plus ^[crack:N:
1126 std::ostringstream os(std::ios::binary);
1127 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1128 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1129 os<<"o"; // use ^[cracko
1130 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1131 m_crack_materials.insert(std::make_pair(i, os.str()));
1132 // Replace tile texture with the cracked one
1133 p.tile.texture = tsrc->getTexture(
1135 &p.tile.texture_id);
1137 // - Texture animation
1138 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1140 // Add to MapBlockMesh in order to animate these tiles
1141 m_animation_tiles[i] = p.tile;
1142 m_animation_frames[i] = 0;
1143 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1144 // Get starting position from noise
1145 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1146 data->m_blockpos.X, data->m_blockpos.Y,
1147 data->m_blockpos.Z, 0));
1149 // Play all synchronized
1150 m_animation_frame_offsets[i] = 0;
1152 // Replace tile texture with the first animation frame
1153 FrameSpec animation_frame = p.tile.frames[0];
1154 p.tile.texture = animation_frame.texture;
1157 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1158 m_highlighted_materials.push_back(i);
1160 for(u32 j = 0; j < p.vertices.size(); j++)
1162 // Note applyFacesShading second parameter is precalculated sqrt
1163 // value for speed improvement
1164 // Skip it for lightsources and top faces.
1165 video::SColor &vc = p.vertices[j].Color;
1166 if (!vc.getBlue()) {
1167 if (p.vertices[j].Normal.Y < -0.5) {
1168 applyFacesShading (vc, 0.447213);
1169 } else if (p.vertices[j].Normal.X > 0.5) {
1170 applyFacesShading (vc, 0.670820);
1171 } else if (p.vertices[j].Normal.X < -0.5) {
1172 applyFacesShading (vc, 0.670820);
1173 } else if (p.vertices[j].Normal.Z > 0.5) {
1174 applyFacesShading (vc, 0.836660);
1175 } else if (p.vertices[j].Normal.Z < -0.5) {
1176 applyFacesShading (vc, 0.836660);
1179 if(!m_enable_shaders)
1181 // - Classic lighting (shaders handle this by themselves)
1182 // Set initial real color and store for later updates
1183 u8 day = vc.getRed();
1184 u8 night = vc.getGreen();
1185 finalColorBlend(vc, day, night, 1000);
1187 m_daynight_diffs[i][j] = std::make_pair(day, night);
1192 video::SMaterial material;
1193 material.setFlag(video::EMF_LIGHTING, false);
1194 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1195 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1196 material.setFlag(video::EMF_FOG_ENABLE, true);
1197 material.setTexture(0, p.tile.texture);
1199 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1200 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1202 if (m_enable_shaders) {
1203 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1204 p.tile.applyMaterialOptionsWithShaders(material);
1205 if (p.tile.normal_texture) {
1206 material.setTexture(1, p.tile.normal_texture);
1207 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1209 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1212 p.tile.applyMaterialOptions(material);
1216 // Create meshbuffer
1217 // This is a "Standard MeshBuffer",
1218 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1219 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1221 buf->Material = material;
1223 m_mesh->addMeshBuffer(buf);
1226 buf->append(&p.vertices[0], p.vertices.size(),
1227 &p.indices[0], p.indices.size());
1230 m_camera_offset = camera_offset;
1233 Do some stuff to the mesh
1236 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1241 // Usually 1-700 faces and 1-7 materials
1242 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1243 <<"and uses "<<m_mesh->getMeshBufferCount()
1244 <<" materials (meshbuffers)"<<std::endl;
1247 // Use VBO for mesh (this just would set this for ever buffer)
1248 // This will lead to infinite memory usage because or irrlicht.
1249 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1252 NOTE: If that is enabled, some kind of a queue to the main
1253 thread should be made which would call irrlicht to delete
1254 the hardware buffer and then delete the mesh
1258 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1260 // Check if animation is required for this mesh
1262 !m_crack_materials.empty() ||
1263 !m_daynight_diffs.empty() ||
1264 !m_animation_tiles.empty() ||
1265 !m_highlighted_materials.empty();
1268 MapBlockMesh::~MapBlockMesh()
1274 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1277 if(!m_has_animation)
1279 m_animation_force_timer = 100000;
1283 m_animation_force_timer = myrand_range(5, 100);
1286 if(crack != m_last_crack)
1288 for(std::map<u32, std::string>::iterator
1289 i = m_crack_materials.begin();
1290 i != m_crack_materials.end(); i++)
1292 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1293 std::string basename = i->second;
1295 // Create new texture name from original
1296 ITextureSource *tsrc = m_gamedef->getTextureSource();
1297 std::ostringstream os;
1298 os<<basename<<crack;
1299 u32 new_texture_id = 0;
1300 video::ITexture *new_texture =
1301 tsrc->getTexture(os.str(), &new_texture_id);
1302 buf->getMaterial().setTexture(0, new_texture);
1304 // If the current material is also animated,
1305 // update animation info
1306 std::map<u32, TileSpec>::iterator anim_iter =
1307 m_animation_tiles.find(i->first);
1308 if(anim_iter != m_animation_tiles.end()){
1309 TileSpec &tile = anim_iter->second;
1310 tile.texture = new_texture;
1311 tile.texture_id = new_texture_id;
1312 // force animation update
1313 m_animation_frames[i->first] = -1;
1317 m_last_crack = crack;
1320 // Texture animation
1321 for(std::map<u32, TileSpec>::iterator
1322 i = m_animation_tiles.begin();
1323 i != m_animation_tiles.end(); i++)
1325 const TileSpec &tile = i->second;
1326 // Figure out current frame
1327 int frameoffset = m_animation_frame_offsets[i->first];
1328 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1329 + frameoffset) % tile.animation_frame_count;
1330 // If frame doesn't change, skip
1331 if(frame == m_animation_frames[i->first])
1334 m_animation_frames[i->first] = frame;
1336 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1337 ITextureSource *tsrc = m_gamedef->getTextureSource();
1339 FrameSpec animation_frame = tile.frames[frame];
1340 buf->getMaterial().setTexture(0, animation_frame.texture);
1341 if (m_enable_shaders) {
1342 if (animation_frame.normal_texture) {
1343 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1344 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1346 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1351 // Day-night transition
1352 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1354 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1355 i = m_daynight_diffs.begin();
1356 i != m_daynight_diffs.end(); i++)
1358 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1359 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1360 for(std::map<u32, std::pair<u8, u8 > >::iterator
1361 j = i->second.begin();
1362 j != i->second.end(); j++)
1364 u32 vertexIndex = j->first;
1365 u8 day = j->second.first;
1366 u8 night = j->second.second;
1367 finalColorBlend(vertices[vertexIndex].Color,
1368 day, night, daynight_ratio);
1371 m_last_daynight_ratio = daynight_ratio;
1374 // Node highlighting
1375 if (m_enable_highlighting) {
1376 u8 day = m_highlight_mesh_color.getRed();
1377 u8 night = m_highlight_mesh_color.getGreen();
1379 finalColorBlend(hc, day, night, daynight_ratio);
1380 float sin_r = 0.07 * sin(1.5 * time);
1381 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1382 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1383 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1384 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1385 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1387 for(std::list<u32>::iterator
1388 i = m_highlighted_materials.begin();
1389 i != m_highlighted_materials.end(); i++)
1391 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1392 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1393 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1394 vertices[j].Color = hc;
1401 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1403 if (camera_offset != m_camera_offset) {
1404 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1405 m_camera_offset = camera_offset;
1413 void MeshCollector::append(const TileSpec &tile,
1414 const video::S3DVertex *vertices, u32 numVertices,
1415 const u16 *indices, u32 numIndices)
1417 if(numIndices > 65535)
1419 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1423 PreMeshBuffer *p = NULL;
1424 for(u32 i=0; i<prebuffers.size(); i++)
1426 PreMeshBuffer &pp = prebuffers[i];
1429 if(pp.indices.size() + numIndices > 65535)
1440 prebuffers.push_back(pp);
1441 p = &prebuffers[prebuffers.size()-1];
1444 u32 vertex_count = p->vertices.size();
1445 for(u32 i=0; i<numIndices; i++)
1447 u32 j = indices[i] + vertex_count;
1448 p->indices.push_back(j);
1450 for(u32 i=0; i<numVertices; i++)
1452 p->vertices.push_back(vertices[i]);
1457 MeshCollector - for meshnodes and converted drawtypes.
1460 void MeshCollector::append(const TileSpec &tile,
1461 const video::S3DVertex *vertices, u32 numVertices,
1462 const u16 *indices, u32 numIndices,
1463 v3f pos, video::SColor c)
1465 if(numIndices > 65535)
1467 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1471 PreMeshBuffer *p = NULL;
1472 for(u32 i=0; i<prebuffers.size(); i++)
1474 PreMeshBuffer &pp = prebuffers[i];
1477 if(pp.indices.size() + numIndices > 65535)
1488 prebuffers.push_back(pp);
1489 p = &prebuffers[prebuffers.size()-1];
1492 u32 vertex_count = p->vertices.size();
1493 for(u32 i=0; i<numIndices; i++)
1495 u32 j = indices[i] + vertex_count;
1496 p->indices.push_back(j);
1498 for(u32 i=0; i<numVertices; i++)
1500 video::S3DVertex vert = vertices[i];
1503 p->vertices.push_back(vert);