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 = 1;
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;
892 continuous_tiles_count++;
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 if(next_is_different)
921 Create a face if there should be one
925 // Floating point conversion of the position vector
926 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
927 // Center point of face (kind of)
928 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
931 if(translate_dir.X != 0) {
932 scale.X = continuous_tiles_count;
934 if(translate_dir.Y != 0) {
935 scale.Y = continuous_tiles_count;
937 if(translate_dir.Z != 0) {
938 scale.Z = continuous_tiles_count;
941 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
942 sp, face_dir_corrected, scale, light_source,
945 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
946 for(int i = 1; i < continuous_tiles_count; i++){
947 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
951 continuous_tiles_count = 1;
954 makes_face = next_makes_face;
955 p_corrected = next_p_corrected;
956 face_dir_corrected = next_face_dir_corrected;
957 lights[0] = next_lights[0];
958 lights[1] = next_lights[1];
959 lights[2] = next_lights[2];
960 lights[3] = next_lights[3];
962 light_source = next_light_source;
967 static void updateAllFastFaceRows(MeshMakeData *data,
968 std::vector<FastFace> &dest)
971 Go through every y,z and get top(y+) faces in rows of x+
973 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
974 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
975 updateFastFaceRow(data,
979 v3s16(0,1,0), //face dir
986 Go through every x,y and get right(x+) faces in rows of z+
988 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
989 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
990 updateFastFaceRow(data,
994 v3s16(1,0,0), //face dir
1001 Go through every y,z and get back(z+) faces in rows of x+
1003 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1004 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1005 updateFastFaceRow(data,
1009 v3s16(0,0,1), //face dir
1020 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1021 m_mesh(new scene::SMesh()),
1022 m_minimap_mapblock(NULL),
1023 m_gamedef(data->m_gamedef),
1024 m_driver(m_gamedef->tsrc()->getDevice()->getVideoDriver()),
1025 m_tsrc(m_gamedef->getTextureSource()),
1026 m_shdrsrc(m_gamedef->getShaderSource()),
1027 m_animation_force_timer(0), // force initial animation
1029 m_crack_materials(),
1030 m_last_daynight_ratio((u32) -1),
1033 m_enable_shaders = data->m_use_shaders;
1034 m_use_tangent_vertices = data->m_use_tangent_vertices;
1035 m_enable_vbo = g_settings->getBool("enable_vbo");
1037 if (g_settings->getBool("enable_minimap")) {
1038 m_minimap_mapblock = new MinimapMapblock;
1039 m_minimap_mapblock->getMinimapNodes(
1040 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1043 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1044 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1045 //TimeTaker timer1("MapBlockMesh()");
1047 std::vector<FastFace> fastfaces_new;
1048 fastfaces_new.reserve(512);
1051 We are including the faces of the trailing edges of the block.
1052 This means that when something changes, the caller must
1053 also update the meshes of the blocks at the leading edges.
1055 NOTE: This is the slowest part of this method.
1058 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1059 //TimeTaker timer2("updateAllFastFaceRows()");
1060 updateAllFastFaceRows(data, fastfaces_new);
1065 Convert FastFaces to MeshCollector
1068 MeshCollector collector(m_use_tangent_vertices);
1071 // avg 0ms (100ms spikes when loading textures the first time)
1072 // (NOTE: probably outdated)
1073 //TimeTaker timer2("MeshCollector building");
1075 for (u32 i = 0; i < fastfaces_new.size(); i++) {
1076 FastFace &f = fastfaces_new[i];
1078 const u16 indices[] = {0,1,2,2,3,0};
1079 const u16 indices_alternate[] = {0,1,3,2,3,1};
1081 if(f.tile.texture == NULL)
1084 const u16 *indices_p = indices;
1087 Revert triangles for nicer looking gradient if vertices
1088 1 and 3 have same color or 0 and 2 have different color.
1089 getRed() is the day color.
1091 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1092 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1093 indices_p = indices_alternate;
1095 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1100 Add special graphics:
1107 mapblock_mesh_generate_special(data, collector);
1110 Convert MeshCollector to SMesh
1113 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1115 PreMeshBuffer &p = collector.prebuffers[i];
1117 // Generate animation data
1119 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1121 // Find the texture name plus ^[crack:N:
1122 std::ostringstream os(std::ios::binary);
1123 os<<m_tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1124 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1125 os<<"o"; // use ^[cracko
1126 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1127 m_crack_materials.insert(std::make_pair(i, os.str()));
1128 // Replace tile texture with the cracked one
1129 p.tile.texture = m_tsrc->getTextureForMesh(
1131 &p.tile.texture_id);
1133 // - Texture animation
1134 if (p.tile.material_flags & MATERIAL_FLAG_ANIMATION) {
1135 // Add to MapBlockMesh in order to animate these tiles
1136 m_animation_tiles[i] = p.tile;
1137 m_animation_frames[i] = 0;
1138 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1139 // Get starting position from noise
1140 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1141 data->m_blockpos.X, data->m_blockpos.Y,
1142 data->m_blockpos.Z, 0));
1144 // Play all synchronized
1145 m_animation_frame_offsets[i] = 0;
1147 // Replace tile texture with the first animation frame
1148 FrameSpec animation_frame = p.tile.frames[0];
1149 p.tile.texture = animation_frame.texture;
1152 u32 vertex_count = m_use_tangent_vertices ?
1153 p.tangent_vertices.size() : p.vertices.size();
1154 for (u32 j = 0; j < vertex_count; j++) {
1157 if (m_use_tangent_vertices) {
1158 vc = &p.tangent_vertices[j].Color;
1159 Normal = &p.tangent_vertices[j].Normal;
1161 vc = &p.vertices[j].Color;
1162 Normal = &p.vertices[j].Normal;
1164 // Note applyFacesShading second parameter is precalculated sqrt
1165 // value for speed improvement
1166 // Skip it for lightsources and top faces.
1167 if (!vc->getBlue()) {
1168 if (Normal->Y < -0.5) {
1169 applyFacesShading(*vc, 0.447213);
1170 } else if (Normal->X > 0.5) {
1171 applyFacesShading(*vc, 0.670820);
1172 } else if (Normal->X < -0.5) {
1173 applyFacesShading(*vc, 0.670820);
1174 } else if (Normal->Z > 0.5) {
1175 applyFacesShading(*vc, 0.836660);
1176 } else if (Normal->Z < -0.5) {
1177 applyFacesShading(*vc, 0.836660);
1180 if (!m_enable_shaders) {
1181 // - Classic lighting (shaders handle this by themselves)
1182 // Set initial real color and store for later updates
1183 u8 day = vc->getRed();
1184 u8 night = vc->getGreen();
1185 finalColorBlend(*vc, day, night, 1000);
1187 m_daynight_diffs[i][j] = std::make_pair(day, night);
1193 video::SMaterial material;
1194 material.setFlag(video::EMF_LIGHTING, false);
1195 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1196 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1197 material.setFlag(video::EMF_FOG_ENABLE, true);
1198 material.setTexture(0, p.tile.texture);
1200 if (m_enable_shaders) {
1201 material.MaterialType = m_shdrsrc->getShaderInfo(p.tile.shader_id).material;
1202 p.tile.applyMaterialOptionsWithShaders(material);
1203 if (p.tile.normal_texture) {
1204 material.setTexture(1, p.tile.normal_texture);
1206 material.setTexture(2, p.tile.flags_texture);
1208 p.tile.applyMaterialOptions(material);
1211 scene::SMesh *mesh = (scene::SMesh *)m_mesh;
1213 // Create meshbuffer, add to mesh
1214 if (m_use_tangent_vertices) {
1215 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1217 buf->Material = material;
1219 mesh->addMeshBuffer(buf);
1222 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1223 &p.indices[0], p.indices.size());
1225 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1227 buf->Material = material;
1229 mesh->addMeshBuffer(buf);
1232 buf->append(&p.vertices[0], p.vertices.size(),
1233 &p.indices[0], p.indices.size());
1238 Do some stuff to the mesh
1240 m_camera_offset = camera_offset;
1241 translateMesh(m_mesh,
1242 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1244 if (m_use_tangent_vertices) {
1245 scene::IMeshManipulator* meshmanip =
1246 m_gamedef->getSceneManager()->getMeshManipulator();
1247 meshmanip->recalculateTangents(m_mesh, true, false, false);
1253 // Usually 1-700 faces and 1-7 materials
1254 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1255 <<"and uses "<<m_mesh->getMeshBufferCount()
1256 <<" materials (meshbuffers)"<<std::endl;
1259 // Use VBO for mesh (this just would set this for ever buffer)
1261 m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1265 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1267 // Check if animation is required for this mesh
1269 !m_crack_materials.empty() ||
1270 !m_daynight_diffs.empty() ||
1271 !m_animation_tiles.empty();
1274 MapBlockMesh::~MapBlockMesh()
1276 if (m_enable_vbo && m_mesh) {
1277 for (u32 i = 0; i < m_mesh->getMeshBufferCount(); i++) {
1278 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i);
1279 m_driver->removeHardwareBuffer(buf);
1284 delete m_minimap_mapblock;
1287 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1289 if(!m_has_animation)
1291 m_animation_force_timer = 100000;
1295 m_animation_force_timer = myrand_range(5, 100);
1298 if(crack != m_last_crack)
1300 for (UNORDERED_MAP<u32, std::string>::iterator i = m_crack_materials.begin();
1301 i != m_crack_materials.end(); ++i) {
1302 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1303 std::string basename = i->second;
1305 // Create new texture name from original
1306 std::ostringstream os;
1307 os<<basename<<crack;
1308 u32 new_texture_id = 0;
1309 video::ITexture *new_texture =
1310 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1311 buf->getMaterial().setTexture(0, new_texture);
1313 // If the current material is also animated,
1314 // update animation info
1315 UNORDERED_MAP<u32, TileSpec>::iterator anim_iter =
1316 m_animation_tiles.find(i->first);
1317 if (anim_iter != m_animation_tiles.end()){
1318 TileSpec &tile = anim_iter->second;
1319 tile.texture = new_texture;
1320 tile.texture_id = new_texture_id;
1321 // force animation update
1322 m_animation_frames[i->first] = -1;
1326 m_last_crack = crack;
1329 // Texture animation
1330 for (UNORDERED_MAP<u32, TileSpec>::iterator i = m_animation_tiles.begin();
1331 i != m_animation_tiles.end(); ++i) {
1332 const TileSpec &tile = i->second;
1333 // Figure out current frame
1334 int frameoffset = m_animation_frame_offsets[i->first];
1335 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1336 + frameoffset) % tile.animation_frame_count;
1337 // If frame doesn't change, skip
1338 if(frame == m_animation_frames[i->first])
1341 m_animation_frames[i->first] = frame;
1343 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1345 FrameSpec animation_frame = tile.frames[frame];
1346 buf->getMaterial().setTexture(0, animation_frame.texture);
1347 if (m_enable_shaders) {
1348 if (animation_frame.normal_texture) {
1349 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1351 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1355 // Day-night transition
1356 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1358 // Force reload mesh to VBO
1362 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1363 i = m_daynight_diffs.begin();
1364 i != m_daynight_diffs.end(); ++i)
1366 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1367 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1368 for(std::map<u32, std::pair<u8, u8 > >::iterator
1369 j = i->second.begin();
1370 j != i->second.end(); ++j)
1372 u8 day = j->second.first;
1373 u8 night = j->second.second;
1374 finalColorBlend(vertices[j->first].Color, day, night, daynight_ratio);
1377 m_last_daynight_ratio = daynight_ratio;
1383 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1385 if (camera_offset != m_camera_offset) {
1386 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
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);