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_driver(m_gamedef->tsrc()->getDevice()->getVideoDriver()),
1030 m_tsrc(m_gamedef->getTextureSource()),
1031 m_shdrsrc(m_gamedef->getShaderSource()),
1032 m_animation_force_timer(0), // force initial animation
1034 m_crack_materials(),
1035 m_last_daynight_ratio((u32) -1),
1038 m_enable_shaders = data->m_use_shaders;
1039 m_use_tangent_vertices = data->m_use_tangent_vertices;
1040 m_enable_vbo = g_settings->getBool("enable_vbo");
1042 if (g_settings->getBool("enable_minimap")) {
1043 m_minimap_mapblock = new MinimapMapblock;
1044 m_minimap_mapblock->getMinimapNodes(
1045 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1048 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1049 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1050 //TimeTaker timer1("MapBlockMesh()");
1052 std::vector<FastFace> fastfaces_new;
1053 fastfaces_new.reserve(512);
1056 We are including the faces of the trailing edges of the block.
1057 This means that when something changes, the caller must
1058 also update the meshes of the blocks at the leading edges.
1060 NOTE: This is the slowest part of this method.
1063 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1064 //TimeTaker timer2("updateAllFastFaceRows()");
1065 updateAllFastFaceRows(data, fastfaces_new);
1070 Convert FastFaces to MeshCollector
1073 MeshCollector collector(m_use_tangent_vertices);
1076 // avg 0ms (100ms spikes when loading textures the first time)
1077 // (NOTE: probably outdated)
1078 //TimeTaker timer2("MeshCollector building");
1080 for (u32 i = 0; i < fastfaces_new.size(); i++) {
1081 FastFace &f = fastfaces_new[i];
1083 const u16 indices[] = {0,1,2,2,3,0};
1084 const u16 indices_alternate[] = {0,1,3,2,3,1};
1086 if(f.tile.texture == NULL)
1089 const u16 *indices_p = indices;
1092 Revert triangles for nicer looking gradient if vertices
1093 1 and 3 have same color or 0 and 2 have different color.
1094 getRed() is the day color.
1096 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1097 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1098 indices_p = indices_alternate;
1100 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1105 Add special graphics:
1112 mapblock_mesh_generate_special(data, collector);
1115 Convert MeshCollector to SMesh
1118 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1120 PreMeshBuffer &p = collector.prebuffers[i];
1122 // Generate animation data
1124 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1126 // Find the texture name plus ^[crack:N:
1127 std::ostringstream os(std::ios::binary);
1128 os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1129 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1130 os<<"o"; // use ^[cracko
1131 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1132 m_crack_materials.insert(std::make_pair(i, os.str()));
1133 // Replace tile texture with the cracked one
1134 p.tile.texture = m_tsrc->getTextureForMesh(
1136 &p.tile.texture_id);
1138 // - Texture animation
1139 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1141 // Add to MapBlockMesh in order to animate these tiles
1142 m_animation_tiles[i] = p.tile;
1143 m_animation_frames[i] = 0;
1144 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1145 // Get starting position from noise
1146 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1147 data->m_blockpos.X, data->m_blockpos.Y,
1148 data->m_blockpos.Z, 0));
1150 // Play all synchronized
1151 m_animation_frame_offsets[i] = 0;
1153 // Replace tile texture with the first animation frame
1154 FrameSpec animation_frame = p.tile.frames[0];
1155 p.tile.texture = animation_frame.texture;
1158 u32 vertex_count = m_use_tangent_vertices ?
1159 p.tangent_vertices.size() : p.vertices.size();
1160 for (u32 j = 0; j < vertex_count; j++) {
1163 if (m_use_tangent_vertices) {
1164 vc = &p.tangent_vertices[j].Color;
1165 Normal = &p.tangent_vertices[j].Normal;
1167 vc = &p.vertices[j].Color;
1168 Normal = &p.vertices[j].Normal;
1170 // Note applyFacesShading second parameter is precalculated sqrt
1171 // value for speed improvement
1172 // Skip it for lightsources and top faces.
1173 if (!vc->getBlue()) {
1174 if (Normal->Y < -0.5) {
1175 applyFacesShading(*vc, 0.447213);
1176 } else if (Normal->X > 0.5) {
1177 applyFacesShading(*vc, 0.670820);
1178 } else if (Normal->X < -0.5) {
1179 applyFacesShading(*vc, 0.670820);
1180 } else if (Normal->Z > 0.5) {
1181 applyFacesShading(*vc, 0.836660);
1182 } else if (Normal->Z < -0.5) {
1183 applyFacesShading(*vc, 0.836660);
1186 if (!m_enable_shaders) {
1187 // - Classic lighting (shaders handle this by themselves)
1188 // Set initial real color and store for later updates
1189 u8 day = vc->getRed();
1190 u8 night = vc->getGreen();
1191 finalColorBlend(*vc, day, night, 1000);
1193 m_daynight_diffs[i][j] = std::make_pair(day, night);
1199 video::SMaterial material;
1200 material.setFlag(video::EMF_LIGHTING, false);
1201 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1202 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1203 material.setFlag(video::EMF_FOG_ENABLE, true);
1204 material.setTexture(0, p.tile.texture);
1206 if (m_enable_shaders) {
1207 material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
1208 p.tile.applyMaterialOptionsWithShaders(material);
1209 if (p.tile.normal_texture) {
1210 material.setTexture(1, p.tile.normal_texture);
1212 material.setTexture(2, p.tile.flags_texture);
1214 p.tile.applyMaterialOptions(material);
1217 scene::SMesh *mesh = (scene::SMesh *)m_mesh;
1219 // Create meshbuffer, add to mesh
1220 if (m_use_tangent_vertices) {
1221 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1223 buf->Material = material;
1225 mesh->addMeshBuffer(buf);
1228 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1229 &p.indices[0], p.indices.size());
1231 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1233 buf->Material = material;
1235 mesh->addMeshBuffer(buf);
1238 buf->append(&p.vertices[0], p.vertices.size(),
1239 &p.indices[0], p.indices.size());
1244 Do some stuff to the mesh
1246 m_camera_offset = camera_offset;
1247 translateMesh(m_mesh,
1248 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1250 if (m_use_tangent_vertices) {
1251 scene::IMeshManipulator* meshmanip =
1252 m_gamedef->getSceneManager()->getMeshManipulator();
1253 meshmanip->recalculateTangents(m_mesh, true, false, false);
1259 // Usually 1-700 faces and 1-7 materials
1260 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1261 <<"and uses "<<m_mesh->getMeshBufferCount()
1262 <<" materials (meshbuffers)"<<std::endl;
1265 // Use VBO for mesh (this just would set this for ever buffer)
1267 m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1271 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1273 // Check if animation is required for this mesh
1275 !m_crack_materials.empty() ||
1276 !m_daynight_diffs.empty() ||
1277 !m_animation_tiles.empty();
1280 MapBlockMesh::~MapBlockMesh()
1282 if (m_enable_vbo && m_mesh) {
1283 for (u32 i = 0; i < m_mesh->getMeshBufferCount(); i++) {
1284 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i);
1285 m_driver->removeHardwareBuffer(buf);
1290 delete m_minimap_mapblock;
1293 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1295 if(!m_has_animation)
1297 m_animation_force_timer = 100000;
1301 m_animation_force_timer = myrand_range(5, 100);
1304 if(crack != m_last_crack)
1306 for(std::map<u32, std::string>::iterator
1307 i = m_crack_materials.begin();
1308 i != m_crack_materials.end(); ++i)
1310 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1311 std::string basename = i->second;
1313 // Create new texture name from original
1314 std::ostringstream os;
1315 os<<basename<<crack;
1316 u32 new_texture_id = 0;
1317 video::ITexture *new_texture =
1318 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1319 buf->getMaterial().setTexture(0, new_texture);
1321 // If the current material is also animated,
1322 // update animation info
1323 std::map<u32, TileSpec>::iterator anim_iter =
1324 m_animation_tiles.find(i->first);
1325 if(anim_iter != m_animation_tiles.end()){
1326 TileSpec &tile = anim_iter->second;
1327 tile.texture = new_texture;
1328 tile.texture_id = new_texture_id;
1329 // force animation update
1330 m_animation_frames[i->first] = -1;
1334 m_last_crack = crack;
1337 // Texture animation
1338 for(std::map<u32, TileSpec>::iterator
1339 i = m_animation_tiles.begin();
1340 i != m_animation_tiles.end(); ++i)
1342 const TileSpec &tile = i->second;
1343 // Figure out current frame
1344 int frameoffset = m_animation_frame_offsets[i->first];
1345 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1346 + frameoffset) % tile.animation_frame_count;
1347 // If frame doesn't change, skip
1348 if(frame == m_animation_frames[i->first])
1351 m_animation_frames[i->first] = frame;
1353 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1355 FrameSpec animation_frame = tile.frames[frame];
1356 buf->getMaterial().setTexture(0, animation_frame.texture);
1357 if (m_enable_shaders) {
1358 if (animation_frame.normal_texture) {
1359 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1361 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1365 // Day-night transition
1366 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1368 // Force reload mesh to VBO
1372 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1373 i = m_daynight_diffs.begin();
1374 i != m_daynight_diffs.end(); ++i)
1376 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1377 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1378 for(std::map<u32, std::pair<u8, u8 > >::iterator
1379 j = i->second.begin();
1380 j != i->second.end(); ++j)
1382 u8 day = j->second.first;
1383 u8 night = j->second.second;
1384 finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
1387 m_last_daynight_ratio = daynight_ratio;
1393 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1395 if (camera_offset != m_camera_offset) {
1396 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1400 m_camera_offset = camera_offset;
1408 void MeshCollector::append(const TileSpec &tile,
1409 const video::S3DVertex *vertices, u32 numVertices,
1410 const u16 *indices, u32 numIndices)
1412 if (numIndices > 65535) {
1413 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1417 PreMeshBuffer *p = NULL;
1418 for (u32 i = 0; i < prebuffers.size(); i++) {
1419 PreMeshBuffer &pp = prebuffers[i];
1420 if (pp.tile != tile)
1422 if (pp.indices.size() + numIndices > 65535)
1432 prebuffers.push_back(pp);
1433 p = &prebuffers[prebuffers.size() - 1];
1437 if (m_use_tangent_vertices) {
1438 vertex_count = p->tangent_vertices.size();
1439 for (u32 i = 0; i < numVertices; i++) {
1440 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1441 vertices[i].Color, vertices[i].TCoords);
1442 p->tangent_vertices.push_back(vert);
1445 vertex_count = p->vertices.size();
1446 for (u32 i = 0; i < numVertices; i++) {
1447 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1448 vertices[i].Color, vertices[i].TCoords);
1449 p->vertices.push_back(vert);
1453 for (u32 i = 0; i < numIndices; i++) {
1454 u32 j = indices[i] + vertex_count;
1455 p->indices.push_back(j);
1460 MeshCollector - for meshnodes and converted drawtypes.
1463 void MeshCollector::append(const TileSpec &tile,
1464 const video::S3DVertex *vertices, u32 numVertices,
1465 const u16 *indices, u32 numIndices,
1466 v3f pos, video::SColor c)
1468 if (numIndices > 65535) {
1469 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1473 PreMeshBuffer *p = NULL;
1474 for (u32 i = 0; i < prebuffers.size(); i++) {
1475 PreMeshBuffer &pp = prebuffers[i];
1478 if(pp.indices.size() + numIndices > 65535)
1488 prebuffers.push_back(pp);
1489 p = &prebuffers[prebuffers.size() - 1];
1493 if (m_use_tangent_vertices) {
1494 vertex_count = p->tangent_vertices.size();
1495 for (u32 i = 0; i < numVertices; i++) {
1496 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1497 vertices[i].Normal, c, vertices[i].TCoords);
1498 p->tangent_vertices.push_back(vert);
1501 vertex_count = p->vertices.size();
1502 for (u32 i = 0; i < numVertices; i++) {
1503 video::S3DVertex vert(vertices[i].Pos + pos,
1504 vertices[i].Normal, c, vertices[i].TCoords);
1505 p->vertices.push_back(vert);
1509 for (u32 i = 0; i < numIndices; i++) {
1510 u32 j = indices[i] + vertex_count;
1511 p->indices.push_back(j);