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 better this way
261 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
262 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
263 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
273 light_day /= light_count;
274 light_night /= light_count;
276 // Boost brightness around light sources
277 bool skip_ambient_occlusion_day = false;
278 if(decode_light(light_source_max) >= light_day) {
279 light_day = decode_light(light_source_max);
280 skip_ambient_occlusion_day = true;
283 bool skip_ambient_occlusion_night = false;
284 if(decode_light(light_source_max) >= light_night) {
285 light_night = decode_light(light_source_max);
286 skip_ambient_occlusion_night = true;
289 if (ambient_occlusion > 4)
291 //table of precalculated gamma space multiply factors
292 //light^2.2 * factor (0.75, 0.5, 0.25, 0.0), so table holds factor ^ (1 / 2.2)
293 static const float light_amount[4] = { 0.877424315, 0.729740053, 0.532520545, 0.0 };
295 //calculate table index for gamma space multiplier
296 ambient_occlusion -= 5;
298 if (!skip_ambient_occlusion_day)
299 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
300 if (!skip_ambient_occlusion_night)
301 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
304 return light_day | (light_night << 8);
308 Calculate smooth lighting at the given corner of p.
311 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
313 if(corner.X == 1) p.X += 1;
314 // else corner.X == -1
315 if(corner.Y == 1) p.Y += 1;
316 // else corner.Y == -1
317 if(corner.Z == 1) p.Z += 1;
318 // else corner.Z == -1
320 return getSmoothLightCombined(p, data);
324 Converts from day + night color values (0..255)
325 and a given daynight_ratio to the final SColor shown on screen.
327 void finalColorBlend(video::SColor& result,
328 u8 day, u8 night, u32 daynight_ratio)
330 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
334 b += (day - night) / 13;
335 rg -= (day - night) / 23;
337 // Emphase blue a bit in darker places
338 // Each entry of this array represents a range of 8 blue levels
339 static const u8 emphase_blue_when_dark[32] = {
340 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
341 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
343 b += emphase_blue_when_dark[b / 8];
344 b = irr::core::clamp (b, 0, 255);
346 // Artificial light is yellow-ish
347 static const u8 emphase_yellow_when_artificial[16] = {
348 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
350 rg += emphase_yellow_when_artificial[night/16];
351 rg = irr::core::clamp (rg, 0, 255);
359 Mesh generation helpers
363 vertex_dirs: v3s16[4]
365 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
368 If looked from outside the node towards the face, the corners are:
374 if(dir == v3s16(0,0,1))
376 // If looking towards z+, this is the face that is behind
377 // the center point, facing towards z+.
378 vertex_dirs[0] = v3s16(-1,-1, 1);
379 vertex_dirs[1] = v3s16( 1,-1, 1);
380 vertex_dirs[2] = v3s16( 1, 1, 1);
381 vertex_dirs[3] = v3s16(-1, 1, 1);
383 else if(dir == v3s16(0,0,-1))
386 vertex_dirs[0] = v3s16( 1,-1,-1);
387 vertex_dirs[1] = v3s16(-1,-1,-1);
388 vertex_dirs[2] = v3s16(-1, 1,-1);
389 vertex_dirs[3] = v3s16( 1, 1,-1);
391 else if(dir == v3s16(1,0,0))
394 vertex_dirs[0] = v3s16( 1,-1, 1);
395 vertex_dirs[1] = v3s16( 1,-1,-1);
396 vertex_dirs[2] = v3s16( 1, 1,-1);
397 vertex_dirs[3] = v3s16( 1, 1, 1);
399 else if(dir == v3s16(-1,0,0))
402 vertex_dirs[0] = v3s16(-1,-1,-1);
403 vertex_dirs[1] = v3s16(-1,-1, 1);
404 vertex_dirs[2] = v3s16(-1, 1, 1);
405 vertex_dirs[3] = v3s16(-1, 1,-1);
407 else if(dir == v3s16(0,1,0))
409 // faces towards Y+ (assume Z- as "down" in texture)
410 vertex_dirs[0] = v3s16( 1, 1,-1);
411 vertex_dirs[1] = v3s16(-1, 1,-1);
412 vertex_dirs[2] = v3s16(-1, 1, 1);
413 vertex_dirs[3] = v3s16( 1, 1, 1);
415 else if(dir == v3s16(0,-1,0))
417 // faces towards Y- (assume Z+ as "down" in texture)
418 vertex_dirs[0] = v3s16( 1,-1, 1);
419 vertex_dirs[1] = v3s16(-1,-1, 1);
420 vertex_dirs[2] = v3s16(-1,-1,-1);
421 vertex_dirs[3] = v3s16( 1,-1,-1);
428 video::S3DVertex vertices[4]; // Precalculated vertices
431 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
432 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
436 // Position is at the center of the cube.
445 v3s16 vertex_dirs[4];
446 getNodeVertexDirs(dir, vertex_dirs);
450 switch (tile.rotation)
456 vertex_dirs[0] = vertex_dirs[3];
457 vertex_dirs[3] = vertex_dirs[2];
458 vertex_dirs[2] = vertex_dirs[1];
468 vertex_dirs[0] = vertex_dirs[2];
471 vertex_dirs[1] = vertex_dirs[3];
482 vertex_dirs[0] = vertex_dirs[1];
483 vertex_dirs[1] = vertex_dirs[2];
484 vertex_dirs[2] = vertex_dirs[3];
494 vertex_dirs[0] = vertex_dirs[3];
495 vertex_dirs[3] = vertex_dirs[2];
496 vertex_dirs[2] = vertex_dirs[1];
508 vertex_dirs[0] = vertex_dirs[1];
509 vertex_dirs[1] = vertex_dirs[2];
510 vertex_dirs[2] = vertex_dirs[3];
522 vertex_dirs[0] = vertex_dirs[3];
523 vertex_dirs[3] = vertex_dirs[2];
524 vertex_dirs[2] = vertex_dirs[1];
536 vertex_dirs[0] = vertex_dirs[1];
537 vertex_dirs[1] = vertex_dirs[2];
538 vertex_dirs[2] = vertex_dirs[3];
560 for(u16 i=0; i<4; i++)
563 BS/2*vertex_dirs[i].X,
564 BS/2*vertex_dirs[i].Y,
565 BS/2*vertex_dirs[i].Z
569 for(u16 i=0; i<4; i++)
571 vertex_pos[i].X *= scale.X;
572 vertex_pos[i].Y *= scale.Y;
573 vertex_pos[i].Z *= scale.Z;
574 vertex_pos[i] += pos;
578 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
579 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
580 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
582 v3f normal(dir.X, dir.Y, dir.Z);
584 u8 alpha = tile.alpha;
586 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
587 MapBlock_LightColor(alpha, li0, light_source),
588 core::vector2d<f32>(x0+w*abs_scale, y0+h));
589 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
590 MapBlock_LightColor(alpha, li1, light_source),
591 core::vector2d<f32>(x0, y0+h));
592 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
593 MapBlock_LightColor(alpha, li2, light_source),
594 core::vector2d<f32>(x0, y0));
595 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
596 MapBlock_LightColor(alpha, li3, light_source),
597 core::vector2d<f32>(x0+w*abs_scale, y0));
600 dest.push_back(face);
604 Nodes make a face if contents differ and solidness differs.
607 1: Face uses m1's content
608 2: Face uses m2's content
609 equivalent: Whether the blocks share the same face (eg. water and glass)
611 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
613 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
614 INodeDefManager *ndef)
618 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
621 bool contents_differ = (m1 != m2);
623 const ContentFeatures &f1 = ndef->get(m1);
624 const ContentFeatures &f2 = ndef->get(m2);
626 // Contents don't differ for different forms of same liquid
627 if(f1.sameLiquid(f2))
628 contents_differ = false;
630 u8 c1 = f1.solidness;
631 u8 c2 = f2.solidness;
633 bool solidness_differs = (c1 != c2);
634 bool makes_face = contents_differ && solidness_differs;
636 if(makes_face == false)
640 c1 = f1.visual_solidness;
642 c2 = f2.visual_solidness;
646 // If same solidness, liquid takes precense
660 Gets nth node tile (0 <= n <= 5).
662 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
664 INodeDefManager *ndef = data->m_gamedef->ndef();
665 TileSpec spec = ndef->get(mn).tiles[tileindex];
666 // Apply temporary crack
667 if (p == data->m_crack_pos_relative)
668 spec.material_flags |= MATERIAL_FLAG_CRACK;
673 Gets node tile given a face direction.
675 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
677 INodeDefManager *ndef = data->m_gamedef->ndef();
679 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
680 // (0,0,1), (0,0,-1) or (0,0,0)
681 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
683 // Convert direction to single integer for table lookup
688 // 4 = invalid, treat as (0,0,0)
692 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
694 // Get rotation for things like chests
695 u8 facedir = mn.getFaceDir(ndef);
697 static const u16 dir_to_tile[24 * 16] =
699 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
700 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
701 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
702 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
703 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
705 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
706 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
707 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
708 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
710 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
711 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
712 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
713 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
715 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
716 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
717 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
718 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
720 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
721 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
722 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
723 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
725 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
726 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
727 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
728 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
731 u16 tile_index=facedir*16 + dir_i;
732 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
733 spec.rotation=dir_to_tile[tile_index + 1];
734 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
738 static void getTileInfo(
746 v3s16 &face_dir_corrected,
752 VoxelManipulator &vmanip = data->m_vmanip;
753 INodeDefManager *ndef = data->m_gamedef->ndef();
754 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
756 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
758 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
759 if (n0.getContent() == CONTENT_IGNORE ) {
763 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
766 bool equivalent = false;
767 u8 mf = face_contents(n0.getContent(), n1.getContent(),
780 tile = getNodeTile(n0, p, face_dir, data);
782 face_dir_corrected = face_dir;
783 light_source = ndef->get(n0).light_source;
787 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
788 p_corrected = p + face_dir;
789 face_dir_corrected = -face_dir;
790 light_source = ndef->get(n1).light_source;
793 // eg. water and glass
795 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
797 if(data->m_smooth_lighting == false)
799 lights[0] = lights[1] = lights[2] = lights[3] =
800 getFaceLight(n0, n1, face_dir, ndef);
804 v3s16 vertex_dirs[4];
805 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
806 for(u16 i=0; i<4; i++)
808 lights[i] = getSmoothLight(
809 blockpos_nodes + p_corrected,
810 vertex_dirs[i], data);
819 translate_dir: unit vector with only one of x, y or z
820 face_dir: unit vector with only one of x, y or z
822 static void updateFastFaceRow(
829 std::vector<FastFace> &dest)
833 u16 continuous_tiles_count = 0;
835 bool makes_face = false;
837 v3s16 face_dir_corrected;
838 u16 lights[4] = {0,0,0,0};
841 getTileInfo(data, p, face_dir,
842 makes_face, p_corrected, face_dir_corrected,
843 lights, tile, light_source);
845 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
847 // If tiling can be done, this is set to false in the next step
848 bool next_is_different = true;
852 bool next_makes_face = false;
853 v3s16 next_p_corrected;
854 v3s16 next_face_dir_corrected;
855 u16 next_lights[4] = {0,0,0,0};
857 u8 next_light_source = 0;
859 // If at last position, there is nothing to compare to and
860 // the face must be drawn anyway
861 if(j != MAP_BLOCKSIZE - 1)
863 p_next = p + translate_dir;
865 getTileInfo(data, p_next, face_dir,
866 next_makes_face, next_p_corrected,
867 next_face_dir_corrected, next_lights,
868 next_tile, next_light_source);
870 if(next_makes_face == makes_face
871 && next_p_corrected == p_corrected + translate_dir
872 && next_face_dir_corrected == face_dir_corrected
873 && next_lights[0] == lights[0]
874 && next_lights[1] == lights[1]
875 && next_lights[2] == lights[2]
876 && next_lights[3] == lights[3]
878 && tile.rotation == 0
879 && next_light_source == light_source)
881 next_is_different = false;
885 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
886 next_makes_face != makes_face ? 1 : 0);
887 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
888 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
889 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
890 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
891 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
892 (next_lights[0] != lights[0] ||
893 next_lights[0] != lights[0] ||
894 next_lights[0] != lights[0] ||
895 next_lights[0] != lights[0]) ? 1 : 0);
896 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
897 !(next_tile == tile) ? 1 : 0);
900 /*g_profiler->add("Meshgen: Total faces checked", 1);
902 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
905 g_profiler->add("Meshgen: diff: last position", 1);*/
908 continuous_tiles_count++;
910 if(next_is_different)
913 Create a face if there should be one
917 // Floating point conversion of the position vector
918 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
919 // Center point of face (kind of)
920 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
921 if(continuous_tiles_count != 1)
922 sp += translate_dir_f;
925 if(translate_dir.X != 0) {
926 scale.X = continuous_tiles_count;
928 if(translate_dir.Y != 0) {
929 scale.Y = continuous_tiles_count;
931 if(translate_dir.Z != 0) {
932 scale.Z = continuous_tiles_count;
935 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
936 sp, face_dir_corrected, scale, light_source,
939 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
940 for(int i = 1; i < continuous_tiles_count; i++){
941 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
945 continuous_tiles_count = 0;
947 makes_face = next_makes_face;
948 p_corrected = next_p_corrected;
949 face_dir_corrected = next_face_dir_corrected;
950 lights[0] = next_lights[0];
951 lights[1] = next_lights[1];
952 lights[2] = next_lights[2];
953 lights[3] = next_lights[3];
955 light_source = next_light_source;
962 static void updateAllFastFaceRows(MeshMakeData *data,
963 std::vector<FastFace> &dest)
966 Go through every y,z and get top(y+) faces in rows of x+
968 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
969 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
970 updateFastFaceRow(data,
974 v3s16(0,1,0), //face dir
981 Go through every x,y and get right(x+) faces in rows of z+
983 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
984 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
985 updateFastFaceRow(data,
989 v3s16(1,0,0), //face dir
996 Go through every y,z and get back(z+) faces in rows of x+
998 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
999 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1000 updateFastFaceRow(data,
1004 v3s16(0,0,1), //face dir
1015 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1016 m_mesh(new scene::SMesh()),
1017 m_gamedef(data->m_gamedef),
1018 m_animation_force_timer(0), // force initial animation
1020 m_crack_materials(),
1021 m_highlighted_materials(),
1022 m_last_daynight_ratio((u32) -1),
1025 m_enable_shaders = g_settings->getBool("enable_shaders");
1026 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1028 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1029 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1030 //TimeTaker timer1("MapBlockMesh()");
1032 std::vector<FastFace> fastfaces_new;
1035 We are including the faces of the trailing edges of the block.
1036 This means that when something changes, the caller must
1037 also update the meshes of the blocks at the leading edges.
1039 NOTE: This is the slowest part of this method.
1042 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1043 //TimeTaker timer2("updateAllFastFaceRows()");
1044 updateAllFastFaceRows(data, fastfaces_new);
1049 Convert FastFaces to MeshCollector
1052 MeshCollector collector;
1055 // avg 0ms (100ms spikes when loading textures the first time)
1056 // (NOTE: probably outdated)
1057 //TimeTaker timer2("MeshCollector building");
1059 for(u32 i=0; i<fastfaces_new.size(); i++)
1061 FastFace &f = fastfaces_new[i];
1063 const u16 indices[] = {0,1,2,2,3,0};
1064 const u16 indices_alternate[] = {0,1,3,2,3,1};
1066 if(f.tile.texture == NULL)
1069 const u16 *indices_p = indices;
1072 Revert triangles for nicer looking gradient if vertices
1073 1 and 3 have same color or 0 and 2 have different color.
1074 getRed() is the day color.
1076 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1077 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1078 indices_p = indices_alternate;
1080 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1085 Add special graphics:
1092 mapblock_mesh_generate_special(data, collector);
1094 m_highlight_mesh_color = data->m_highlight_mesh_color;
1097 Convert MeshCollector to SMesh
1099 ITextureSource *tsrc = m_gamedef->tsrc();
1100 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1102 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1104 PreMeshBuffer &p = collector.prebuffers[i];
1106 // Generate animation data
1108 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1110 // Find the texture name plus ^[crack:N:
1111 std::ostringstream os(std::ios::binary);
1112 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1113 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1114 os<<"o"; // use ^[cracko
1115 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1116 m_crack_materials.insert(std::make_pair(i, os.str()));
1117 // Replace tile texture with the cracked one
1118 p.tile.texture = tsrc->getTexture(
1120 &p.tile.texture_id);
1122 // - Texture animation
1123 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1125 // Add to MapBlockMesh in order to animate these tiles
1126 m_animation_tiles[i] = p.tile;
1127 m_animation_frames[i] = 0;
1128 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1129 // Get starting position from noise
1130 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1131 data->m_blockpos.X, data->m_blockpos.Y,
1132 data->m_blockpos.Z, 0));
1134 // Play all synchronized
1135 m_animation_frame_offsets[i] = 0;
1137 // Replace tile texture with the first animation frame
1138 FrameSpec animation_frame = p.tile.frames[0];
1139 p.tile.texture = animation_frame.texture;
1142 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1143 m_highlighted_materials.push_back(i);
1145 for(u32 j = 0; j < p.vertices.size(); j++)
1147 // Note applyFacesShading second parameter is precalculated sqrt
1148 // value for speed improvement
1149 // Skip it for lightsources and top faces.
1150 video::SColor &vc = p.vertices[j].Color;
1151 if (!vc.getBlue()) {
1152 if (p.vertices[j].Normal.Y < -0.5) {
1153 applyFacesShading (vc, 0.447213);
1154 } else if (p.vertices[j].Normal.X > 0.5) {
1155 applyFacesShading (vc, 0.670820);
1156 } else if (p.vertices[j].Normal.X < -0.5) {
1157 applyFacesShading (vc, 0.670820);
1158 } else if (p.vertices[j].Normal.Z > 0.5) {
1159 applyFacesShading (vc, 0.836660);
1160 } else if (p.vertices[j].Normal.Z < -0.5) {
1161 applyFacesShading (vc, 0.836660);
1164 if(!m_enable_shaders)
1166 // - Classic lighting (shaders handle this by themselves)
1167 // Set initial real color and store for later updates
1168 u8 day = vc.getRed();
1169 u8 night = vc.getGreen();
1170 finalColorBlend(vc, day, night, 1000);
1172 m_daynight_diffs[i][j] = std::make_pair(day, night);
1177 video::SMaterial material;
1178 material.setFlag(video::EMF_LIGHTING, false);
1179 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1180 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1181 material.setFlag(video::EMF_FOG_ENABLE, true);
1182 material.setTexture(0, p.tile.texture);
1184 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1185 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1187 if (m_enable_shaders) {
1188 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1189 p.tile.applyMaterialOptionsWithShaders(material);
1190 if (p.tile.normal_texture) {
1191 material.setTexture(1, p.tile.normal_texture);
1192 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1194 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1197 p.tile.applyMaterialOptions(material);
1201 // Create meshbuffer
1202 // This is a "Standard MeshBuffer",
1203 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1204 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1206 buf->Material = material;
1208 m_mesh->addMeshBuffer(buf);
1211 buf->append(&p.vertices[0], p.vertices.size(),
1212 &p.indices[0], p.indices.size());
1215 m_camera_offset = camera_offset;
1218 Do some stuff to the mesh
1221 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1226 // Usually 1-700 faces and 1-7 materials
1227 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1228 <<"and uses "<<m_mesh->getMeshBufferCount()
1229 <<" materials (meshbuffers)"<<std::endl;
1232 // Use VBO for mesh (this just would set this for ever buffer)
1233 // This will lead to infinite memory usage because or irrlicht.
1234 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1237 NOTE: If that is enabled, some kind of a queue to the main
1238 thread should be made which would call irrlicht to delete
1239 the hardware buffer and then delete the mesh
1243 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1245 // Check if animation is required for this mesh
1247 !m_crack_materials.empty() ||
1248 !m_daynight_diffs.empty() ||
1249 !m_animation_tiles.empty() ||
1250 !m_highlighted_materials.empty();
1253 MapBlockMesh::~MapBlockMesh()
1259 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1262 if(!m_has_animation)
1264 m_animation_force_timer = 100000;
1268 m_animation_force_timer = myrand_range(5, 100);
1271 if(crack != m_last_crack)
1273 for(std::map<u32, std::string>::iterator
1274 i = m_crack_materials.begin();
1275 i != m_crack_materials.end(); i++)
1277 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1278 std::string basename = i->second;
1280 // Create new texture name from original
1281 ITextureSource *tsrc = m_gamedef->getTextureSource();
1282 std::ostringstream os;
1283 os<<basename<<crack;
1284 u32 new_texture_id = 0;
1285 video::ITexture *new_texture =
1286 tsrc->getTexture(os.str(), &new_texture_id);
1287 buf->getMaterial().setTexture(0, new_texture);
1289 // If the current material is also animated,
1290 // update animation info
1291 std::map<u32, TileSpec>::iterator anim_iter =
1292 m_animation_tiles.find(i->first);
1293 if(anim_iter != m_animation_tiles.end()){
1294 TileSpec &tile = anim_iter->second;
1295 tile.texture = new_texture;
1296 tile.texture_id = new_texture_id;
1297 // force animation update
1298 m_animation_frames[i->first] = -1;
1302 m_last_crack = crack;
1305 // Texture animation
1306 for(std::map<u32, TileSpec>::iterator
1307 i = m_animation_tiles.begin();
1308 i != m_animation_tiles.end(); i++)
1310 const TileSpec &tile = i->second;
1311 // Figure out current frame
1312 int frameoffset = m_animation_frame_offsets[i->first];
1313 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1314 + frameoffset) % tile.animation_frame_count;
1315 // If frame doesn't change, skip
1316 if(frame == m_animation_frames[i->first])
1319 m_animation_frames[i->first] = frame;
1321 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1322 ITextureSource *tsrc = m_gamedef->getTextureSource();
1324 FrameSpec animation_frame = tile.frames[frame];
1325 buf->getMaterial().setTexture(0, animation_frame.texture);
1326 if (m_enable_shaders) {
1327 if (animation_frame.normal_texture) {
1328 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1329 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1331 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1336 // Day-night transition
1337 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1339 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1340 i = m_daynight_diffs.begin();
1341 i != m_daynight_diffs.end(); i++)
1343 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1344 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1345 for(std::map<u32, std::pair<u8, u8 > >::iterator
1346 j = i->second.begin();
1347 j != i->second.end(); j++)
1349 u32 vertexIndex = j->first;
1350 u8 day = j->second.first;
1351 u8 night = j->second.second;
1352 finalColorBlend(vertices[vertexIndex].Color,
1353 day, night, daynight_ratio);
1356 m_last_daynight_ratio = daynight_ratio;
1359 // Node highlighting
1360 if (m_enable_highlighting) {
1361 u8 day = m_highlight_mesh_color.getRed();
1362 u8 night = m_highlight_mesh_color.getGreen();
1364 finalColorBlend(hc, day, night, daynight_ratio);
1365 float sin_r = 0.07 * sin(1.5 * time);
1366 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1367 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1368 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1369 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1370 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1372 for(std::list<u32>::iterator
1373 i = m_highlighted_materials.begin();
1374 i != m_highlighted_materials.end(); i++)
1376 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1377 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1378 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1379 vertices[j].Color = hc;
1386 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1388 if (camera_offset != m_camera_offset) {
1389 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1390 m_camera_offset = camera_offset;
1398 void MeshCollector::append(const TileSpec &tile,
1399 const video::S3DVertex *vertices, u32 numVertices,
1400 const u16 *indices, u32 numIndices)
1402 if(numIndices > 65535)
1404 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1408 PreMeshBuffer *p = NULL;
1409 for(u32 i=0; i<prebuffers.size(); i++)
1411 PreMeshBuffer &pp = prebuffers[i];
1414 if(pp.indices.size() + numIndices > 65535)
1425 prebuffers.push_back(pp);
1426 p = &prebuffers[prebuffers.size()-1];
1429 u32 vertex_count = p->vertices.size();
1430 for(u32 i=0; i<numIndices; i++)
1432 u32 j = indices[i] + vertex_count;
1433 p->indices.push_back(j);
1435 for(u32 i=0; i<numVertices; i++)
1437 p->vertices.push_back(vertices[i]);
1442 MeshCollector - for meshnodes and converted drawtypes.
1445 void MeshCollector::append(const TileSpec &tile,
1446 const video::S3DVertex *vertices, u32 numVertices,
1447 const u16 *indices, u32 numIndices,
1448 v3f pos, video::SColor c)
1450 if(numIndices > 65535)
1452 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1456 PreMeshBuffer *p = NULL;
1457 for(u32 i=0; i<prebuffers.size(); i++)
1459 PreMeshBuffer &pp = prebuffers[i];
1462 if(pp.indices.size() + numIndices > 65535)
1473 prebuffers.push_back(pp);
1474 p = &prebuffers[prebuffers.size()-1];
1477 u32 vertex_count = p->vertices.size();
1478 for(u32 i=0; i<numIndices; i++)
1480 u32 j = indices[i] + vertex_count;
1481 p->indices.push_back(j);
1483 for(u32 i=0; i<numVertices; i++)
1485 video::S3DVertex vert = vertices[i];
1488 p->vertices.push_back(vert);