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.getLight(LIGHTBANK_DAY, ndef));
263 light_night += decode_light(n.getLight(LIGHTBANK_NIGHT, ndef));
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);
698 static const u16 dir_to_tile[24 * 16] =
700 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
701 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
702 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
703 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
704 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
706 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
707 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
708 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
709 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
711 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
712 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
713 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
714 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
716 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
717 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
718 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
719 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
721 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
722 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
723 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
724 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
726 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
727 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
728 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
729 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
732 u16 tile_index=facedir*16 + dir_i;
733 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
734 spec.rotation=dir_to_tile[tile_index + 1];
735 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
739 static void getTileInfo(
747 v3s16 &face_dir_corrected,
753 VoxelManipulator &vmanip = data->m_vmanip;
754 INodeDefManager *ndef = data->m_gamedef->ndef();
755 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
757 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
759 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
760 if (n0.getContent() == CONTENT_IGNORE ) {
764 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
767 bool equivalent = false;
768 u8 mf = face_contents(n0.getContent(), n1.getContent(),
781 tile = getNodeTile(n0, p, face_dir, data);
783 face_dir_corrected = face_dir;
784 light_source = ndef->get(n0).light_source;
788 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
789 p_corrected = p + face_dir;
790 face_dir_corrected = -face_dir;
791 light_source = ndef->get(n1).light_source;
794 // eg. water and glass
796 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
798 if(data->m_smooth_lighting == false)
800 lights[0] = lights[1] = lights[2] = lights[3] =
801 getFaceLight(n0, n1, face_dir, ndef);
805 v3s16 vertex_dirs[4];
806 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
807 for(u16 i=0; i<4; i++)
809 lights[i] = getSmoothLight(
810 blockpos_nodes + p_corrected,
811 vertex_dirs[i], data);
820 translate_dir: unit vector with only one of x, y or z
821 face_dir: unit vector with only one of x, y or z
823 static void updateFastFaceRow(
830 std::vector<FastFace> &dest)
834 u16 continuous_tiles_count = 0;
836 bool makes_face = false;
838 v3s16 face_dir_corrected;
839 u16 lights[4] = {0,0,0,0};
842 getTileInfo(data, p, face_dir,
843 makes_face, p_corrected, face_dir_corrected,
844 lights, tile, light_source);
846 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
848 // If tiling can be done, this is set to false in the next step
849 bool next_is_different = true;
853 bool next_makes_face = false;
854 v3s16 next_p_corrected;
855 v3s16 next_face_dir_corrected;
856 u16 next_lights[4] = {0,0,0,0};
858 u8 next_light_source = 0;
860 // If at last position, there is nothing to compare to and
861 // the face must be drawn anyway
862 if(j != MAP_BLOCKSIZE - 1)
864 p_next = p + translate_dir;
866 getTileInfo(data, p_next, face_dir,
867 next_makes_face, next_p_corrected,
868 next_face_dir_corrected, next_lights,
869 next_tile, next_light_source);
871 if(next_makes_face == makes_face
872 && next_p_corrected == p_corrected + translate_dir
873 && next_face_dir_corrected == face_dir_corrected
874 && next_lights[0] == lights[0]
875 && next_lights[1] == lights[1]
876 && next_lights[2] == lights[2]
877 && next_lights[3] == lights[3]
879 && tile.rotation == 0
880 && next_light_source == light_source)
882 next_is_different = false;
886 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
887 next_makes_face != makes_face ? 1 : 0);
888 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
889 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
890 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
891 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
892 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
893 (next_lights[0] != lights[0] ||
894 next_lights[0] != lights[0] ||
895 next_lights[0] != lights[0] ||
896 next_lights[0] != lights[0]) ? 1 : 0);
897 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
898 !(next_tile == tile) ? 1 : 0);
901 /*g_profiler->add("Meshgen: Total faces checked", 1);
903 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
906 g_profiler->add("Meshgen: diff: last position", 1);*/
909 continuous_tiles_count++;
911 if(next_is_different)
914 Create a face if there should be one
918 // Floating point conversion of the position vector
919 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
920 // Center point of face (kind of)
921 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
922 if(continuous_tiles_count != 1)
923 sp += translate_dir_f;
926 if(translate_dir.X != 0) {
927 scale.X = continuous_tiles_count;
929 if(translate_dir.Y != 0) {
930 scale.Y = continuous_tiles_count;
932 if(translate_dir.Z != 0) {
933 scale.Z = continuous_tiles_count;
936 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
937 sp, face_dir_corrected, scale, light_source,
940 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
941 for(int i = 1; i < continuous_tiles_count; i++){
942 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
946 continuous_tiles_count = 0;
948 makes_face = next_makes_face;
949 p_corrected = next_p_corrected;
950 face_dir_corrected = next_face_dir_corrected;
951 lights[0] = next_lights[0];
952 lights[1] = next_lights[1];
953 lights[2] = next_lights[2];
954 lights[3] = next_lights[3];
956 light_source = next_light_source;
963 static void updateAllFastFaceRows(MeshMakeData *data,
964 std::vector<FastFace> &dest)
967 Go through every y,z and get top(y+) faces in rows of x+
969 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
970 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
971 updateFastFaceRow(data,
975 v3s16(0,1,0), //face dir
982 Go through every x,y and get right(x+) faces in rows of z+
984 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
985 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
986 updateFastFaceRow(data,
990 v3s16(1,0,0), //face dir
997 Go through every y,z and get back(z+) faces in rows of x+
999 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1000 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1001 updateFastFaceRow(data,
1005 v3s16(0,0,1), //face dir
1016 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1017 m_mesh(new scene::SMesh()),
1018 m_gamedef(data->m_gamedef),
1019 m_animation_force_timer(0), // force initial animation
1021 m_crack_materials(),
1022 m_highlighted_materials(),
1023 m_last_daynight_ratio((u32) -1),
1026 m_enable_shaders = g_settings->getBool("enable_shaders");
1027 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1029 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1030 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1031 //TimeTaker timer1("MapBlockMesh()");
1033 std::vector<FastFace> fastfaces_new;
1036 We are including the faces of the trailing edges of the block.
1037 This means that when something changes, the caller must
1038 also update the meshes of the blocks at the leading edges.
1040 NOTE: This is the slowest part of this method.
1043 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1044 //TimeTaker timer2("updateAllFastFaceRows()");
1045 updateAllFastFaceRows(data, fastfaces_new);
1050 Convert FastFaces to MeshCollector
1053 MeshCollector collector;
1056 // avg 0ms (100ms spikes when loading textures the first time)
1057 // (NOTE: probably outdated)
1058 //TimeTaker timer2("MeshCollector building");
1060 for(u32 i=0; i<fastfaces_new.size(); i++)
1062 FastFace &f = fastfaces_new[i];
1064 const u16 indices[] = {0,1,2,2,3,0};
1065 const u16 indices_alternate[] = {0,1,3,2,3,1};
1067 if(f.tile.texture == NULL)
1070 const u16 *indices_p = indices;
1073 Revert triangles for nicer looking gradient if vertices
1074 1 and 3 have same color or 0 and 2 have different color.
1075 getRed() is the day color.
1077 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1078 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1079 indices_p = indices_alternate;
1081 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1086 Add special graphics:
1093 mapblock_mesh_generate_special(data, collector);
1095 m_highlight_mesh_color = data->m_highlight_mesh_color;
1098 Convert MeshCollector to SMesh
1100 ITextureSource *tsrc = m_gamedef->tsrc();
1101 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1103 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1105 PreMeshBuffer &p = collector.prebuffers[i];
1107 // Generate animation data
1109 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1111 // Find the texture name plus ^[crack:N:
1112 std::ostringstream os(std::ios::binary);
1113 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1114 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1115 os<<"o"; // use ^[cracko
1116 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1117 m_crack_materials.insert(std::make_pair(i, os.str()));
1118 // Replace tile texture with the cracked one
1119 p.tile.texture = tsrc->getTexture(
1121 &p.tile.texture_id);
1123 // - Texture animation
1124 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1126 // Add to MapBlockMesh in order to animate these tiles
1127 m_animation_tiles[i] = p.tile;
1128 m_animation_frames[i] = 0;
1129 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1130 // Get starting position from noise
1131 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1132 data->m_blockpos.X, data->m_blockpos.Y,
1133 data->m_blockpos.Z, 0));
1135 // Play all synchronized
1136 m_animation_frame_offsets[i] = 0;
1138 // Replace tile texture with the first animation frame
1139 FrameSpec animation_frame = p.tile.frames.find(0)->second;
1140 p.tile.texture = animation_frame.texture;
1143 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1144 m_highlighted_materials.push_back(i);
1146 for(u32 j = 0; j < p.vertices.size(); j++)
1148 // Note applyFacesShading second parameter is precalculated sqrt
1149 // value for speed improvement
1150 // Skip it for lightsources and top faces.
1151 video::SColor &vc = p.vertices[j].Color;
1152 if (!vc.getBlue()) {
1153 if (p.vertices[j].Normal.Y < -0.5) {
1154 applyFacesShading (vc, 0.447213);
1155 } else if (p.vertices[j].Normal.X > 0.5) {
1156 applyFacesShading (vc, 0.670820);
1157 } else if (p.vertices[j].Normal.X < -0.5) {
1158 applyFacesShading (vc, 0.670820);
1159 } else if (p.vertices[j].Normal.Z > 0.5) {
1160 applyFacesShading (vc, 0.836660);
1161 } else if (p.vertices[j].Normal.Z < -0.5) {
1162 applyFacesShading (vc, 0.836660);
1165 // - Classic lighting
1166 // Set initial real color and store for later updates
1167 u8 day = vc.getRed();
1168 u8 night = vc.getGreen();
1169 finalColorBlend(vc, day, night, 1000);
1170 m_daynight_diffs[i][j] = std::make_pair(day, night);
1174 video::SMaterial material;
1175 material.setFlag(video::EMF_LIGHTING, false);
1176 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1177 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1178 material.setFlag(video::EMF_FOG_ENABLE, true);
1179 material.setTexture(0, p.tile.texture);
1181 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1182 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1184 if (m_enable_shaders) {
1185 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1186 p.tile.applyMaterialOptionsWithShaders(material);
1187 if (p.tile.normal_texture) {
1188 material.setTexture(1, p.tile.normal_texture);
1189 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1191 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1194 p.tile.applyMaterialOptions(material);
1198 // Create meshbuffer
1199 // This is a "Standard MeshBuffer",
1200 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1201 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1203 buf->Material = material;
1205 m_mesh->addMeshBuffer(buf);
1208 buf->append(&p.vertices[0], p.vertices.size(),
1209 &p.indices[0], p.indices.size());
1212 m_camera_offset = camera_offset;
1215 Do some stuff to the mesh
1218 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1223 // Usually 1-700 faces and 1-7 materials
1224 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1225 <<"and uses "<<m_mesh->getMeshBufferCount()
1226 <<" materials (meshbuffers)"<<std::endl;
1229 // Use VBO for mesh (this just would set this for ever buffer)
1230 // This will lead to infinite memory usage because or irrlicht.
1231 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1234 NOTE: If that is enabled, some kind of a queue to the main
1235 thread should be made which would call irrlicht to delete
1236 the hardware buffer and then delete the mesh
1240 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1242 // Check if animation is required for this mesh
1244 !m_crack_materials.empty() ||
1245 !m_daynight_diffs.empty() ||
1246 !m_animation_tiles.empty() ||
1247 !m_highlighted_materials.empty();
1250 MapBlockMesh::~MapBlockMesh()
1256 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1259 if(!m_has_animation)
1261 m_animation_force_timer = 100000;
1265 m_animation_force_timer = myrand_range(5, 100);
1268 if(crack != m_last_crack)
1270 for(std::map<u32, std::string>::iterator
1271 i = m_crack_materials.begin();
1272 i != m_crack_materials.end(); i++)
1274 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1275 std::string basename = i->second;
1277 // Create new texture name from original
1278 ITextureSource *tsrc = m_gamedef->getTextureSource();
1279 std::ostringstream os;
1280 os<<basename<<crack;
1281 u32 new_texture_id = 0;
1282 video::ITexture *new_texture =
1283 tsrc->getTexture(os.str(), &new_texture_id);
1284 buf->getMaterial().setTexture(0, new_texture);
1286 // If the current material is also animated,
1287 // update animation info
1288 std::map<u32, TileSpec>::iterator anim_iter =
1289 m_animation_tiles.find(i->first);
1290 if(anim_iter != m_animation_tiles.end()){
1291 TileSpec &tile = anim_iter->second;
1292 tile.texture = new_texture;
1293 tile.texture_id = new_texture_id;
1294 // force animation update
1295 m_animation_frames[i->first] = -1;
1299 m_last_crack = crack;
1302 // Texture animation
1303 for(std::map<u32, TileSpec>::iterator
1304 i = m_animation_tiles.begin();
1305 i != m_animation_tiles.end(); i++)
1307 const TileSpec &tile = i->second;
1308 // Figure out current frame
1309 int frameoffset = m_animation_frame_offsets[i->first];
1310 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1311 + frameoffset) % tile.animation_frame_count;
1312 // If frame doesn't change, skip
1313 if(frame == m_animation_frames[i->first])
1316 m_animation_frames[i->first] = frame;
1318 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1319 ITextureSource *tsrc = m_gamedef->getTextureSource();
1321 FrameSpec animation_frame = tile.frames.find(frame)->second;
1322 buf->getMaterial().setTexture(0, animation_frame.texture);
1323 if (m_enable_shaders) {
1324 if (animation_frame.normal_texture) {
1325 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1326 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1328 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1333 // Day-night transition
1334 if(daynight_ratio != m_last_daynight_ratio)
1336 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1337 i = m_daynight_diffs.begin();
1338 i != m_daynight_diffs.end(); i++)
1340 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1341 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1342 for(std::map<u32, std::pair<u8, u8 > >::iterator
1343 j = i->second.begin();
1344 j != i->second.end(); j++)
1346 u32 vertexIndex = j->first;
1347 u8 day = j->second.first;
1348 u8 night = j->second.second;
1349 finalColorBlend(vertices[vertexIndex].Color,
1350 day, night, daynight_ratio);
1353 m_last_daynight_ratio = daynight_ratio;
1356 // Node highlighting
1357 if (m_enable_highlighting) {
1358 u8 day = m_highlight_mesh_color.getRed();
1359 u8 night = m_highlight_mesh_color.getGreen();
1361 finalColorBlend(hc, day, night, daynight_ratio);
1362 float sin_r = 0.07 * sin(1.5 * time);
1363 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1364 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1365 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1366 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1367 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1369 for(std::list<u32>::iterator
1370 i = m_highlighted_materials.begin();
1371 i != m_highlighted_materials.end(); i++)
1373 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1374 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1375 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1376 vertices[j].Color = hc;
1383 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1385 if (camera_offset != m_camera_offset) {
1386 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1387 m_camera_offset = camera_offset;
1395 void MeshCollector::append(const TileSpec &tile,
1396 const video::S3DVertex *vertices, u32 numVertices,
1397 const u16 *indices, u32 numIndices)
1399 if(numIndices > 65535)
1401 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1405 PreMeshBuffer *p = NULL;
1406 for(u32 i=0; i<prebuffers.size(); i++)
1408 PreMeshBuffer &pp = prebuffers[i];
1411 if(pp.indices.size() + numIndices > 65535)
1422 prebuffers.push_back(pp);
1423 p = &prebuffers[prebuffers.size()-1];
1426 u32 vertex_count = p->vertices.size();
1427 for(u32 i=0; i<numIndices; i++)
1429 u32 j = indices[i] + vertex_count;
1430 p->indices.push_back(j);
1432 for(u32 i=0; i<numVertices; i++)
1434 p->vertices.push_back(vertices[i]);
1439 MeshCollector - for meshnodes and converted drawtypes.
1442 void MeshCollector::append(const TileSpec &tile,
1443 const video::S3DVertex *vertices, u32 numVertices,
1444 const u16 *indices, u32 numIndices,
1445 v3f pos, video::SColor c)
1447 if(numIndices > 65535)
1449 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1453 PreMeshBuffer *p = NULL;
1454 for(u32 i=0; i<prebuffers.size(); i++)
1456 PreMeshBuffer &pp = prebuffers[i];
1459 if(pp.indices.size() + numIndices > 65535)
1470 prebuffers.push_back(pp);
1471 p = &prebuffers[prebuffers.size()-1];
1474 u32 vertex_count = p->vertices.size();
1475 for(u32 i=0; i<numIndices; i++)
1477 u32 j = indices[i] + vertex_count;
1478 p->indices.push_back(j);
1480 for(u32 i=0; i<numVertices; i++)
1482 video::S3DVertex vert = vertices[i];
1485 p->vertices.push_back(vert);