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
889 && (tile.material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)
890 && (tile.material_flags & MATERIAL_FLAG_TILEABLE_VERTICAL)) {
891 next_is_different = false;
895 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
896 next_makes_face != makes_face ? 1 : 0);
897 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
898 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
899 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
900 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
901 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
902 (next_lights[0] != lights[0] ||
903 next_lights[0] != lights[0] ||
904 next_lights[0] != lights[0] ||
905 next_lights[0] != lights[0]) ? 1 : 0);
906 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
907 !(next_tile == tile) ? 1 : 0);
910 /*g_profiler->add("Meshgen: Total faces checked", 1);
912 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
915 g_profiler->add("Meshgen: diff: last position", 1);*/
918 continuous_tiles_count++;
920 if(next_is_different)
923 Create a face if there should be one
927 // Floating point conversion of the position vector
928 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
929 // Center point of face (kind of)
930 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
931 if(continuous_tiles_count != 1)
932 sp += translate_dir_f;
935 if(translate_dir.X != 0) {
936 scale.X = continuous_tiles_count;
938 if(translate_dir.Y != 0) {
939 scale.Y = continuous_tiles_count;
941 if(translate_dir.Z != 0) {
942 scale.Z = continuous_tiles_count;
945 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
946 sp, face_dir_corrected, scale, light_source,
949 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
950 for(int i = 1; i < continuous_tiles_count; i++){
951 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
955 continuous_tiles_count = 0;
957 makes_face = next_makes_face;
958 p_corrected = next_p_corrected;
959 face_dir_corrected = next_face_dir_corrected;
960 lights[0] = next_lights[0];
961 lights[1] = next_lights[1];
962 lights[2] = next_lights[2];
963 lights[3] = next_lights[3];
965 light_source = next_light_source;
972 static void updateAllFastFaceRows(MeshMakeData *data,
973 std::vector<FastFace> &dest)
976 Go through every y,z and get top(y+) faces in rows of x+
978 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
979 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
980 updateFastFaceRow(data,
984 v3s16(0,1,0), //face dir
991 Go through every x,y and get right(x+) faces in rows of z+
993 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
994 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
995 updateFastFaceRow(data,
999 v3s16(1,0,0), //face dir
1006 Go through every y,z and get back(z+) faces in rows of x+
1008 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1009 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1010 updateFastFaceRow(data,
1014 v3s16(0,0,1), //face dir
1025 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1026 m_mesh(new scene::SMesh()),
1027 m_minimap_mapblock(NULL),
1028 m_gamedef(data->m_gamedef),
1029 m_tsrc(m_gamedef->getTextureSource()),
1030 m_shdrsrc(m_gamedef->getShaderSource()),
1031 m_animation_force_timer(0), // force initial animation
1033 m_crack_materials(),
1034 m_last_daynight_ratio((u32) -1),
1037 m_enable_shaders = data->m_use_shaders;
1038 m_use_tangent_vertices = data->m_use_tangent_vertices;
1040 if (g_settings->getBool("enable_minimap")) {
1041 m_minimap_mapblock = new MinimapMapblock;
1042 m_minimap_mapblock->getMinimapNodes(
1043 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1046 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1047 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1048 //TimeTaker timer1("MapBlockMesh()");
1050 std::vector<FastFace> fastfaces_new;
1051 fastfaces_new.reserve(512);
1054 We are including the faces of the trailing edges of the block.
1055 This means that when something changes, the caller must
1056 also update the meshes of the blocks at the leading edges.
1058 NOTE: This is the slowest part of this method.
1061 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1062 //TimeTaker timer2("updateAllFastFaceRows()");
1063 updateAllFastFaceRows(data, fastfaces_new);
1068 Convert FastFaces to MeshCollector
1071 MeshCollector collector(m_use_tangent_vertices);
1074 // avg 0ms (100ms spikes when loading textures the first time)
1075 // (NOTE: probably outdated)
1076 //TimeTaker timer2("MeshCollector building");
1078 for (u32 i = 0; i < fastfaces_new.size(); i++) {
1079 FastFace &f = fastfaces_new[i];
1081 const u16 indices[] = {0,1,2,2,3,0};
1082 const u16 indices_alternate[] = {0,1,3,2,3,1};
1084 if(f.tile.texture == NULL)
1087 const u16 *indices_p = indices;
1090 Revert triangles for nicer looking gradient if vertices
1091 1 and 3 have same color or 0 and 2 have different color.
1092 getRed() is the day color.
1094 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1095 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1096 indices_p = indices_alternate;
1098 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1103 Add special graphics:
1110 mapblock_mesh_generate_special(data, collector);
1113 Convert MeshCollector to SMesh
1116 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1118 PreMeshBuffer &p = collector.prebuffers[i];
1120 // Generate animation data
1122 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1124 // Find the texture name plus ^[crack:N:
1125 std::ostringstream os(std::ios::binary);
1126 os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1127 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1128 os<<"o"; // use ^[cracko
1129 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1130 m_crack_materials.insert(std::make_pair(i, os.str()));
1131 // Replace tile texture with the cracked one
1132 p.tile.texture = m_tsrc->getTextureForMesh(
1134 &p.tile.texture_id);
1136 // - Texture animation
1137 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1139 // Add to MapBlockMesh in order to animate these tiles
1140 m_animation_tiles[i] = p.tile;
1141 m_animation_frames[i] = 0;
1142 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1143 // Get starting position from noise
1144 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1145 data->m_blockpos.X, data->m_blockpos.Y,
1146 data->m_blockpos.Z, 0));
1148 // Play all synchronized
1149 m_animation_frame_offsets[i] = 0;
1151 // Replace tile texture with the first animation frame
1152 FrameSpec animation_frame = p.tile.frames[0];
1153 p.tile.texture = animation_frame.texture;
1156 u32 vertex_count = m_use_tangent_vertices ?
1157 p.tangent_vertices.size() : p.vertices.size();
1158 for (u32 j = 0; j < vertex_count; j++) {
1161 if (m_use_tangent_vertices) {
1162 vc = &p.tangent_vertices[j].Color;
1163 Normal = &p.tangent_vertices[j].Normal;
1165 vc = &p.vertices[j].Color;
1166 Normal = &p.vertices[j].Normal;
1168 // Note applyFacesShading second parameter is precalculated sqrt
1169 // value for speed improvement
1170 // Skip it for lightsources and top faces.
1171 if (!vc->getBlue()) {
1172 if (Normal->Y < -0.5) {
1173 applyFacesShading(*vc, 0.447213);
1174 } else if (Normal->X > 0.5) {
1175 applyFacesShading(*vc, 0.670820);
1176 } else if (Normal->X < -0.5) {
1177 applyFacesShading(*vc, 0.670820);
1178 } else if (Normal->Z > 0.5) {
1179 applyFacesShading(*vc, 0.836660);
1180 } else if (Normal->Z < -0.5) {
1181 applyFacesShading(*vc, 0.836660);
1184 if (!m_enable_shaders) {
1185 // - Classic lighting (shaders handle this by themselves)
1186 // Set initial real color and store for later updates
1187 u8 day = vc->getRed();
1188 u8 night = vc->getGreen();
1189 finalColorBlend(*vc, day, night, 1000);
1191 m_daynight_diffs[i][j] = std::make_pair(day, night);
1197 video::SMaterial material;
1198 material.setFlag(video::EMF_LIGHTING, false);
1199 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1200 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1201 material.setFlag(video::EMF_FOG_ENABLE, true);
1202 material.setTexture(0, p.tile.texture);
1204 if (m_enable_shaders) {
1205 material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
1206 p.tile.applyMaterialOptionsWithShaders(material);
1207 if (p.tile.normal_texture) {
1208 material.setTexture(1, p.tile.normal_texture);
1210 material.setTexture(2, p.tile.flags_texture);
1212 p.tile.applyMaterialOptions(material);
1215 scene::SMesh *mesh = (scene::SMesh *)m_mesh;
1217 // Create meshbuffer, add to mesh
1218 if (m_use_tangent_vertices) {
1219 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1221 buf->Material = material;
1223 mesh->addMeshBuffer(buf);
1226 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1227 &p.indices[0], p.indices.size());
1229 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1231 buf->Material = material;
1233 mesh->addMeshBuffer(buf);
1236 buf->append(&p.vertices[0], p.vertices.size(),
1237 &p.indices[0], p.indices.size());
1242 Do some stuff to the mesh
1244 m_camera_offset = camera_offset;
1245 translateMesh(m_mesh,
1246 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1248 if (m_use_tangent_vertices) {
1249 scene::IMeshManipulator* meshmanip =
1250 m_gamedef->getSceneManager()->getMeshManipulator();
1251 meshmanip->recalculateTangents(m_mesh, true, false, false);
1257 // Usually 1-700 faces and 1-7 materials
1258 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1259 <<"and uses "<<m_mesh->getMeshBufferCount()
1260 <<" materials (meshbuffers)"<<std::endl;
1263 // Use VBO for mesh (this just would set this for ever buffer)
1264 // This will lead to infinite memory usage because or irrlicht.
1265 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1268 NOTE: If that is enabled, some kind of a queue to the main
1269 thread should be made which would call irrlicht to delete
1270 the hardware buffer and then delete the mesh
1274 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1276 // Check if animation is required for this mesh
1278 !m_crack_materials.empty() ||
1279 !m_daynight_diffs.empty() ||
1280 !m_animation_tiles.empty();
1283 MapBlockMesh::~MapBlockMesh()
1287 delete m_minimap_mapblock;
1290 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1292 if(!m_has_animation)
1294 m_animation_force_timer = 100000;
1298 m_animation_force_timer = myrand_range(5, 100);
1301 if(crack != m_last_crack)
1303 for(std::map<u32, std::string>::iterator
1304 i = m_crack_materials.begin();
1305 i != m_crack_materials.end(); ++i)
1307 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1308 std::string basename = i->second;
1310 // Create new texture name from original
1311 std::ostringstream os;
1312 os<<basename<<crack;
1313 u32 new_texture_id = 0;
1314 video::ITexture *new_texture =
1315 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1316 buf->getMaterial().setTexture(0, new_texture);
1318 // If the current material is also animated,
1319 // update animation info
1320 std::map<u32, TileSpec>::iterator anim_iter =
1321 m_animation_tiles.find(i->first);
1322 if(anim_iter != m_animation_tiles.end()){
1323 TileSpec &tile = anim_iter->second;
1324 tile.texture = new_texture;
1325 tile.texture_id = new_texture_id;
1326 // force animation update
1327 m_animation_frames[i->first] = -1;
1331 m_last_crack = crack;
1334 // Texture animation
1335 for(std::map<u32, TileSpec>::iterator
1336 i = m_animation_tiles.begin();
1337 i != m_animation_tiles.end(); ++i)
1339 const TileSpec &tile = i->second;
1340 // Figure out current frame
1341 int frameoffset = m_animation_frame_offsets[i->first];
1342 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1343 + frameoffset) % tile.animation_frame_count;
1344 // If frame doesn't change, skip
1345 if(frame == m_animation_frames[i->first])
1348 m_animation_frames[i->first] = frame;
1350 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1352 FrameSpec animation_frame = tile.frames[frame];
1353 buf->getMaterial().setTexture(0, animation_frame.texture);
1354 if (m_enable_shaders) {
1355 if (animation_frame.normal_texture) {
1356 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1358 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1362 // Day-night transition
1363 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1365 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1366 i = m_daynight_diffs.begin();
1367 i != m_daynight_diffs.end(); ++i)
1369 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1370 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1371 for(std::map<u32, std::pair<u8, u8 > >::iterator
1372 j = i->second.begin();
1373 j != i->second.end(); ++j)
1375 u8 day = j->second.first;
1376 u8 night = j->second.second;
1377 finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
1380 m_last_daynight_ratio = daynight_ratio;
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) {
1403 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1407 PreMeshBuffer *p = NULL;
1408 for (u32 i = 0; i < prebuffers.size(); i++) {
1409 PreMeshBuffer &pp = prebuffers[i];
1410 if (pp.tile != tile)
1412 if (pp.indices.size() + numIndices > 65535)
1422 prebuffers.push_back(pp);
1423 p = &prebuffers[prebuffers.size() - 1];
1427 if (m_use_tangent_vertices) {
1428 vertex_count = p->tangent_vertices.size();
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 for (u32 i = 0; i < numVertices; i++) {
1437 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1438 vertices[i].Color, vertices[i].TCoords);
1439 p->vertices.push_back(vert);
1443 for (u32 i = 0; i < numIndices; i++) {
1444 u32 j = indices[i] + vertex_count;
1445 p->indices.push_back(j);
1450 MeshCollector - for meshnodes and converted drawtypes.
1453 void MeshCollector::append(const TileSpec &tile,
1454 const video::S3DVertex *vertices, u32 numVertices,
1455 const u16 *indices, u32 numIndices,
1456 v3f pos, video::SColor c)
1458 if (numIndices > 65535) {
1459 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1463 PreMeshBuffer *p = NULL;
1464 for (u32 i = 0; i < prebuffers.size(); i++) {
1465 PreMeshBuffer &pp = prebuffers[i];
1468 if(pp.indices.size() + numIndices > 65535)
1478 prebuffers.push_back(pp);
1479 p = &prebuffers[prebuffers.size() - 1];
1483 if (m_use_tangent_vertices) {
1484 vertex_count = p->tangent_vertices.size();
1485 for (u32 i = 0; i < numVertices; i++) {
1486 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1487 vertices[i].Normal, c, vertices[i].TCoords);
1488 p->tangent_vertices.push_back(vert);
1491 vertex_count = p->vertices.size();
1492 for (u32 i = 0; i < numVertices; i++) {
1493 video::S3DVertex vert(vertices[i].Pos + pos,
1494 vertices[i].Normal, c, vertices[i].TCoords);
1495 p->vertices.push_back(vert);
1499 for (u32 i = 0; i < numIndices; i++) {
1500 u32 j = indices[i] + vertex_count;
1501 p->indices.push_back(j);