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"
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
34 #include <IMeshManipulator.h>
36 static void applyFacesShading(video::SColor &color, const float factor)
38 color.setRed(core::clamp(core::round32(color.getRed() * factor), 0, 255));
39 color.setGreen(core::clamp(core::round32(color.getGreen() * factor), 0, 255));
46 MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders,
47 bool use_tangent_vertices):
49 m_blockpos(-1337,-1337,-1337),
50 m_crack_pos_relative(-1337, -1337, -1337),
51 m_smooth_lighting(false),
54 m_use_shaders(use_shaders),
55 m_use_tangent_vertices(use_tangent_vertices)
58 void MeshMakeData::fill(MapBlock *block)
60 m_blockpos = block->getPos();
62 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
68 // Allocate this block + neighbors
70 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
71 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
72 m_vmanip.addArea(voxel_area);
75 //TimeTaker timer("copy central block data");
79 block->copyTo(m_vmanip);
82 //TimeTaker timer("copy neighbor block data");
86 Copy neighbors. This is lightning fast.
87 Copying only the borders would be *very* slow.
91 Map *map = block->getParent();
93 for(u16 i=0; i<26; i++)
95 const v3s16 &dir = g_26dirs[i];
96 v3s16 bp = m_blockpos + dir;
97 MapBlock *b = map->getBlockNoCreateNoEx(bp);
104 void MeshMakeData::fillSingleNode(MapNode *node)
106 m_blockpos = v3s16(0,0,0);
108 v3s16 blockpos_nodes = v3s16(0,0,0);
109 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
110 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
111 s32 volume = area.getVolume();
112 s32 our_node_index = area.index(1,1,1);
114 // Allocate this block + neighbors
116 m_vmanip.addArea(area);
119 MapNode *data = new MapNode[volume];
120 for(s32 i = 0; i < volume; i++)
122 if(i == our_node_index)
128 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
131 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
135 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
138 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
141 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
143 m_smooth_lighting = smooth_lighting;
147 Light and vertex color functions
151 Calculate non-smooth lighting at interior of node.
154 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
155 INodeDefManager *ndef)
157 u8 light = n.getLight(bank, ndef);
161 light = undiminish_light(light);
166 light = diminish_light(light);
170 return decode_light(light);
174 Calculate non-smooth lighting at interior of node.
177 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
179 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
180 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
181 return day | (night << 8);
185 Calculate non-smooth lighting at face of node.
188 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
189 v3s16 face_dir, INodeDefManager *ndef)
192 u8 l1 = n.getLight(bank, ndef);
193 u8 l2 = n2.getLight(bank, ndef);
199 // Boost light level for light sources
200 u8 light_source = MYMAX(ndef->get(n).light_source,
201 ndef->get(n2).light_source);
202 if(light_source > light)
203 light = light_source;
205 return decode_light(light);
209 Calculate non-smooth lighting at face of node.
212 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
214 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
215 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
216 return day | (night << 8);
220 Calculate smooth lighting at the XYZ- corner of p.
223 static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
225 static const v3s16 dirs8[8] = {
236 INodeDefManager *ndef = data->m_gamedef->ndef();
238 u16 ambient_occlusion = 0;
240 u8 light_source_max = 0;
244 for (u32 i = 0; i < 8; i++)
246 const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(p - dirs8[i]);
248 // if it's CONTENT_IGNORE we can't do any light calculations
249 if (n.getContent() == CONTENT_IGNORE) {
253 const ContentFeatures &f = ndef->get(n);
254 if (f.light_source > light_source_max)
255 light_source_max = f.light_source;
256 // Check f.solidness because fast-style leaves look better this way
257 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
258 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
259 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
269 light_day /= light_count;
270 light_night /= light_count;
272 // Boost brightness around light sources
273 bool skip_ambient_occlusion_day = false;
274 if(decode_light(light_source_max) >= light_day) {
275 light_day = decode_light(light_source_max);
276 skip_ambient_occlusion_day = true;
279 bool skip_ambient_occlusion_night = false;
280 if(decode_light(light_source_max) >= light_night) {
281 light_night = decode_light(light_source_max);
282 skip_ambient_occlusion_night = true;
285 if (ambient_occlusion > 4)
287 static const float ao_gamma = rangelim(
288 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
290 // Table of gamma space multiply factors.
291 static const float light_amount[3] = {
292 powf(0.75, 1.0 / ao_gamma),
293 powf(0.5, 1.0 / ao_gamma),
294 powf(0.25, 1.0 / ao_gamma)
297 //calculate table index for gamma space multiplier
298 ambient_occlusion -= 5;
300 if (!skip_ambient_occlusion_day)
301 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
302 if (!skip_ambient_occlusion_night)
303 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
306 return light_day | (light_night << 8);
310 Calculate smooth lighting at the given corner of p.
313 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
315 if(corner.X == 1) p.X += 1;
316 // else corner.X == -1
317 if(corner.Y == 1) p.Y += 1;
318 // else corner.Y == -1
319 if(corner.Z == 1) p.Z += 1;
320 // else corner.Z == -1
322 return getSmoothLightCombined(p, data);
326 Converts from day + night color values (0..255)
327 and a given daynight_ratio to the final SColor shown on screen.
329 void finalColorBlend(video::SColor& result,
330 u8 day, u8 night, u32 daynight_ratio)
332 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
336 b += (day - night) / 13;
337 rg -= (day - night) / 23;
339 // Emphase blue a bit in darker places
340 // Each entry of this array represents a range of 8 blue levels
341 static const u8 emphase_blue_when_dark[32] = {
342 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
343 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
345 b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8];
346 b = irr::core::clamp(b, 0, 255);
348 // Artificial light is yellow-ish
349 static const u8 emphase_yellow_when_artificial[16] = {
350 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
352 rg += emphase_yellow_when_artificial[night/16];
353 rg = irr::core::clamp(rg, 0, 255);
361 Mesh generation helpers
365 vertex_dirs: v3s16[4]
367 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
370 If looked from outside the node towards the face, the corners are:
376 if(dir == v3s16(0,0,1))
378 // If looking towards z+, this is the face that is behind
379 // the center point, facing towards z+.
380 vertex_dirs[0] = v3s16(-1,-1, 1);
381 vertex_dirs[1] = v3s16( 1,-1, 1);
382 vertex_dirs[2] = v3s16( 1, 1, 1);
383 vertex_dirs[3] = v3s16(-1, 1, 1);
385 else if(dir == v3s16(0,0,-1))
388 vertex_dirs[0] = v3s16( 1,-1,-1);
389 vertex_dirs[1] = v3s16(-1,-1,-1);
390 vertex_dirs[2] = v3s16(-1, 1,-1);
391 vertex_dirs[3] = v3s16( 1, 1,-1);
393 else if(dir == v3s16(1,0,0))
396 vertex_dirs[0] = v3s16( 1,-1, 1);
397 vertex_dirs[1] = v3s16( 1,-1,-1);
398 vertex_dirs[2] = v3s16( 1, 1,-1);
399 vertex_dirs[3] = v3s16( 1, 1, 1);
401 else if(dir == v3s16(-1,0,0))
404 vertex_dirs[0] = v3s16(-1,-1,-1);
405 vertex_dirs[1] = v3s16(-1,-1, 1);
406 vertex_dirs[2] = v3s16(-1, 1, 1);
407 vertex_dirs[3] = v3s16(-1, 1,-1);
409 else if(dir == v3s16(0,1,0))
411 // faces towards Y+ (assume Z- as "down" in texture)
412 vertex_dirs[0] = v3s16( 1, 1,-1);
413 vertex_dirs[1] = v3s16(-1, 1,-1);
414 vertex_dirs[2] = v3s16(-1, 1, 1);
415 vertex_dirs[3] = v3s16( 1, 1, 1);
417 else if(dir == v3s16(0,-1,0))
419 // faces towards Y- (assume Z+ as "down" in texture)
420 vertex_dirs[0] = v3s16( 1,-1, 1);
421 vertex_dirs[1] = v3s16(-1,-1, 1);
422 vertex_dirs[2] = v3s16(-1,-1,-1);
423 vertex_dirs[3] = v3s16( 1,-1,-1);
430 video::S3DVertex vertices[4]; // Precalculated vertices
433 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
434 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 dest.push_back(FastFace());
588 FastFace& face = *dest.rbegin();
590 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
591 MapBlock_LightColor(alpha, li0, light_source),
592 core::vector2d<f32>(x0+w*abs_scale, y0+h));
593 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
594 MapBlock_LightColor(alpha, li1, light_source),
595 core::vector2d<f32>(x0, y0+h));
596 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
597 MapBlock_LightColor(alpha, li2, light_source),
598 core::vector2d<f32>(x0, y0));
599 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
600 MapBlock_LightColor(alpha, li3, light_source),
601 core::vector2d<f32>(x0+w*abs_scale, y0));
607 Nodes make a face if contents differ and solidness differs.
610 1: Face uses m1's content
611 2: Face uses m2's content
612 equivalent: Whether the blocks share the same face (eg. water and glass)
614 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
616 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
617 INodeDefManager *ndef)
621 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
624 bool contents_differ = (m1 != m2);
626 const ContentFeatures &f1 = ndef->get(m1);
627 const ContentFeatures &f2 = ndef->get(m2);
629 // Contents don't differ for different forms of same liquid
630 if(f1.sameLiquid(f2))
631 contents_differ = false;
633 u8 c1 = f1.solidness;
634 u8 c2 = f2.solidness;
636 bool solidness_differs = (c1 != c2);
637 bool makes_face = contents_differ && solidness_differs;
639 if(makes_face == false)
643 c1 = f1.visual_solidness;
645 c2 = f2.visual_solidness;
649 // If same solidness, liquid takes precense
663 Gets nth node tile (0 <= n <= 5).
665 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
667 INodeDefManager *ndef = data->m_gamedef->ndef();
668 TileSpec spec = ndef->get(mn).tiles[tileindex];
669 // Apply temporary crack
670 if (p == data->m_crack_pos_relative)
671 spec.material_flags |= MATERIAL_FLAG_CRACK;
676 Gets node tile given a face direction.
678 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
680 INodeDefManager *ndef = data->m_gamedef->ndef();
682 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
683 // (0,0,1), (0,0,-1) or (0,0,0)
684 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
686 // Convert direction to single integer for table lookup
691 // 4 = invalid, treat as (0,0,0)
695 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
697 // Get rotation for things like chests
698 u8 facedir = mn.getFaceDir(ndef);
700 static const u16 dir_to_tile[24 * 16] =
702 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
703 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
704 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
705 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
706 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
708 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
709 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
710 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
711 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
713 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
714 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
715 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
716 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
718 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
719 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
720 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
721 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
723 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
724 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
725 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
726 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
728 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
729 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
730 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
731 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
734 u16 tile_index=facedir*16 + dir_i;
735 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
736 spec.rotation=dir_to_tile[tile_index + 1];
737 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
741 static void getTileInfo(
745 const v3s16 &face_dir,
749 v3s16 &face_dir_corrected,
755 VoxelManipulator &vmanip = data->m_vmanip;
756 INodeDefManager *ndef = data->m_gamedef->ndef();
757 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
759 MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
761 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
762 if (n0.getContent() == CONTENT_IGNORE) {
767 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
769 if (n1.getContent() == CONTENT_IGNORE) {
775 bool equivalent = false;
776 u8 mf = face_contents(n0.getContent(), n1.getContent(),
789 tile = getNodeTile(n0, p, face_dir, data);
791 face_dir_corrected = face_dir;
792 light_source = ndef->get(n0).light_source;
796 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
797 p_corrected = p + face_dir;
798 face_dir_corrected = -face_dir;
799 light_source = ndef->get(n1).light_source;
802 // eg. water and glass
804 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
806 if(data->m_smooth_lighting == false)
808 lights[0] = lights[1] = lights[2] = lights[3] =
809 getFaceLight(n0, n1, face_dir, ndef);
813 v3s16 vertex_dirs[4];
814 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
815 for(u16 i=0; i<4; i++)
817 lights[i] = getSmoothLight(
818 blockpos_nodes + p_corrected,
819 vertex_dirs[i], data);
828 translate_dir: unit vector with only one of x, y or z
829 face_dir: unit vector with only one of x, y or z
831 static void updateFastFaceRow(
838 std::vector<FastFace> &dest)
842 u16 continuous_tiles_count = 0;
844 bool makes_face = false;
846 v3s16 face_dir_corrected;
847 u16 lights[4] = {0,0,0,0};
850 getTileInfo(data, p, face_dir,
851 makes_face, p_corrected, face_dir_corrected,
852 lights, tile, light_source);
854 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
856 // If tiling can be done, this is set to false in the next step
857 bool next_is_different = true;
861 bool next_makes_face = false;
862 v3s16 next_p_corrected;
863 v3s16 next_face_dir_corrected;
864 u16 next_lights[4] = {0,0,0,0};
866 u8 next_light_source = 0;
868 // If at last position, there is nothing to compare to and
869 // the face must be drawn anyway
870 if(j != MAP_BLOCKSIZE - 1)
872 p_next = p + translate_dir;
874 getTileInfo(data, p_next, face_dir,
875 next_makes_face, next_p_corrected,
876 next_face_dir_corrected, next_lights,
877 next_tile, next_light_source);
879 if(next_makes_face == makes_face
880 && next_p_corrected == p_corrected + translate_dir
881 && next_face_dir_corrected == face_dir_corrected
882 && next_lights[0] == lights[0]
883 && next_lights[1] == lights[1]
884 && next_lights[2] == lights[2]
885 && next_lights[3] == lights[3]
887 && tile.rotation == 0
888 && next_light_source == light_source)
890 next_is_different = false;
894 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
895 next_makes_face != makes_face ? 1 : 0);
896 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
897 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
898 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
899 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
900 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
901 (next_lights[0] != lights[0] ||
902 next_lights[0] != lights[0] ||
903 next_lights[0] != lights[0] ||
904 next_lights[0] != lights[0]) ? 1 : 0);
905 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
906 !(next_tile == tile) ? 1 : 0);
909 /*g_profiler->add("Meshgen: Total faces checked", 1);
911 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
914 g_profiler->add("Meshgen: diff: last position", 1);*/
917 continuous_tiles_count++;
919 if(next_is_different)
922 Create a face if there should be one
926 // Floating point conversion of the position vector
927 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
928 // Center point of face (kind of)
929 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
930 if(continuous_tiles_count != 1)
931 sp += translate_dir_f;
934 if(translate_dir.X != 0) {
935 scale.X = continuous_tiles_count;
937 if(translate_dir.Y != 0) {
938 scale.Y = continuous_tiles_count;
940 if(translate_dir.Z != 0) {
941 scale.Z = continuous_tiles_count;
944 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
945 sp, face_dir_corrected, scale, light_source,
948 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
949 for(int i = 1; i < continuous_tiles_count; i++){
950 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
954 continuous_tiles_count = 0;
956 makes_face = next_makes_face;
957 p_corrected = next_p_corrected;
958 face_dir_corrected = next_face_dir_corrected;
959 lights[0] = next_lights[0];
960 lights[1] = next_lights[1];
961 lights[2] = next_lights[2];
962 lights[3] = next_lights[3];
964 light_source = next_light_source;
971 static void updateAllFastFaceRows(MeshMakeData *data,
972 std::vector<FastFace> &dest)
975 Go through every y,z and get top(y+) faces in rows of x+
977 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
978 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
979 updateFastFaceRow(data,
983 v3s16(0,1,0), //face dir
990 Go through every x,y and get right(x+) faces in rows of z+
992 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
993 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
994 updateFastFaceRow(data,
998 v3s16(1,0,0), //face dir
1005 Go through every y,z and get back(z+) faces in rows of x+
1007 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1008 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1009 updateFastFaceRow(data,
1013 v3s16(0,0,1), //face dir
1024 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1025 m_mesh(new scene::SMesh()),
1026 m_minimap_mapblock(NULL),
1027 m_gamedef(data->m_gamedef),
1028 m_tsrc(m_gamedef->getTextureSource()),
1029 m_shdrsrc(m_gamedef->getShaderSource()),
1030 m_animation_force_timer(0), // force initial animation
1032 m_crack_materials(),
1033 m_last_daynight_ratio((u32) -1),
1036 m_enable_shaders = data->m_use_shaders;
1037 m_use_tangent_vertices = data->m_use_tangent_vertices;
1039 if (g_settings->getBool("enable_minimap")) {
1040 m_minimap_mapblock = new MinimapMapblock;
1041 m_minimap_mapblock->getMinimapNodes(
1042 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1045 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1046 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1047 //TimeTaker timer1("MapBlockMesh()");
1049 std::vector<FastFace> fastfaces_new;
1050 fastfaces_new.reserve(512);
1053 We are including the faces of the trailing edges of the block.
1054 This means that when something changes, the caller must
1055 also update the meshes of the blocks at the leading edges.
1057 NOTE: This is the slowest part of this method.
1060 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1061 //TimeTaker timer2("updateAllFastFaceRows()");
1062 updateAllFastFaceRows(data, fastfaces_new);
1067 Convert FastFaces to MeshCollector
1070 MeshCollector collector(m_use_tangent_vertices);
1073 // avg 0ms (100ms spikes when loading textures the first time)
1074 // (NOTE: probably outdated)
1075 //TimeTaker timer2("MeshCollector building");
1077 for (u32 i = 0; i < fastfaces_new.size(); i++) {
1078 FastFace &f = fastfaces_new[i];
1080 const u16 indices[] = {0,1,2,2,3,0};
1081 const u16 indices_alternate[] = {0,1,3,2,3,1};
1083 if(f.tile.texture == NULL)
1086 const u16 *indices_p = indices;
1089 Revert triangles for nicer looking gradient if vertices
1090 1 and 3 have same color or 0 and 2 have different color.
1091 getRed() is the day color.
1093 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1094 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1095 indices_p = indices_alternate;
1097 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1102 Add special graphics:
1109 mapblock_mesh_generate_special(data, collector);
1112 Convert MeshCollector to SMesh
1115 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1117 PreMeshBuffer &p = collector.prebuffers[i];
1119 // Generate animation data
1121 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1123 // Find the texture name plus ^[crack:N:
1124 std::ostringstream os(std::ios::binary);
1125 os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1126 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1127 os<<"o"; // use ^[cracko
1128 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1129 m_crack_materials.insert(std::make_pair(i, os.str()));
1130 // Replace tile texture with the cracked one
1131 p.tile.texture = m_tsrc->getTextureForMesh(
1133 &p.tile.texture_id);
1135 // - Texture animation
1136 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1138 // Add to MapBlockMesh in order to animate these tiles
1139 m_animation_tiles[i] = p.tile;
1140 m_animation_frames[i] = 0;
1141 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1142 // Get starting position from noise
1143 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1144 data->m_blockpos.X, data->m_blockpos.Y,
1145 data->m_blockpos.Z, 0));
1147 // Play all synchronized
1148 m_animation_frame_offsets[i] = 0;
1150 // Replace tile texture with the first animation frame
1151 FrameSpec animation_frame = p.tile.frames[0];
1152 p.tile.texture = animation_frame.texture;
1155 u32 vertex_count = m_use_tangent_vertices ?
1156 p.tangent_vertices.size() : p.vertices.size();
1157 for (u32 j = 0; j < vertex_count; j++) {
1160 if (m_use_tangent_vertices) {
1161 vc = &p.tangent_vertices[j].Color;
1162 Normal = &p.tangent_vertices[j].Normal;
1164 vc = &p.vertices[j].Color;
1165 Normal = &p.vertices[j].Normal;
1167 // Note applyFacesShading second parameter is precalculated sqrt
1168 // value for speed improvement
1169 // Skip it for lightsources and top faces.
1170 if (!vc->getBlue()) {
1171 if (Normal->Y < -0.5) {
1172 applyFacesShading(*vc, 0.447213);
1173 } else if (Normal->X > 0.5) {
1174 applyFacesShading(*vc, 0.670820);
1175 } else if (Normal->X < -0.5) {
1176 applyFacesShading(*vc, 0.670820);
1177 } else if (Normal->Z > 0.5) {
1178 applyFacesShading(*vc, 0.836660);
1179 } else if (Normal->Z < -0.5) {
1180 applyFacesShading(*vc, 0.836660);
1183 if (!m_enable_shaders) {
1184 // - Classic lighting (shaders handle this by themselves)
1185 // Set initial real color and store for later updates
1186 u8 day = vc->getRed();
1187 u8 night = vc->getGreen();
1188 finalColorBlend(*vc, day, night, 1000);
1190 m_daynight_diffs[i][j] = std::make_pair(day, night);
1196 video::SMaterial material;
1197 material.setFlag(video::EMF_LIGHTING, false);
1198 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1199 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1200 material.setFlag(video::EMF_FOG_ENABLE, true);
1201 material.setTexture(0, p.tile.texture);
1203 if (m_enable_shaders) {
1204 material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
1205 p.tile.applyMaterialOptionsWithShaders(material);
1206 if (p.tile.normal_texture) {
1207 material.setTexture(1, p.tile.normal_texture);
1209 material.setTexture(2, p.tile.flags_texture);
1211 p.tile.applyMaterialOptions(material);
1214 scene::SMesh *mesh = (scene::SMesh *)m_mesh;
1216 // Create meshbuffer, add to mesh
1217 if (m_use_tangent_vertices) {
1218 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1220 buf->Material = material;
1222 mesh->addMeshBuffer(buf);
1225 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1226 &p.indices[0], p.indices.size());
1228 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1230 buf->Material = material;
1232 mesh->addMeshBuffer(buf);
1235 buf->append(&p.vertices[0], p.vertices.size(),
1236 &p.indices[0], p.indices.size());
1241 Do some stuff to the mesh
1243 m_camera_offset = camera_offset;
1244 translateMesh(m_mesh,
1245 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1247 if (m_use_tangent_vertices) {
1248 scene::IMeshManipulator* meshmanip =
1249 m_gamedef->getSceneManager()->getMeshManipulator();
1250 meshmanip->recalculateTangents(m_mesh, true, false, false);
1256 // Usually 1-700 faces and 1-7 materials
1257 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1258 <<"and uses "<<m_mesh->getMeshBufferCount()
1259 <<" materials (meshbuffers)"<<std::endl;
1262 // Use VBO for mesh (this just would set this for ever buffer)
1263 // This will lead to infinite memory usage because or irrlicht.
1264 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1267 NOTE: If that is enabled, some kind of a queue to the main
1268 thread should be made which would call irrlicht to delete
1269 the hardware buffer and then delete the mesh
1273 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1275 // Check if animation is required for this mesh
1277 !m_crack_materials.empty() ||
1278 !m_daynight_diffs.empty() ||
1279 !m_animation_tiles.empty();
1282 MapBlockMesh::~MapBlockMesh()
1286 delete m_minimap_mapblock;
1289 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1291 if(!m_has_animation)
1293 m_animation_force_timer = 100000;
1297 m_animation_force_timer = myrand_range(5, 100);
1300 if(crack != m_last_crack)
1302 for(std::map<u32, std::string>::iterator
1303 i = m_crack_materials.begin();
1304 i != m_crack_materials.end(); ++i)
1306 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1307 std::string basename = i->second;
1309 // Create new texture name from original
1310 std::ostringstream os;
1311 os<<basename<<crack;
1312 u32 new_texture_id = 0;
1313 video::ITexture *new_texture =
1314 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1315 buf->getMaterial().setTexture(0, new_texture);
1317 // If the current material is also animated,
1318 // update animation info
1319 std::map<u32, TileSpec>::iterator anim_iter =
1320 m_animation_tiles.find(i->first);
1321 if(anim_iter != m_animation_tiles.end()){
1322 TileSpec &tile = anim_iter->second;
1323 tile.texture = new_texture;
1324 tile.texture_id = new_texture_id;
1325 // force animation update
1326 m_animation_frames[i->first] = -1;
1330 m_last_crack = crack;
1333 // Texture animation
1334 for(std::map<u32, TileSpec>::iterator
1335 i = m_animation_tiles.begin();
1336 i != m_animation_tiles.end(); ++i)
1338 const TileSpec &tile = i->second;
1339 // Figure out current frame
1340 int frameoffset = m_animation_frame_offsets[i->first];
1341 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1342 + frameoffset) % tile.animation_frame_count;
1343 // If frame doesn't change, skip
1344 if(frame == m_animation_frames[i->first])
1347 m_animation_frames[i->first] = frame;
1349 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1351 FrameSpec animation_frame = tile.frames[frame];
1352 buf->getMaterial().setTexture(0, animation_frame.texture);
1353 if (m_enable_shaders) {
1354 if (animation_frame.normal_texture) {
1355 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1357 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1361 // Day-night transition
1362 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1364 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1365 i = m_daynight_diffs.begin();
1366 i != m_daynight_diffs.end(); ++i)
1368 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1369 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1370 for(std::map<u32, std::pair<u8, u8 > >::iterator
1371 j = i->second.begin();
1372 j != i->second.end(); ++j)
1374 u8 day = j->second.first;
1375 u8 night = j->second.second;
1376 finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
1379 m_last_daynight_ratio = daynight_ratio;
1385 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1387 if (camera_offset != m_camera_offset) {
1388 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1389 m_camera_offset = camera_offset;
1397 void MeshCollector::append(const TileSpec &tile,
1398 const video::S3DVertex *vertices, u32 numVertices,
1399 const u16 *indices, u32 numIndices)
1401 if (numIndices > 65535) {
1402 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1406 PreMeshBuffer *p = NULL;
1407 for (u32 i = 0; i < prebuffers.size(); i++) {
1408 PreMeshBuffer &pp = prebuffers[i];
1409 if (pp.tile != tile)
1411 if (pp.indices.size() + numIndices > 65535)
1421 prebuffers.push_back(pp);
1422 p = &prebuffers[prebuffers.size() - 1];
1426 if (m_use_tangent_vertices) {
1427 vertex_count = p->tangent_vertices.size();
1428 p->tangent_vertices.reserve(vertex_count + numVertices);
1429 for (u32 i = 0; i < numVertices; i++) {
1430 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1431 vertices[i].Color, vertices[i].TCoords);
1432 p->tangent_vertices.push_back(vert);
1435 vertex_count = p->vertices.size();
1436 p->vertices.reserve(vertex_count + numVertices);
1437 for (u32 i = 0; i < numVertices; i++) {
1438 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1439 vertices[i].Color, vertices[i].TCoords);
1440 p->vertices.push_back(vert);
1444 p->indices.reserve(p->indices.size() + numIndices);
1445 for (u32 i = 0; i < numIndices; i++) {
1446 u32 j = indices[i] + vertex_count;
1447 p->indices.push_back(j);
1452 MeshCollector - for meshnodes and converted drawtypes.
1455 void MeshCollector::append(const TileSpec &tile,
1456 const video::S3DVertex *vertices, u32 numVertices,
1457 const u16 *indices, u32 numIndices,
1458 v3f pos, video::SColor c)
1460 if (numIndices > 65535) {
1461 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1465 PreMeshBuffer *p = NULL;
1466 for (u32 i = 0; i < prebuffers.size(); i++) {
1467 PreMeshBuffer &pp = prebuffers[i];
1470 if(pp.indices.size() + numIndices > 65535)
1480 prebuffers.push_back(pp);
1481 p = &prebuffers[prebuffers.size() - 1];
1485 if (m_use_tangent_vertices) {
1486 vertex_count = p->tangent_vertices.size();
1487 p->tangent_vertices.reserve(vertex_count + numVertices);
1488 for (u32 i = 0; i < numVertices; i++) {
1489 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1490 vertices[i].Normal, c, vertices[i].TCoords);
1491 p->tangent_vertices.push_back(vert);
1494 vertex_count = p->vertices.size();
1495 p->vertices.reserve(vertex_count + numVertices);
1496 for (u32 i = 0; i < numVertices; i++) {
1497 video::S3DVertex vert(vertices[i].Pos + pos,
1498 vertices[i].Normal, c, vertices[i].TCoords);
1499 p->vertices.push_back(vert);
1503 p->indices.reserve(p->indices.size() + numIndices);
1504 for (u32 i = 0; i < numIndices; i++) {
1505 u32 j = indices[i] + vertex_count;
1506 p->indices.push_back(j);