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"
28 #include "content_mapblock.h"
29 #include "util/directiontables.h"
30 #include "client/meshgen/collector.h"
31 #include "client/renderingengine.h"
38 MeshMakeData::MeshMakeData(Client *client, bool use_shaders,
39 bool use_tangent_vertices):
41 m_use_shaders(use_shaders),
42 m_use_tangent_vertices(use_tangent_vertices)
45 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
47 m_blockpos = blockpos;
49 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
52 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
53 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
54 m_vmanip.addArea(voxel_area);
57 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
59 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
60 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
62 v3s16 bp = m_blockpos + block_offset;
63 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
64 m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
67 void MeshMakeData::fill(MapBlock *block)
69 fillBlockDataBegin(block->getPos());
71 fillBlockData(v3s16(0,0,0), block->getData());
73 // Get map for reading neighbor blocks
74 Map *map = block->getParent();
76 for (const v3s16 &dir : g_26dirs) {
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
80 fillBlockData(dir, b->getData());
84 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
87 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
90 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
92 m_smooth_lighting = smooth_lighting;
96 Light and vertex color functions
100 Calculate non-smooth lighting at interior of node.
103 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
104 const NodeDefManager *ndef)
106 u8 light = n.getLight(bank, ndef);
108 light = rangelim(light + increment, 0, LIGHT_SUN);
109 return decode_light(light);
113 Calculate non-smooth lighting at interior of node.
116 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
118 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
119 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
120 return day | (night << 8);
124 Calculate non-smooth lighting at face of node.
127 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
128 v3s16 face_dir, const NodeDefManager *ndef)
131 u8 l1 = n.getLight(bank, ndef);
132 u8 l2 = n2.getLight(bank, ndef);
138 // Boost light level for light sources
139 u8 light_source = MYMAX(ndef->get(n).light_source,
140 ndef->get(n2).light_source);
141 if(light_source > light)
142 light = light_source;
144 return decode_light(light);
148 Calculate non-smooth lighting at face of node.
151 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
152 const NodeDefManager *ndef)
154 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
155 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
156 return day | (night << 8);
160 Calculate smooth lighting at the XYZ- corner of p.
163 static u16 getSmoothLightCombined(const v3s16 &p,
164 const std::array<v3s16,8> &dirs, MeshMakeData *data)
166 const NodeDefManager *ndef = data->m_client->ndef();
168 u16 ambient_occlusion = 0;
170 u8 light_source_max = 0;
173 bool direct_sunlight = false;
175 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
180 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
181 if (n.getContent() == CONTENT_IGNORE)
183 const ContentFeatures &f = ndef->get(n);
184 if (f.light_source > light_source_max)
185 light_source_max = f.light_source;
186 // Check f.solidness because fast-style leaves look better this way
187 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
188 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
189 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
190 if (light_level_day == LIGHT_SUN)
191 direct_sunlight = true;
192 light_day += decode_light(light_level_day);
193 light_night += decode_light(light_level_night);
198 return f.light_propagates;
201 bool obstructed[4] = { true, true, true, true };
203 bool opaque1 = !add_node(1);
204 bool opaque2 = !add_node(2);
205 bool opaque3 = !add_node(3);
206 obstructed[0] = opaque1 && opaque2;
207 obstructed[1] = opaque1 && opaque3;
208 obstructed[2] = opaque2 && opaque3;
209 for (u8 k = 0; k < 3; ++k)
210 if (add_node(k + 4, obstructed[k]))
211 obstructed[3] = false;
212 if (add_node(7, obstructed[3])) { // wrap light around nodes
213 ambient_occlusion -= 3;
214 for (u8 k = 0; k < 3; ++k)
215 add_node(k + 4, !obstructed[k]);
218 if (light_count == 0) {
219 light_day = light_night = 0;
221 light_day /= light_count;
222 light_night /= light_count;
225 // boost direct sunlight, if any
229 // Boost brightness around light sources
230 bool skip_ambient_occlusion_day = false;
231 if (decode_light(light_source_max) >= light_day) {
232 light_day = decode_light(light_source_max);
233 skip_ambient_occlusion_day = true;
236 bool skip_ambient_occlusion_night = false;
237 if(decode_light(light_source_max) >= light_night) {
238 light_night = decode_light(light_source_max);
239 skip_ambient_occlusion_night = true;
242 if (ambient_occlusion > 4) {
243 static thread_local const float ao_gamma = rangelim(
244 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
246 // Table of gamma space multiply factors.
247 static thread_local const float light_amount[3] = {
248 powf(0.75, 1.0 / ao_gamma),
249 powf(0.5, 1.0 / ao_gamma),
250 powf(0.25, 1.0 / ao_gamma)
253 //calculate table index for gamma space multiplier
254 ambient_occlusion -= 5;
256 if (!skip_ambient_occlusion_day)
257 light_day = rangelim(core::round32(
258 light_day * light_amount[ambient_occlusion]), 0, 255);
259 if (!skip_ambient_occlusion_night)
260 light_night = rangelim(core::round32(
261 light_night * light_amount[ambient_occlusion]), 0, 255);
264 return light_day | (light_night << 8);
268 Calculate smooth lighting at the given corner of p.
270 Node at p is solid, and thus the lighting is face-dependent.
272 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
274 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
278 Calculate smooth lighting at the given corner of p.
280 Node at p is not solid, and the lighting is not face-dependent.
282 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
284 const std::array<v3s16,8> dirs = {{
285 // Always shine light
292 v3s16(corner.X,corner.Y,0),
293 v3s16(corner.X,0,corner.Z),
294 v3s16(0,corner.Y,corner.Z),
295 v3s16(corner.X,corner.Y,corner.Z)
297 return getSmoothLightCombined(p, dirs, data);
300 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
301 f32 rg = daynight_ratio / 1000.0f - 0.04f;
302 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
308 void final_color_blend(video::SColor *result,
309 u16 light, u32 daynight_ratio)
311 video::SColorf dayLight;
312 get_sunlight_color(&dayLight, daynight_ratio);
313 final_color_blend(result,
314 encode_light(light, 0), dayLight);
317 void final_color_blend(video::SColor *result,
318 const video::SColor &data, const video::SColorf &dayLight)
320 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
322 video::SColorf c(data);
325 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
326 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
327 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
329 // Emphase blue a bit in darker places
330 // Each entry of this array represents a range of 8 blue levels
331 static const u8 emphase_blue_when_dark[32] = {
332 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
333 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
336 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
337 0, 255) / 8] / 255.0f;
339 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
340 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
341 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
345 Mesh generation helpers
348 // This table is moved outside getNodeVertexDirs to avoid the compiler using
349 // a mutex to initialize this table at runtime right in the hot path.
350 // For details search the internet for "cxa_guard_acquire".
351 static const v3s16 vertex_dirs_table[] = {
353 v3s16( 1,-1, 1), v3s16( 1,-1,-1),
354 v3s16( 1, 1,-1), v3s16( 1, 1, 1),
356 v3s16( 1, 1,-1), v3s16(-1, 1,-1),
357 v3s16(-1, 1, 1), v3s16( 1, 1, 1),
359 v3s16(-1,-1, 1), v3s16( 1,-1, 1),
360 v3s16( 1, 1, 1), v3s16(-1, 1, 1),
362 v3s16(), v3s16(), v3s16(), v3s16(),
364 v3s16( 1,-1,-1), v3s16(-1,-1,-1),
365 v3s16(-1, 1,-1), v3s16( 1, 1,-1),
367 v3s16( 1,-1, 1), v3s16(-1,-1, 1),
368 v3s16(-1,-1,-1), v3s16( 1,-1,-1),
370 v3s16(-1,-1,-1), v3s16(-1,-1, 1),
371 v3s16(-1, 1, 1), v3s16(-1, 1,-1)
375 vertex_dirs: v3s16[4]
377 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
380 If looked from outside the node towards the face, the corners are:
387 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
389 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
391 // Convert direction to single integer for table lookup
392 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
395 #if defined(__GNUC__) && !defined(__clang__)
396 #pragma GCC diagnostic push
397 #pragma GCC diagnostic ignored "-Wclass-memaccess"
399 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
400 #if defined(__GNUC__) && !defined(__clang__)
401 #pragma GCC diagnostic pop
405 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
407 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
409 if (dir == v3s16(0,0,1)) {
412 } else if (dir == v3s16(0,0,-1)) {
415 } else if (dir == v3s16(1,0,0)) {
418 } else if (dir == v3s16(-1,0,0)) {
421 } else if (dir == v3s16(0,1,0)) {
424 } else if (dir == v3s16(0,-1,0)) {
433 video::S3DVertex vertices[4]; // Precalculated vertices
435 * The face is divided into two triangles. If this is true,
436 * vertices 0 and 2 are connected, othervise vertices 1 and 3
439 bool vertex_0_2_connected;
442 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
443 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
445 // Position is at the center of the cube.
454 v3s16 vertex_dirs[4];
455 getNodeVertexDirs(dir, vertex_dirs);
456 if (tile.world_aligned)
457 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
461 switch (tile.rotation) {
466 vertex_dirs[0] = vertex_dirs[3];
467 vertex_dirs[3] = vertex_dirs[2];
468 vertex_dirs[2] = vertex_dirs[1];
478 vertex_dirs[0] = vertex_dirs[2];
481 vertex_dirs[1] = vertex_dirs[3];
492 vertex_dirs[0] = vertex_dirs[1];
493 vertex_dirs[1] = vertex_dirs[2];
494 vertex_dirs[2] = vertex_dirs[3];
504 vertex_dirs[0] = vertex_dirs[3];
505 vertex_dirs[3] = vertex_dirs[2];
506 vertex_dirs[2] = vertex_dirs[1];
518 vertex_dirs[0] = vertex_dirs[1];
519 vertex_dirs[1] = vertex_dirs[2];
520 vertex_dirs[2] = vertex_dirs[3];
532 vertex_dirs[0] = vertex_dirs[3];
533 vertex_dirs[3] = vertex_dirs[2];
534 vertex_dirs[2] = vertex_dirs[1];
546 vertex_dirs[0] = vertex_dirs[1];
547 vertex_dirs[1] = vertex_dirs[2];
548 vertex_dirs[2] = vertex_dirs[3];
570 for (u16 i = 0; i < 4; i++) {
572 BS / 2 * vertex_dirs[i].X,
573 BS / 2 * vertex_dirs[i].Y,
574 BS / 2 * vertex_dirs[i].Z
578 for (v3f &vpos : vertex_pos) {
585 f32 abs_scale = 1.0f;
586 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
587 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
588 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
590 v3f normal(dir.X, dir.Y, dir.Z);
592 u16 li[4] = { li0, li1, li2, li3 };
596 for (u8 i = 0; i < 4; i++) {
598 night[i] = li[i] & 0xFF;
601 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
602 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
605 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
606 core::vector2d<f32>(x0, y0 + h),
607 core::vector2d<f32>(x0, y0),
608 core::vector2d<f32>(x0 + w * abs_scale, y0) };
610 // equivalent to dest.push_back(FastFace()) but faster
612 FastFace& face = *dest.rbegin();
614 for (u8 i = 0; i < 4; i++) {
615 video::SColor c = encode_light(li[i], tile.emissive_light);
616 if (!tile.emissive_light)
617 applyFacesShading(c, normal);
619 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
623 Revert triangles for nicer looking gradient if the
624 brightness of vertices 1 and 3 differ less than
625 the brightness of vertices 0 and 2.
627 face.vertex_0_2_connected = vertex_0_2_connected;
632 Nodes make a face if contents differ and solidness differs.
635 1: Face uses m1's content
636 2: Face uses m2's content
637 equivalent: Whether the blocks share the same face (eg. water and glass)
639 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
641 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
642 const NodeDefManager *ndef)
646 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
649 const ContentFeatures &f1 = ndef->get(m1);
650 const ContentFeatures &f2 = ndef->get(m2);
652 // Contents don't differ for different forms of same liquid
653 if (f1.sameLiquid(f2))
656 u8 c1 = f1.solidness;
657 u8 c2 = f2.solidness;
663 c1 = f1.visual_solidness;
665 c2 = f2.visual_solidness;
669 // If same solidness, liquid takes precense
683 Gets nth node tile (0 <= n <= 5).
685 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
687 const NodeDefManager *ndef = data->m_client->ndef();
688 const ContentFeatures &f = ndef->get(mn);
689 tile = f.tiles[tileindex];
690 bool has_crack = p == data->m_crack_pos_relative;
691 for (TileLayer &layer : tile.layers) {
692 if (layer.texture_id == 0)
694 if (!layer.has_color)
695 mn.getColor(f, &(layer.color));
696 // Apply temporary crack
698 layer.material_flags |= MATERIAL_FLAG_CRACK;
703 Gets node tile given a face direction.
705 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
707 const NodeDefManager *ndef = data->m_client->ndef();
709 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
710 // (0,0,1), (0,0,-1) or (0,0,0)
711 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
713 // Convert direction to single integer for table lookup
718 // 4 = invalid, treat as (0,0,0)
722 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
724 // Get rotation for things like chests
725 u8 facedir = mn.getFaceDir(ndef, true);
727 static const u16 dir_to_tile[24 * 16] =
729 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
730 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
731 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
732 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
733 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
735 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
736 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
737 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
738 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
740 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
741 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
742 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
743 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
745 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
746 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
747 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
748 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
750 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
751 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
752 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
753 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
755 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
756 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
757 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
758 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
761 u16 tile_index = facedir * 16 + dir_i;
762 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
763 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
766 static void getTileInfo(
770 const v3s16 &face_dir,
774 v3s16 &face_dir_corrected,
780 VoxelManipulator &vmanip = data->m_vmanip;
781 const NodeDefManager *ndef = data->m_client->ndef();
782 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
784 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
786 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
787 if (n0.getContent() == CONTENT_IGNORE) {
792 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
794 if (n1.getContent() == CONTENT_IGNORE) {
800 bool equivalent = false;
801 u8 mf = face_contents(n0.getContent(), n1.getContent(),
815 face_dir_corrected = face_dir;
818 p_corrected = p + face_dir;
819 face_dir_corrected = -face_dir;
822 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
823 const ContentFeatures &f = ndef->get(n);
825 tile.emissive_light = f.light_source;
827 // eg. water and glass
829 for (TileLayer &layer : tile.layers)
830 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
833 if (!data->m_smooth_lighting) {
834 lights[0] = lights[1] = lights[2] = lights[3] =
835 getFaceLight(n0, n1, face_dir, ndef);
837 v3s16 vertex_dirs[4];
838 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
840 v3s16 light_p = blockpos_nodes + p_corrected;
841 for (u16 i = 0; i < 4; i++)
842 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
848 translate_dir: unit vector with only one of x, y or z
849 face_dir: unit vector with only one of x, y or z
851 static void updateFastFaceRow(
853 const v3s16 &&startpos,
855 const v3f &&translate_dir_f,
856 const v3s16 &&face_dir,
857 std::vector<FastFace> &dest)
859 static thread_local const bool waving_liquids =
860 g_settings->getBool("enable_shaders") &&
861 g_settings->getBool("enable_waving_water");
865 u16 continuous_tiles_count = 1;
867 bool makes_face = false;
869 v3s16 face_dir_corrected;
870 u16 lights[4] = {0, 0, 0, 0};
874 // Get info of first tile
875 getTileInfo(data, p, face_dir,
876 makes_face, p_corrected, face_dir_corrected,
877 lights, waving, tile);
879 // Unroll this variable which has a significant build cost
881 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
882 // If tiling can be done, this is set to false in the next step
883 bool next_is_different = true;
885 bool next_makes_face = false;
886 v3s16 next_p_corrected;
887 v3s16 next_face_dir_corrected;
888 u16 next_lights[4] = {0, 0, 0, 0};
890 // If at last position, there is nothing to compare to and
891 // the face must be drawn anyway
892 if (j != MAP_BLOCKSIZE - 1) {
895 getTileInfo(data, p, face_dir,
896 next_makes_face, next_p_corrected,
897 next_face_dir_corrected, next_lights,
901 if (next_makes_face == makes_face
902 && next_p_corrected == p_corrected + translate_dir
903 && next_face_dir_corrected == face_dir_corrected
904 && memcmp(next_lights, lights, sizeof(lights)) == 0
905 // Don't apply fast faces to waving water.
906 && (waving != 3 || !waving_liquids)
907 && next_tile.isTileable(tile)) {
908 next_is_different = false;
909 continuous_tiles_count++;
912 if (next_is_different) {
914 Create a face if there should be one
917 // Floating point conversion of the position vector
918 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
919 // Center point of face (kind of)
920 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
924 if (translate_dir.X != 0)
925 scale.X = continuous_tiles_count;
926 if (translate_dir.Y != 0)
927 scale.Y = continuous_tiles_count;
928 if (translate_dir.Z != 0)
929 scale.Z = continuous_tiles_count;
931 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
932 pf, sp, face_dir_corrected, scale, dest);
933 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
936 continuous_tiles_count = 1;
939 makes_face = next_makes_face;
940 p_corrected = next_p_corrected;
941 face_dir_corrected = next_face_dir_corrected;
942 memcpy(lights, next_lights, sizeof(lights));
943 if (next_is_different)
944 tile = std::move(next_tile); // faster than copy
948 static void updateAllFastFaceRows(MeshMakeData *data,
949 std::vector<FastFace> &dest)
952 Go through every y,z and get top(y+) faces in rows of x+
954 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
955 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
956 updateFastFaceRow(data,
958 v3s16(1, 0, 0), //dir
960 v3s16(0, 1, 0), //face dir
964 Go through every x,y and get right(x+) faces in rows of z+
966 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
967 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
968 updateFastFaceRow(data,
970 v3s16(0, 0, 1), //dir
972 v3s16(1, 0, 0), //face dir
976 Go through every y,z and get back(z+) faces in rows of x+
978 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
979 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
980 updateFastFaceRow(data,
982 v3s16(1, 0, 0), //dir
984 v3s16(0, 0, 1), //face dir
988 static void applyTileColor(PreMeshBuffer &pmb)
990 video::SColor tc = pmb.layer.color;
991 if (tc == video::SColor(0xFFFFFFFF))
993 for (video::S3DVertex &vertex : pmb.vertices) {
994 video::SColor *c = &vertex.Color;
995 c->set(c->getAlpha(),
996 c->getRed() * tc.getRed() / 255,
997 c->getGreen() * tc.getGreen() / 255,
998 c->getBlue() * tc.getBlue() / 255);
1006 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1007 m_minimap_mapblock(NULL),
1008 m_tsrc(data->m_client->getTextureSource()),
1009 m_shdrsrc(data->m_client->getShaderSource()),
1010 m_animation_force_timer(0), // force initial animation
1012 m_last_daynight_ratio((u32) -1)
1014 for (auto &m : m_mesh)
1015 m = new scene::SMesh();
1016 m_enable_shaders = data->m_use_shaders;
1017 m_use_tangent_vertices = data->m_use_tangent_vertices;
1018 m_enable_vbo = g_settings->getBool("enable_vbo");
1020 if (data->m_client->getMinimap()) {
1021 m_minimap_mapblock = new MinimapMapblock;
1022 m_minimap_mapblock->getMinimapNodes(
1023 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1026 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1027 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1028 //TimeTaker timer1("MapBlockMesh()");
1030 std::vector<FastFace> fastfaces_new;
1031 fastfaces_new.reserve(512);
1034 We are including the faces of the trailing edges of the block.
1035 This means that when something changes, the caller must
1036 also update the meshes of the blocks at the leading edges.
1038 NOTE: This is the slowest part of this method.
1041 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1042 //TimeTaker timer2("updateAllFastFaceRows()");
1043 updateAllFastFaceRows(data, fastfaces_new);
1048 Convert FastFaces to MeshCollector
1051 MeshCollector collector;
1054 // avg 0ms (100ms spikes when loading textures the first time)
1055 // (NOTE: probably outdated)
1056 //TimeTaker timer2("MeshCollector building");
1058 for (const FastFace &f : fastfaces_new) {
1059 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1060 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1061 const u16 *indices_p =
1062 f.vertex_0_2_connected ? indices : indices_alternate;
1063 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1068 Add special graphics:
1076 MapblockMeshGenerator generator(data, &collector);
1077 generator.generate();
1081 Convert MeshCollector to SMesh
1084 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1085 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1087 PreMeshBuffer &p = collector.prebuffers[layer][i];
1091 // Generate animation data
1093 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1094 // Find the texture name plus ^[crack:N:
1095 std::ostringstream os(std::ios::binary);
1096 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1097 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1098 os << "o"; // use ^[cracko
1099 u8 tiles = p.layer.scale;
1101 os << ":" << (u32)tiles;
1102 os << ":" << (u32)p.layer.animation_frame_count << ":";
1103 m_crack_materials.insert(std::make_pair(
1104 std::pair<u8, u32>(layer, i), os.str()));
1105 // Replace tile texture with the cracked one
1106 p.layer.texture = m_tsrc->getTextureForMesh(
1108 &p.layer.texture_id);
1110 // - Texture animation
1111 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1112 // Add to MapBlockMesh in order to animate these tiles
1113 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1114 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1115 if (g_settings->getBool(
1116 "desynchronize_mapblock_texture_animation")) {
1117 // Get starting position from noise
1118 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1119 100000 * (2.0 + noise3d(
1120 data->m_blockpos.X, data->m_blockpos.Y,
1121 data->m_blockpos.Z, 0));
1123 // Play all synchronized
1124 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1126 // Replace tile texture with the first animation frame
1127 p.layer.texture = (*p.layer.frames)[0].texture;
1130 if (!m_enable_shaders) {
1131 // Extract colors for day-night animation
1132 // Dummy sunlight to handle non-sunlit areas
1133 video::SColorf sunlight;
1134 get_sunlight_color(&sunlight, 0);
1135 u32 vertex_count = p.vertices.size();
1136 for (u32 j = 0; j < vertex_count; j++) {
1137 video::SColor *vc = &p.vertices[j].Color;
1138 video::SColor copy = *vc;
1139 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1140 final_color_blend(vc, copy, sunlight); // Finalize color
1141 else // Record color to animate
1142 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1144 // The sunlight ratio has been stored,
1145 // delete alpha (for the final rendering).
1151 video::SMaterial material;
1152 material.setFlag(video::EMF_LIGHTING, false);
1153 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1154 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1155 material.setFlag(video::EMF_FOG_ENABLE, true);
1156 material.setTexture(0, p.layer.texture);
1158 if (m_enable_shaders) {
1159 material.MaterialType = m_shdrsrc->getShaderInfo(
1160 p.layer.shader_id).material;
1161 p.layer.applyMaterialOptionsWithShaders(material);
1162 if (p.layer.normal_texture)
1163 material.setTexture(1, p.layer.normal_texture);
1164 material.setTexture(2, p.layer.flags_texture);
1166 p.layer.applyMaterialOptions(material);
1169 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1171 // Create meshbuffer, add to mesh
1172 if (m_use_tangent_vertices) {
1173 scene::SMeshBufferTangents *buf =
1174 new scene::SMeshBufferTangents();
1175 buf->Material = material;
1176 buf->Vertices.reallocate(p.vertices.size());
1177 buf->Indices.reallocate(p.indices.size());
1178 for (const video::S3DVertex &v: p.vertices)
1179 buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
1180 for (u16 i: p.indices)
1181 buf->Indices.push_back(i);
1182 buf->recalculateBoundingBox();
1183 mesh->addMeshBuffer(buf);
1186 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1187 buf->Material = material;
1188 buf->append(&p.vertices[0], p.vertices.size(),
1189 &p.indices[0], p.indices.size());
1190 mesh->addMeshBuffer(buf);
1196 Do some stuff to the mesh
1198 m_camera_offset = camera_offset;
1199 translateMesh(m_mesh[layer],
1200 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1202 if (m_use_tangent_vertices) {
1203 scene::IMeshManipulator* meshmanip =
1204 RenderingEngine::get_scene_manager()->getMeshManipulator();
1205 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1208 if (m_mesh[layer]) {
1210 // Usually 1-700 faces and 1-7 materials
1211 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1212 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1213 << " materials (meshbuffers)" << std::endl;
1216 // Use VBO for mesh (this just would set this for ever buffer)
1218 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1222 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1224 // Check if animation is required for this mesh
1226 !m_crack_materials.empty() ||
1227 !m_daynight_diffs.empty() ||
1228 !m_animation_tiles.empty();
1231 MapBlockMesh::~MapBlockMesh()
1233 for (scene::IMesh *m : m_mesh) {
1234 if (m_enable_vbo && m)
1235 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1236 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1237 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1242 delete m_minimap_mapblock;
1245 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1248 if (!m_has_animation) {
1249 m_animation_force_timer = 100000;
1253 m_animation_force_timer = myrand_range(5, 100);
1256 if (crack != m_last_crack) {
1257 for (auto &crack_material : m_crack_materials) {
1258 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1259 getMeshBuffer(crack_material.first.second);
1260 std::string basename = crack_material.second;
1262 // Create new texture name from original
1263 std::ostringstream os;
1264 os << basename << crack;
1265 u32 new_texture_id = 0;
1266 video::ITexture *new_texture =
1267 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1268 buf->getMaterial().setTexture(0, new_texture);
1270 // If the current material is also animated,
1271 // update animation info
1272 auto anim_iter = m_animation_tiles.find(crack_material.first);
1273 if (anim_iter != m_animation_tiles.end()) {
1274 TileLayer &tile = anim_iter->second;
1275 tile.texture = new_texture;
1276 tile.texture_id = new_texture_id;
1277 // force animation update
1278 m_animation_frames[crack_material.first] = -1;
1282 m_last_crack = crack;
1285 // Texture animation
1286 for (auto &animation_tile : m_animation_tiles) {
1287 const TileLayer &tile = animation_tile.second;
1288 // Figure out current frame
1289 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1290 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1291 + frameoffset) % tile.animation_frame_count;
1292 // If frame doesn't change, skip
1293 if (frame == m_animation_frames[animation_tile.first])
1296 m_animation_frames[animation_tile.first] = frame;
1298 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1299 getMeshBuffer(animation_tile.first.second);
1301 const FrameSpec &animation_frame = (*tile.frames)[frame];
1302 buf->getMaterial().setTexture(0, animation_frame.texture);
1303 if (m_enable_shaders) {
1304 if (animation_frame.normal_texture)
1305 buf->getMaterial().setTexture(1,
1306 animation_frame.normal_texture);
1307 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1311 // Day-night transition
1312 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1313 // Force reload mesh to VBO
1315 for (scene::IMesh *m : m_mesh)
1317 video::SColorf day_color;
1318 get_sunlight_color(&day_color, daynight_ratio);
1320 for (auto &daynight_diff : m_daynight_diffs) {
1321 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1322 getMeshBuffer(daynight_diff.first.second);
1323 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1324 for (const auto &j : daynight_diff.second)
1325 final_color_blend(&(vertices[j.first].Color), j.second,
1328 m_last_daynight_ratio = daynight_ratio;
1334 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1336 if (camera_offset != m_camera_offset) {
1337 for (scene::IMesh *layer : m_mesh) {
1338 translateMesh(layer,
1339 intToFloat(m_camera_offset - camera_offset, BS));
1343 m_camera_offset = camera_offset;
1347 video::SColor encode_light(u16 light, u8 emissive_light)
1350 u32 day = (light & 0xff);
1351 u32 night = (light >> 8);
1352 // Add emissive light
1353 night += emissive_light * 2.5f;
1356 // Since we don't know if the day light is sunlight or
1357 // artificial light, assume it is artificial when the night
1358 // light bank is also lit.
1363 u32 sum = day + night;
1364 // Ratio of sunlight:
1367 r = day * 255 / sum;
1371 float b = (day + night) / 2;
1372 return video::SColor(r, b, b, b);