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"
28 #include "content_mapblock.h"
32 #include "util/directiontables.h"
33 #include <IMeshManipulator.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_tsrc(m_gamedef->getTextureSource()),
1033 m_shdrsrc(m_gamedef->getShaderSource()),
1034 m_animation_force_timer(0), // force initial animation
1036 m_crack_materials(),
1037 m_highlighted_materials(),
1038 m_last_daynight_ratio((u32) -1),
1041 m_enable_shaders = data->m_use_shaders;
1042 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1044 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1045 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1046 //TimeTaker timer1("MapBlockMesh()");
1048 std::vector<FastFace> fastfaces_new;
1049 fastfaces_new.reserve(512);
1052 We are including the faces of the trailing edges of the block.
1053 This means that when something changes, the caller must
1054 also update the meshes of the blocks at the leading edges.
1056 NOTE: This is the slowest part of this method.
1059 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1060 //TimeTaker timer2("updateAllFastFaceRows()");
1061 updateAllFastFaceRows(data, fastfaces_new);
1066 Convert FastFaces to MeshCollector
1069 MeshCollector collector;
1072 // avg 0ms (100ms spikes when loading textures the first time)
1073 // (NOTE: probably outdated)
1074 //TimeTaker timer2("MeshCollector building");
1076 for(u32 i=0; i<fastfaces_new.size(); i++)
1078 FastFace &f = fastfaces_new[i];
1080 const u16 indices[] = {0,1,2,2,3,0};
1081 const u16 indices_alternate[] = {0,1,3,2,3,1};
1083 if(f.tile.texture == NULL)
1086 const u16 *indices_p = indices;
1089 Revert triangles for nicer looking gradient if vertices
1090 1 and 3 have same color or 0 and 2 have different color.
1091 getRed() is the day color.
1093 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1094 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1095 indices_p = indices_alternate;
1097 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1102 Add special graphics:
1109 mapblock_mesh_generate_special(data, collector);
1111 m_highlight_mesh_color = data->m_highlight_mesh_color;
1114 Convert MeshCollector to SMesh
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<<m_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 = m_tsrc->getTextureForMesh(
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 video::S3DVertexTangents *vertex = &p.vertices[j];
1163 // Note applyFacesShading second parameter is precalculated sqrt
1164 // value for speed improvement
1165 // Skip it for lightsources and top faces.
1166 video::SColor &vc = vertex->Color;
1167 if (!vc.getBlue()) {
1168 if (vertex->Normal.Y < -0.5) {
1169 applyFacesShading (vc, 0.447213);
1170 } else if (vertex->Normal.X > 0.5) {
1171 applyFacesShading (vc, 0.670820);
1172 } else if (vertex->Normal.X < -0.5) {
1173 applyFacesShading (vc, 0.670820);
1174 } else if (vertex->Normal.Z > 0.5) {
1175 applyFacesShading (vc, 0.836660);
1176 } else if (vertex->Normal.Z < -0.5) {
1177 applyFacesShading (vc, 0.836660);
1180 if(!m_enable_shaders)
1182 // - Classic lighting (shaders handle this by themselves)
1183 // Set initial real color and store for later updates
1184 u8 day = vc.getRed();
1185 u8 night = vc.getGreen();
1186 finalColorBlend(vc, day, night, 1000);
1188 m_daynight_diffs[i][j] = std::make_pair(day, night);
1193 video::SMaterial material;
1194 material.setFlag(video::EMF_LIGHTING, false);
1195 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1196 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1197 material.setFlag(video::EMF_FOG_ENABLE, true);
1198 material.setTexture(0, p.tile.texture);
1200 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1201 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1203 if (m_enable_shaders) {
1204 material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
1205 p.tile.applyMaterialOptionsWithShaders(material);
1206 if (p.tile.normal_texture) {
1207 material.setTexture(1, p.tile.normal_texture);
1208 material.setTexture(2, m_tsrc->getTextureForMesh("enable_img.png"));
1210 material.setTexture(2, m_tsrc->getTextureForMesh("disable_img.png"));
1213 p.tile.applyMaterialOptions(material);
1217 // Create meshbuffer
1218 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1220 buf->Material = material;
1222 m_mesh->addMeshBuffer(buf);
1225 buf->append(&p.vertices[0], p.vertices.size(),
1226 &p.indices[0], p.indices.size());
1228 m_camera_offset = camera_offset;
1231 Do some stuff to the mesh
1234 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1236 if (m_enable_shaders) {
1237 scene::IMeshManipulator* meshmanip = m_gamedef->getSceneManager()->getMeshManipulator();
1238 meshmanip->recalculateTangents(m_mesh, true, false, false);
1244 // Usually 1-700 faces and 1-7 materials
1245 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1246 <<"and uses "<<m_mesh->getMeshBufferCount()
1247 <<" materials (meshbuffers)"<<std::endl;
1250 // Use VBO for mesh (this just would set this for ever buffer)
1251 // This will lead to infinite memory usage because or irrlicht.
1252 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1255 NOTE: If that is enabled, some kind of a queue to the main
1256 thread should be made which would call irrlicht to delete
1257 the hardware buffer and then delete the mesh
1261 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1263 // Check if animation is required for this mesh
1265 !m_crack_materials.empty() ||
1266 !m_daynight_diffs.empty() ||
1267 !m_animation_tiles.empty() ||
1268 !m_highlighted_materials.empty();
1271 MapBlockMesh::~MapBlockMesh()
1277 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1279 if(!m_has_animation)
1281 m_animation_force_timer = 100000;
1285 m_animation_force_timer = myrand_range(5, 100);
1288 if(crack != m_last_crack)
1290 for(std::map<u32, std::string>::iterator
1291 i = m_crack_materials.begin();
1292 i != m_crack_materials.end(); i++)
1294 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1295 std::string basename = i->second;
1297 // Create new texture name from original
1298 std::ostringstream os;
1299 os<<basename<<crack;
1300 u32 new_texture_id = 0;
1301 video::ITexture *new_texture =
1302 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1303 buf->getMaterial().setTexture(0, new_texture);
1305 // If the current material is also animated,
1306 // update animation info
1307 std::map<u32, TileSpec>::iterator anim_iter =
1308 m_animation_tiles.find(i->first);
1309 if(anim_iter != m_animation_tiles.end()){
1310 TileSpec &tile = anim_iter->second;
1311 tile.texture = new_texture;
1312 tile.texture_id = new_texture_id;
1313 // force animation update
1314 m_animation_frames[i->first] = -1;
1318 m_last_crack = crack;
1321 // Texture animation
1322 for(std::map<u32, TileSpec>::iterator
1323 i = m_animation_tiles.begin();
1324 i != m_animation_tiles.end(); i++)
1326 const TileSpec &tile = i->second;
1327 // Figure out current frame
1328 int frameoffset = m_animation_frame_offsets[i->first];
1329 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1330 + frameoffset) % tile.animation_frame_count;
1331 // If frame doesn't change, skip
1332 if(frame == m_animation_frames[i->first])
1335 m_animation_frames[i->first] = frame;
1337 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
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, m_tsrc->getTextureForMesh("enable_img.png"));
1346 buf->getMaterial().setTexture(2, m_tsrc->getTextureForMesh("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::S3DVertexTangents *vertices = (video::S3DVertexTangents *)buf->getVertices();
1360 for(std::map<u32, std::pair<u8, u8 > >::iterator
1361 j = i->second.begin();
1362 j != i->second.end(); j++)
1364 u8 day = j->second.first;
1365 u8 night = j->second.second;
1366 finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
1369 m_last_daynight_ratio = daynight_ratio;
1372 // Node highlighting
1373 if (m_enable_highlighting) {
1374 u8 day = m_highlight_mesh_color.getRed();
1375 u8 night = m_highlight_mesh_color.getGreen();
1377 finalColorBlend(hc, day, night, daynight_ratio);
1378 float sin_r = 0.07 * sin(1.5 * time);
1379 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1380 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1381 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1382 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1383 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1385 for(std::list<u32>::iterator
1386 i = m_highlighted_materials.begin();
1387 i != m_highlighted_materials.end(); i++)
1389 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1390 video::S3DVertexTangents *vertices = (video::S3DVertexTangents*)buf->getVertices();
1391 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1392 vertices[j].Color = hc;
1399 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1401 if (camera_offset != m_camera_offset) {
1402 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1403 m_camera_offset = camera_offset;
1411 void MeshCollector::append(const TileSpec &tile,
1412 const video::S3DVertex *vertices, u32 numVertices,
1413 const u16 *indices, u32 numIndices)
1415 if (numIndices > 65535) {
1416 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1420 PreMeshBuffer *p = NULL;
1421 for (u32 i = 0; i < prebuffers.size(); i++) {
1422 PreMeshBuffer &pp = prebuffers[i];
1423 if (pp.tile != tile)
1425 if (pp.indices.size() + numIndices > 65535)
1435 prebuffers.push_back(pp);
1436 p = &prebuffers[prebuffers.size() - 1];
1439 u32 vertex_count = p->vertices.size();
1440 for (u32 i = 0; i < numIndices; i++) {
1441 u32 j = indices[i] + vertex_count;
1442 p->indices.push_back(j);
1445 for (u32 i = 0; i < numVertices; i++) {
1446 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1447 vertices[i].Color, vertices[i].TCoords);
1448 p->vertices.push_back(vert);
1453 MeshCollector - for meshnodes and converted drawtypes.
1456 void MeshCollector::append(const TileSpec &tile,
1457 const video::S3DVertex *vertices, u32 numVertices,
1458 const u16 *indices, u32 numIndices,
1459 v3f pos, video::SColor c)
1461 if (numIndices > 65535) {
1462 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1466 PreMeshBuffer *p = NULL;
1467 for (u32 i = 0; i < prebuffers.size(); i++) {
1468 PreMeshBuffer &pp = prebuffers[i];
1471 if(pp.indices.size() + numIndices > 65535)
1481 prebuffers.push_back(pp);
1482 p = &prebuffers[prebuffers.size() - 1];
1485 u32 vertex_count = p->vertices.size();
1486 for (u32 i = 0; i < numIndices; i++) {
1487 u32 j = indices[i] + vertex_count;
1488 p->indices.push_back(j);
1491 for (u32 i = 0; i < numVertices; i++) {
1492 video::S3DVertexTangents vert(vertices[i].Pos + pos, vertices[i].Normal,
1493 c, vertices[i].TCoords);
1494 p->vertices.push_back(vert);