3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "mapblock_mesh.h"
24 #include "main.h" // for g_profiler
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
35 static void applyFacesShading(video::SColor& color, float factor)
37 color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
38 color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
45 MeshMakeData::MeshMakeData(IGameDef *gamedef):
47 m_blockpos(-1337,-1337,-1337),
48 m_crack_pos_relative(-1337, -1337, -1337),
49 m_highlighted_pos_relative(-1337, -1337, -1337),
50 m_smooth_lighting(false),
52 m_highlight_mesh_color(255, 255, 255, 255),
56 void MeshMakeData::fill(MapBlock *block)
58 m_blockpos = block->getPos();
60 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
66 // Allocate this block + neighbors
68 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
69 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
70 m_vmanip.addArea(voxel_area);
73 //TimeTaker timer("copy central block data");
77 block->copyTo(m_vmanip);
80 //TimeTaker timer("copy neighbor block data");
84 Copy neighbors. This is lightning fast.
85 Copying only the borders would be *very* slow.
89 Map *map = block->getParent();
91 for(u16 i=0; i<26; i++)
93 const v3s16 &dir = g_26dirs[i];
94 v3s16 bp = m_blockpos + dir;
95 MapBlock *b = map->getBlockNoCreateNoEx(bp);
102 void MeshMakeData::fillSingleNode(MapNode *node)
104 m_blockpos = v3s16(0,0,0);
106 v3s16 blockpos_nodes = v3s16(0,0,0);
107 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
108 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
109 s32 volume = area.getVolume();
110 s32 our_node_index = area.index(1,1,1);
112 // Allocate this block + neighbors
114 m_vmanip.addArea(area);
117 MapNode *data = new MapNode[volume];
118 for(s32 i = 0; i < volume; i++)
120 if(i == our_node_index)
126 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
129 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
133 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
136 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
139 void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
141 m_show_hud = show_hud;
142 m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
145 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
147 m_smooth_lighting = smooth_lighting;
151 Light and vertex color functions
155 Calculate non-smooth lighting at interior of node.
158 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
159 INodeDefManager *ndef)
161 u8 light = n.getLight(bank, ndef);
165 light = undiminish_light(light);
170 light = diminish_light(light);
174 return decode_light(light);
178 Calculate non-smooth lighting at interior of node.
181 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
183 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
184 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
185 return day | (night << 8);
189 Calculate non-smooth lighting at face of node.
192 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
193 v3s16 face_dir, INodeDefManager *ndef)
196 u8 l1 = n.getLight(bank, ndef);
197 u8 l2 = n2.getLight(bank, ndef);
203 // Boost light level for light sources
204 u8 light_source = MYMAX(ndef->get(n).light_source,
205 ndef->get(n2).light_source);
206 if(light_source > light)
207 light = light_source;
209 return decode_light(light);
213 Calculate non-smooth lighting at face of node.
216 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
218 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
219 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
220 return day | (night << 8);
224 Calculate smooth lighting at the XYZ- corner of p.
227 static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
229 static const v3s16 dirs8[8] = {
240 INodeDefManager *ndef = data->m_gamedef->ndef();
242 u16 ambient_occlusion = 0;
244 u8 light_source_max = 0;
248 for(u32 i = 0; i < 8; i++)
250 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
252 // if it's CONTENT_IGNORE we can't do any light calculations
253 if (n.getContent() == CONTENT_IGNORE) {
257 const ContentFeatures &f = ndef->get(n);
258 if(f.light_source > light_source_max)
259 light_source_max = f.light_source;
260 // Check f.solidness because fast-style leaves look
262 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
263 light_day += decode_light(n.getLight(LIGHTBANK_DAY, ndef));
264 light_night += decode_light(n.getLight(LIGHTBANK_NIGHT, ndef));
274 light_day /= light_count;
275 light_night /= light_count;
277 // Boost brightness around light sources
278 bool skip_ambient_occlusion = false;
279 if(decode_light(light_source_max) >= light_day) {
280 light_day = decode_light(light_source_max);
281 skip_ambient_occlusion = true;
283 if(decode_light(light_source_max) >= light_night) {
284 light_night = decode_light(light_source_max);
285 skip_ambient_occlusion = true;
288 if(ambient_occlusion > 4 && !skip_ambient_occlusion)
290 //calculate table index for gamma space multiplier
291 ambient_occlusion -= 5;
292 //table of precalculated gamma space multiply factors
293 //light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2)
294 static const float light_amount[4] = {0.877424315, 0.729740053, 0.532520545, 0.0};
295 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
296 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
299 return light_day | (light_night << 8);
303 Calculate smooth lighting at the given corner of p.
306 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
308 if(corner.X == 1) p.X += 1;
309 // else corner.X == -1
310 if(corner.Y == 1) p.Y += 1;
311 // else corner.Y == -1
312 if(corner.Z == 1) p.Z += 1;
313 // else corner.Z == -1
315 return getSmoothLightCombined(p, data);
319 Converts from day + night color values (0..255)
320 and a given daynight_ratio to the final SColor shown on screen.
322 void finalColorBlend(video::SColor& result,
323 u8 day, u8 night, u32 daynight_ratio)
325 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
329 b += (day - night) / 13;
330 rg -= (day - night) / 23;
332 // Emphase blue a bit in darker places
333 // Each entry of this array represents a range of 8 blue levels
334 static const u8 emphase_blue_when_dark[32] = {
335 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
336 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
338 b += emphase_blue_when_dark[b / 8];
339 b = irr::core::clamp (b, 0, 255);
341 // Artificial light is yellow-ish
342 static const u8 emphase_yellow_when_artificial[16] = {
343 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
345 rg += emphase_yellow_when_artificial[night/16];
346 rg = irr::core::clamp (rg, 0, 255);
354 Mesh generation helpers
358 vertex_dirs: v3s16[4]
360 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
363 If looked from outside the node towards the face, the corners are:
369 if(dir == v3s16(0,0,1))
371 // If looking towards z+, this is the face that is behind
372 // the center point, facing towards z+.
373 vertex_dirs[0] = v3s16(-1,-1, 1);
374 vertex_dirs[1] = v3s16( 1,-1, 1);
375 vertex_dirs[2] = v3s16( 1, 1, 1);
376 vertex_dirs[3] = v3s16(-1, 1, 1);
378 else if(dir == v3s16(0,0,-1))
381 vertex_dirs[0] = v3s16( 1,-1,-1);
382 vertex_dirs[1] = v3s16(-1,-1,-1);
383 vertex_dirs[2] = v3s16(-1, 1,-1);
384 vertex_dirs[3] = v3s16( 1, 1,-1);
386 else if(dir == v3s16(1,0,0))
389 vertex_dirs[0] = v3s16( 1,-1, 1);
390 vertex_dirs[1] = v3s16( 1,-1,-1);
391 vertex_dirs[2] = v3s16( 1, 1,-1);
392 vertex_dirs[3] = v3s16( 1, 1, 1);
394 else if(dir == v3s16(-1,0,0))
397 vertex_dirs[0] = v3s16(-1,-1,-1);
398 vertex_dirs[1] = v3s16(-1,-1, 1);
399 vertex_dirs[2] = v3s16(-1, 1, 1);
400 vertex_dirs[3] = v3s16(-1, 1,-1);
402 else if(dir == v3s16(0,1,0))
404 // faces towards Y+ (assume Z- as "down" in texture)
405 vertex_dirs[0] = v3s16( 1, 1,-1);
406 vertex_dirs[1] = v3s16(-1, 1,-1);
407 vertex_dirs[2] = v3s16(-1, 1, 1);
408 vertex_dirs[3] = v3s16( 1, 1, 1);
410 else if(dir == v3s16(0,-1,0))
412 // faces towards Y- (assume Z+ as "down" in texture)
413 vertex_dirs[0] = v3s16( 1,-1, 1);
414 vertex_dirs[1] = v3s16(-1,-1, 1);
415 vertex_dirs[2] = v3s16(-1,-1,-1);
416 vertex_dirs[3] = v3s16( 1,-1,-1);
423 video::S3DVertex vertices[4]; // Precalculated vertices
426 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
427 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
431 // Position is at the center of the cube.
440 v3s16 vertex_dirs[4];
441 getNodeVertexDirs(dir, vertex_dirs);
445 switch (tile.rotation)
451 vertex_dirs[0] = vertex_dirs[3];
452 vertex_dirs[3] = vertex_dirs[2];
453 vertex_dirs[2] = vertex_dirs[1];
463 vertex_dirs[0] = vertex_dirs[2];
466 vertex_dirs[1] = vertex_dirs[3];
477 vertex_dirs[0] = vertex_dirs[1];
478 vertex_dirs[1] = vertex_dirs[2];
479 vertex_dirs[2] = vertex_dirs[3];
489 vertex_dirs[0] = vertex_dirs[3];
490 vertex_dirs[3] = vertex_dirs[2];
491 vertex_dirs[2] = vertex_dirs[1];
503 vertex_dirs[0] = vertex_dirs[1];
504 vertex_dirs[1] = vertex_dirs[2];
505 vertex_dirs[2] = vertex_dirs[3];
517 vertex_dirs[0] = vertex_dirs[3];
518 vertex_dirs[3] = vertex_dirs[2];
519 vertex_dirs[2] = vertex_dirs[1];
531 vertex_dirs[0] = vertex_dirs[1];
532 vertex_dirs[1] = vertex_dirs[2];
533 vertex_dirs[2] = vertex_dirs[3];
555 for(u16 i=0; i<4; i++)
558 BS/2*vertex_dirs[i].X,
559 BS/2*vertex_dirs[i].Y,
560 BS/2*vertex_dirs[i].Z
564 for(u16 i=0; i<4; i++)
566 vertex_pos[i].X *= scale.X;
567 vertex_pos[i].Y *= scale.Y;
568 vertex_pos[i].Z *= scale.Z;
569 vertex_pos[i] += pos;
573 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
574 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
575 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
577 v3f normal(dir.X, dir.Y, dir.Z);
579 u8 alpha = tile.alpha;
581 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
582 MapBlock_LightColor(alpha, li0, light_source),
583 core::vector2d<f32>(x0+w*abs_scale, y0+h));
584 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
585 MapBlock_LightColor(alpha, li1, light_source),
586 core::vector2d<f32>(x0, y0+h));
587 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
588 MapBlock_LightColor(alpha, li2, light_source),
589 core::vector2d<f32>(x0, y0));
590 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
591 MapBlock_LightColor(alpha, li3, light_source),
592 core::vector2d<f32>(x0+w*abs_scale, y0));
595 dest.push_back(face);
599 Nodes make a face if contents differ and solidness differs.
602 1: Face uses m1's content
603 2: Face uses m2's content
604 equivalent: Whether the blocks share the same face (eg. water and glass)
606 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
608 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
609 INodeDefManager *ndef)
613 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
616 bool contents_differ = (m1 != m2);
618 const ContentFeatures &f1 = ndef->get(m1);
619 const ContentFeatures &f2 = ndef->get(m2);
621 // Contents don't differ for different forms of same liquid
622 if(f1.sameLiquid(f2))
623 contents_differ = false;
625 u8 c1 = f1.solidness;
626 u8 c2 = f2.solidness;
628 bool solidness_differs = (c1 != c2);
629 bool makes_face = contents_differ && solidness_differs;
631 if(makes_face == false)
635 c1 = f1.visual_solidness;
637 c2 = f2.visual_solidness;
641 // If same solidness, liquid takes precense
655 Gets nth node tile (0 <= n <= 5).
657 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
659 INodeDefManager *ndef = data->m_gamedef->ndef();
660 TileSpec spec = ndef->get(mn).tiles[tileindex];
661 // Apply temporary crack
662 if (p == data->m_crack_pos_relative)
663 spec.material_flags |= MATERIAL_FLAG_CRACK;
668 Gets node tile given a face direction.
670 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
672 INodeDefManager *ndef = data->m_gamedef->ndef();
674 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
675 // (0,0,1), (0,0,-1) or (0,0,0)
676 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
678 // Convert direction to single integer for table lookup
683 // 4 = invalid, treat as (0,0,0)
687 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
689 // Get rotation for things like chests
690 u8 facedir = mn.getFaceDir(ndef);
693 static const u16 dir_to_tile[24 * 16] =
695 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
696 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
697 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
698 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
699 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
701 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
702 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
703 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
704 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
706 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
707 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
708 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
709 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
711 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
712 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
713 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
714 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
716 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
717 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
718 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
719 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
721 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
722 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
723 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
724 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
727 u16 tile_index=facedir*16 + dir_i;
728 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
729 spec.rotation=dir_to_tile[tile_index + 1];
730 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
734 static void getTileInfo(
742 v3s16 &face_dir_corrected,
748 VoxelManipulator &vmanip = data->m_vmanip;
749 INodeDefManager *ndef = data->m_gamedef->ndef();
750 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
752 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
754 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
755 if (n0.getContent() == CONTENT_IGNORE ) {
759 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
762 bool equivalent = false;
763 u8 mf = face_contents(n0.getContent(), n1.getContent(),
776 tile = getNodeTile(n0, p, face_dir, data);
778 face_dir_corrected = face_dir;
779 light_source = ndef->get(n0).light_source;
783 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
784 p_corrected = p + face_dir;
785 face_dir_corrected = -face_dir;
786 light_source = ndef->get(n1).light_source;
789 // eg. water and glass
791 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
793 if(data->m_smooth_lighting == false)
795 lights[0] = lights[1] = lights[2] = lights[3] =
796 getFaceLight(n0, n1, face_dir, ndef);
800 v3s16 vertex_dirs[4];
801 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
802 for(u16 i=0; i<4; i++)
804 lights[i] = getSmoothLight(
805 blockpos_nodes + p_corrected,
806 vertex_dirs[i], data);
815 translate_dir: unit vector with only one of x, y or z
816 face_dir: unit vector with only one of x, y or z
818 static void updateFastFaceRow(
825 std::vector<FastFace> &dest)
829 u16 continuous_tiles_count = 0;
831 bool makes_face = false;
833 v3s16 face_dir_corrected;
834 u16 lights[4] = {0,0,0,0};
837 getTileInfo(data, p, face_dir,
838 makes_face, p_corrected, face_dir_corrected,
839 lights, tile, light_source);
841 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
843 // If tiling can be done, this is set to false in the next step
844 bool next_is_different = true;
848 bool next_makes_face = false;
849 v3s16 next_p_corrected;
850 v3s16 next_face_dir_corrected;
851 u16 next_lights[4] = {0,0,0,0};
853 u8 next_light_source = 0;
855 // If at last position, there is nothing to compare to and
856 // the face must be drawn anyway
857 if(j != MAP_BLOCKSIZE - 1)
859 p_next = p + translate_dir;
861 getTileInfo(data, p_next, face_dir,
862 next_makes_face, next_p_corrected,
863 next_face_dir_corrected, next_lights,
864 next_tile, next_light_source);
866 if(next_makes_face == makes_face
867 && next_p_corrected == p_corrected + translate_dir
868 && next_face_dir_corrected == face_dir_corrected
869 && next_lights[0] == lights[0]
870 && next_lights[1] == lights[1]
871 && next_lights[2] == lights[2]
872 && next_lights[3] == lights[3]
874 && tile.rotation == 0
875 && next_light_source == light_source)
877 next_is_different = false;
881 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
882 next_makes_face != makes_face ? 1 : 0);
883 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
884 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
885 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
886 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
887 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
888 (next_lights[0] != lights[0] ||
889 next_lights[0] != lights[0] ||
890 next_lights[0] != lights[0] ||
891 next_lights[0] != lights[0]) ? 1 : 0);
892 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
893 !(next_tile == tile) ? 1 : 0);
896 /*g_profiler->add("Meshgen: Total faces checked", 1);
898 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
901 g_profiler->add("Meshgen: diff: last position", 1);*/
904 continuous_tiles_count++;
906 if(next_is_different)
909 Create a face if there should be one
913 // Floating point conversion of the position vector
914 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
915 // Center point of face (kind of)
916 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
917 if(continuous_tiles_count != 1)
918 sp += translate_dir_f;
921 if(translate_dir.X != 0) {
922 scale.X = continuous_tiles_count;
924 if(translate_dir.Y != 0) {
925 scale.Y = continuous_tiles_count;
927 if(translate_dir.Z != 0) {
928 scale.Z = continuous_tiles_count;
931 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
932 sp, face_dir_corrected, scale, light_source,
935 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
936 for(int i = 1; i < continuous_tiles_count; i++){
937 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
941 continuous_tiles_count = 0;
943 makes_face = next_makes_face;
944 p_corrected = next_p_corrected;
945 face_dir_corrected = next_face_dir_corrected;
946 lights[0] = next_lights[0];
947 lights[1] = next_lights[1];
948 lights[2] = next_lights[2];
949 lights[3] = next_lights[3];
951 light_source = next_light_source;
958 static void updateAllFastFaceRows(MeshMakeData *data,
959 std::vector<FastFace> &dest)
962 Go through every y,z and get top(y+) faces in rows of x+
964 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
965 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
966 updateFastFaceRow(data,
970 v3s16(0,1,0), //face dir
977 Go through every x,y and get right(x+) faces in rows of z+
979 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
980 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
981 updateFastFaceRow(data,
985 v3s16(1,0,0), //face dir
992 Go through every y,z and get back(z+) faces in rows of x+
994 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
995 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
996 updateFastFaceRow(data,
1000 v3s16(0,0,1), //face dir
1011 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1012 m_mesh(new scene::SMesh()),
1013 m_gamedef(data->m_gamedef),
1014 m_animation_force_timer(0), // force initial animation
1016 m_crack_materials(),
1017 m_highlighted_materials(),
1018 m_last_daynight_ratio((u32) -1),
1021 m_enable_shaders = g_settings->getBool("enable_shaders");
1022 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1024 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1025 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1026 //TimeTaker timer1("MapBlockMesh()");
1028 std::vector<FastFace> fastfaces_new;
1031 We are including the faces of the trailing edges of the block.
1032 This means that when something changes, the caller must
1033 also update the meshes of the blocks at the leading edges.
1035 NOTE: This is the slowest part of this method.
1038 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1039 //TimeTaker timer2("updateAllFastFaceRows()");
1040 updateAllFastFaceRows(data, fastfaces_new);
1045 Convert FastFaces to MeshCollector
1048 MeshCollector collector;
1051 // avg 0ms (100ms spikes when loading textures the first time)
1052 // (NOTE: probably outdated)
1053 //TimeTaker timer2("MeshCollector building");
1055 for(u32 i=0; i<fastfaces_new.size(); i++)
1057 FastFace &f = fastfaces_new[i];
1059 const u16 indices[] = {0,1,2,2,3,0};
1060 const u16 indices_alternate[] = {0,1,3,2,3,1};
1062 if(f.tile.texture == NULL)
1065 const u16 *indices_p = indices;
1068 Revert triangles for nicer looking gradient if vertices
1069 1 and 3 have same color or 0 and 2 have different color.
1070 getRed() is the day color.
1072 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1073 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1074 indices_p = indices_alternate;
1076 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1081 Add special graphics:
1088 mapblock_mesh_generate_special(data, collector);
1090 m_highlight_mesh_color = data->m_highlight_mesh_color;
1093 Convert MeshCollector to SMesh
1095 ITextureSource *tsrc = m_gamedef->tsrc();
1096 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1098 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1100 PreMeshBuffer &p = collector.prebuffers[i];
1102 // Generate animation data
1104 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1106 // Find the texture name plus ^[crack:N:
1107 std::ostringstream os(std::ios::binary);
1108 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1109 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1110 os<<"o"; // use ^[cracko
1111 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1112 m_crack_materials.insert(std::make_pair(i, os.str()));
1113 // Replace tile texture with the cracked one
1114 p.tile.texture = tsrc->getTexture(
1116 &p.tile.texture_id);
1118 // - Texture animation
1119 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1121 // Add to MapBlockMesh in order to animate these tiles
1122 m_animation_tiles[i] = p.tile;
1123 m_animation_frames[i] = 0;
1124 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1125 // Get starting position from noise
1126 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1127 data->m_blockpos.X, data->m_blockpos.Y,
1128 data->m_blockpos.Z, 0));
1130 // Play all synchronized
1131 m_animation_frame_offsets[i] = 0;
1133 // Replace tile texture with the first animation frame
1134 FrameSpec animation_frame = p.tile.frames.find(0)->second;
1135 p.tile.texture = animation_frame.texture;
1138 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1139 m_highlighted_materials.push_back(i);
1141 for(u32 j = 0; j < p.vertices.size(); j++)
1143 // Note applyFacesShading second parameter is precalculated sqrt
1144 // value for speed improvement
1145 // Skip it for lightsources and top faces.
1146 video::SColor &vc = p.vertices[j].Color;
1147 if (!vc.getBlue()) {
1148 if (p.vertices[j].Normal.Y < -0.5) {
1149 applyFacesShading (vc, 0.447213);
1150 } else if (p.vertices[j].Normal.X > 0.5) {
1151 applyFacesShading (vc, 0.670820);
1152 } else if (p.vertices[j].Normal.X < -0.5) {
1153 applyFacesShading (vc, 0.670820);
1154 } else if (p.vertices[j].Normal.Z > 0.5) {
1155 applyFacesShading (vc, 0.836660);
1156 } else if (p.vertices[j].Normal.Z < -0.5) {
1157 applyFacesShading (vc, 0.836660);
1160 // - Classic lighting
1161 // Set initial real color and store for later updates
1162 u8 day = vc.getRed();
1163 u8 night = vc.getGreen();
1164 finalColorBlend(vc, day, night, 1000);
1165 m_daynight_diffs[i][j] = std::make_pair(day, night);
1169 video::SMaterial material;
1170 material.setFlag(video::EMF_LIGHTING, false);
1171 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1172 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1173 material.setFlag(video::EMF_FOG_ENABLE, true);
1174 material.setTexture(0, p.tile.texture);
1176 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1177 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1179 if (m_enable_shaders) {
1180 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1181 p.tile.applyMaterialOptionsWithShaders(material);
1182 if (p.tile.normal_texture) {
1183 material.setTexture(1, p.tile.normal_texture);
1184 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1186 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1189 p.tile.applyMaterialOptions(material);
1193 // Create meshbuffer
1194 // This is a "Standard MeshBuffer",
1195 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1196 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1198 buf->Material = material;
1200 m_mesh->addMeshBuffer(buf);
1203 buf->append(&p.vertices[0], p.vertices.size(),
1204 &p.indices[0], p.indices.size());
1207 m_camera_offset = camera_offset;
1210 Do some stuff to the mesh
1213 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1218 // Usually 1-700 faces and 1-7 materials
1219 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1220 <<"and uses "<<m_mesh->getMeshBufferCount()
1221 <<" materials (meshbuffers)"<<std::endl;
1224 // Use VBO for mesh (this just would set this for ever buffer)
1225 // This will lead to infinite memory usage because or irrlicht.
1226 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1229 NOTE: If that is enabled, some kind of a queue to the main
1230 thread should be made which would call irrlicht to delete
1231 the hardware buffer and then delete the mesh
1235 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1237 // Check if animation is required for this mesh
1239 !m_crack_materials.empty() ||
1240 !m_daynight_diffs.empty() ||
1241 !m_animation_tiles.empty() ||
1242 !m_highlighted_materials.empty();
1245 MapBlockMesh::~MapBlockMesh()
1251 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1254 if(!m_has_animation)
1256 m_animation_force_timer = 100000;
1260 m_animation_force_timer = myrand_range(5, 100);
1263 if(crack != m_last_crack)
1265 for(std::map<u32, std::string>::iterator
1266 i = m_crack_materials.begin();
1267 i != m_crack_materials.end(); i++)
1269 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1270 std::string basename = i->second;
1272 // Create new texture name from original
1273 ITextureSource *tsrc = m_gamedef->getTextureSource();
1274 std::ostringstream os;
1275 os<<basename<<crack;
1276 u32 new_texture_id = 0;
1277 video::ITexture *new_texture =
1278 tsrc->getTexture(os.str(), &new_texture_id);
1279 buf->getMaterial().setTexture(0, new_texture);
1281 // If the current material is also animated,
1282 // update animation info
1283 std::map<u32, TileSpec>::iterator anim_iter =
1284 m_animation_tiles.find(i->first);
1285 if(anim_iter != m_animation_tiles.end()){
1286 TileSpec &tile = anim_iter->second;
1287 tile.texture = new_texture;
1288 tile.texture_id = new_texture_id;
1289 // force animation update
1290 m_animation_frames[i->first] = -1;
1294 m_last_crack = crack;
1297 // Texture animation
1298 for(std::map<u32, TileSpec>::iterator
1299 i = m_animation_tiles.begin();
1300 i != m_animation_tiles.end(); i++)
1302 const TileSpec &tile = i->second;
1303 // Figure out current frame
1304 int frameoffset = m_animation_frame_offsets[i->first];
1305 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1306 + frameoffset) % tile.animation_frame_count;
1307 // If frame doesn't change, skip
1308 if(frame == m_animation_frames[i->first])
1311 m_animation_frames[i->first] = frame;
1313 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1314 ITextureSource *tsrc = m_gamedef->getTextureSource();
1316 FrameSpec animation_frame = tile.frames.find(frame)->second;
1317 buf->getMaterial().setTexture(0, animation_frame.texture);
1318 if (m_enable_shaders) {
1319 if (animation_frame.normal_texture) {
1320 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1321 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1323 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1328 // Day-night transition
1329 if(daynight_ratio != m_last_daynight_ratio)
1331 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1332 i = m_daynight_diffs.begin();
1333 i != m_daynight_diffs.end(); i++)
1335 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1336 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1337 for(std::map<u32, std::pair<u8, u8 > >::iterator
1338 j = i->second.begin();
1339 j != i->second.end(); j++)
1341 u32 vertexIndex = j->first;
1342 u8 day = j->second.first;
1343 u8 night = j->second.second;
1344 finalColorBlend(vertices[vertexIndex].Color,
1345 day, night, daynight_ratio);
1348 m_last_daynight_ratio = daynight_ratio;
1351 // Node highlighting
1352 if (m_enable_highlighting) {
1353 u8 day = m_highlight_mesh_color.getRed();
1354 u8 night = m_highlight_mesh_color.getGreen();
1356 finalColorBlend(hc, day, night, daynight_ratio);
1357 float sin_r = 0.07 * sin(1.5 * time);
1358 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1359 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1360 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1361 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1362 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1364 for(std::list<u32>::iterator
1365 i = m_highlighted_materials.begin();
1366 i != m_highlighted_materials.end(); i++)
1368 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1369 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1370 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1371 vertices[j].Color = hc;
1378 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1380 if (camera_offset != m_camera_offset) {
1381 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1382 m_camera_offset = camera_offset;
1390 void MeshCollector::append(const TileSpec &tile,
1391 const video::S3DVertex *vertices, u32 numVertices,
1392 const u16 *indices, u32 numIndices)
1394 if(numIndices > 65535)
1396 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1400 PreMeshBuffer *p = NULL;
1401 for(u32 i=0; i<prebuffers.size(); i++)
1403 PreMeshBuffer &pp = prebuffers[i];
1406 if(pp.indices.size() + numIndices > 65535)
1417 prebuffers.push_back(pp);
1418 p = &prebuffers[prebuffers.size()-1];
1421 u32 vertex_count = p->vertices.size();
1422 for(u32 i=0; i<numIndices; i++)
1424 u32 j = indices[i] + vertex_count;
1425 p->indices.push_back(j);
1427 for(u32 i=0; i<numVertices; i++)
1429 p->vertices.push_back(vertices[i]);
1434 MeshCollector - for meshnodes and converted drawtypes.
1437 void MeshCollector::append(const TileSpec &tile,
1438 const video::S3DVertex *vertices, u32 numVertices,
1439 const u16 *indices, u32 numIndices,
1440 v3f pos, video::SColor c)
1442 if(numIndices > 65535)
1444 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1448 PreMeshBuffer *p = NULL;
1449 for(u32 i=0; i<prebuffers.size(); i++)
1451 PreMeshBuffer &pp = prebuffers[i];
1454 if(pp.indices.size() + numIndices > 65535)
1465 prebuffers.push_back(pp);
1466 p = &prebuffers[prebuffers.size()-1];
1469 u32 vertex_count = p->vertices.size();
1470 for(u32 i=0; i<numIndices; i++)
1472 u32 j = indices[i] + vertex_count;
1473 p->indices.push_back(j);
1475 for(u32 i=0; i<numVertices; i++)
1477 video::S3DVertex vert = vertices[i];
1480 p->vertices.push_back(vert);