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"
39 MeshMakeData::MeshMakeData(Client *client, bool use_shaders):
41 m_use_shaders(use_shaders)
44 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
46 m_blockpos = blockpos;
48 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
51 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
52 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
53 m_vmanip.addArea(voxel_area);
56 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
58 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
59 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
61 v3s16 bp = m_blockpos + block_offset;
62 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
63 m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
66 void MeshMakeData::fill(MapBlock *block)
68 fillBlockDataBegin(block->getPos());
70 fillBlockData(v3s16(0,0,0), block->getData());
72 // Get map for reading neighbor blocks
73 Map *map = block->getParent();
75 for (const v3s16 &dir : g_26dirs) {
76 v3s16 bp = m_blockpos + dir;
77 MapBlock *b = map->getBlockNoCreateNoEx(bp);
79 fillBlockData(dir, b->getData());
83 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
86 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
89 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
91 m_smooth_lighting = smooth_lighting;
95 Light and vertex color functions
99 Calculate non-smooth lighting at interior of node.
102 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
103 const NodeDefManager *ndef)
105 u8 light = n.getLight(bank, ndef->getLightingFlags(n));
106 light = rangelim(light + increment, 0, LIGHT_SUN);
107 return decode_light(light);
111 Calculate non-smooth lighting at interior of node.
114 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
116 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
117 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
118 return day | (night << 8);
122 Calculate non-smooth lighting at face of node.
125 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
126 v3s16 face_dir, const NodeDefManager *ndef)
128 ContentLightingFlags f1 = ndef->getLightingFlags(n);
129 ContentLightingFlags f2 = ndef->getLightingFlags(n2);
132 u8 l1 = n.getLight(bank, f1);
133 u8 l2 = n2.getLight(bank, f2);
139 // Boost light level for light sources
140 u8 light_source = MYMAX(f1.light_source, f2.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.getLight(LIGHTBANK_DAY, f.getLightingFlags());
189 u8 light_level_night = n.getLight(LIGHTBANK_NIGHT, f.getLightingFlags());
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
398 #pragma GCC diagnostic ignored "-Wclass-memaccess"
401 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
402 #if defined(__GNUC__) && !defined(__clang__)
403 #pragma GCC diagnostic pop
407 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
409 if (dir.X > 0 || dir.Y != 0 || dir.Z < 0)
411 if (dir == v3s16(0,0,1)) {
414 } else if (dir == v3s16(0,0,-1)) {
417 } else if (dir == v3s16(1,0,0)) {
420 } else if (dir == v3s16(-1,0,0)) {
423 } else if (dir == v3s16(0,1,0)) {
426 } else if (dir == v3s16(0,-1,0)) {
435 video::S3DVertex vertices[4]; // Precalculated vertices
437 * The face is divided into two triangles. If this is true,
438 * vertices 0 and 2 are connected, othervise vertices 1 and 3
441 bool vertex_0_2_connected;
444 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
445 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
447 // Position is at the center of the cube.
456 v3s16 vertex_dirs[4];
457 getNodeVertexDirs(dir, vertex_dirs);
458 if (tile.world_aligned)
459 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
463 switch (tile.rotation) {
468 vertex_dirs[0] = vertex_dirs[3];
469 vertex_dirs[3] = vertex_dirs[2];
470 vertex_dirs[2] = vertex_dirs[1];
480 vertex_dirs[0] = vertex_dirs[2];
483 vertex_dirs[1] = vertex_dirs[3];
494 vertex_dirs[0] = vertex_dirs[1];
495 vertex_dirs[1] = vertex_dirs[2];
496 vertex_dirs[2] = vertex_dirs[3];
506 vertex_dirs[0] = vertex_dirs[3];
507 vertex_dirs[3] = vertex_dirs[2];
508 vertex_dirs[2] = vertex_dirs[1];
520 vertex_dirs[0] = vertex_dirs[1];
521 vertex_dirs[1] = vertex_dirs[2];
522 vertex_dirs[2] = vertex_dirs[3];
534 vertex_dirs[0] = vertex_dirs[3];
535 vertex_dirs[3] = vertex_dirs[2];
536 vertex_dirs[2] = vertex_dirs[1];
548 vertex_dirs[0] = vertex_dirs[1];
549 vertex_dirs[1] = vertex_dirs[2];
550 vertex_dirs[2] = vertex_dirs[3];
572 for (u16 i = 0; i < 4; i++) {
574 BS / 2 * vertex_dirs[i].X,
575 BS / 2 * vertex_dirs[i].Y,
576 BS / 2 * vertex_dirs[i].Z
580 for (v3f &vpos : vertex_pos) {
587 f32 abs_scale = 1.0f;
588 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
589 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
590 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
592 v3f normal(dir.X, dir.Y, dir.Z);
594 u16 li[4] = { li0, li1, li2, li3 };
598 for (u8 i = 0; i < 4; i++) {
600 night[i] = li[i] & 0xFF;
603 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
604 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
607 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
608 core::vector2d<f32>(x0, y0 + h),
609 core::vector2d<f32>(x0, y0),
610 core::vector2d<f32>(x0 + w * abs_scale, y0) };
612 // equivalent to dest.push_back(FastFace()) but faster
614 FastFace& face = *dest.rbegin();
616 for (u8 i = 0; i < 4; i++) {
617 video::SColor c = encode_light(li[i], tile.emissive_light);
618 if (!tile.emissive_light)
619 applyFacesShading(c, normal);
621 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
625 Revert triangles for nicer looking gradient if the
626 brightness of vertices 1 and 3 differ less than
627 the brightness of vertices 0 and 2.
629 face.vertex_0_2_connected = vertex_0_2_connected;
634 Nodes make a face if contents differ and solidness differs.
637 1: Face uses m1's content
638 2: Face uses m2's content
639 equivalent: Whether the blocks share the same face (eg. water and glass)
641 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
643 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
644 const NodeDefManager *ndef)
648 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
651 const ContentFeatures &f1 = ndef->get(m1);
652 const ContentFeatures &f2 = ndef->get(m2);
654 // Contents don't differ for different forms of same liquid
655 if (f1.sameLiquidRender(f2))
658 u8 c1 = f1.solidness;
659 u8 c2 = f2.solidness;
665 c1 = f1.visual_solidness;
667 c2 = f2.visual_solidness;
671 // If same solidness, liquid takes precense
672 if (f1.isLiquidRender())
674 if (f2.isLiquidRender())
685 Gets nth node tile (0 <= n <= 5).
687 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
689 const NodeDefManager *ndef = data->m_client->ndef();
690 const ContentFeatures &f = ndef->get(mn);
691 tile = f.tiles[tileindex];
692 bool has_crack = p == data->m_crack_pos_relative;
693 for (TileLayer &layer : tile.layers) {
694 if (layer.texture_id == 0)
696 if (!layer.has_color)
697 mn.getColor(f, &(layer.color));
698 // Apply temporary crack
700 layer.material_flags |= MATERIAL_FLAG_CRACK;
705 Gets node tile given a face direction.
707 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
709 const NodeDefManager *ndef = data->m_client->ndef();
711 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
712 // (0,0,1), (0,0,-1) or (0,0,0)
713 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
715 // Convert direction to single integer for table lookup
720 // 4 = invalid, treat as (0,0,0)
724 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
726 // Get rotation for things like chests
727 u8 facedir = mn.getFaceDir(ndef, true);
729 static const u16 dir_to_tile[24 * 16] =
731 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
732 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
733 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
734 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
735 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
737 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
738 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
739 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
740 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
742 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
743 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
744 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
745 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
747 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
748 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
749 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
750 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
752 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
753 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
754 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
755 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
757 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
758 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
759 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
760 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
763 u16 tile_index = facedir * 16 + dir_i;
764 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
765 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
768 static void getTileInfo(
772 const v3s16 &face_dir,
776 v3s16 &face_dir_corrected,
782 VoxelManipulator &vmanip = data->m_vmanip;
783 const NodeDefManager *ndef = data->m_client->ndef();
784 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
786 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
788 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
789 if (n0.getContent() == CONTENT_IGNORE) {
794 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
796 if (n1.getContent() == CONTENT_IGNORE) {
802 bool equivalent = false;
803 u8 mf = face_contents(n0.getContent(), n1.getContent(),
817 face_dir_corrected = face_dir;
820 p_corrected = p + face_dir;
821 face_dir_corrected = -face_dir;
824 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
825 const ContentFeatures &f = ndef->get(n);
827 tile.emissive_light = f.light_source;
829 // eg. water and glass
831 for (TileLayer &layer : tile.layers)
832 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
835 if (!data->m_smooth_lighting) {
836 lights[0] = lights[1] = lights[2] = lights[3] =
837 getFaceLight(n0, n1, face_dir, ndef);
839 v3s16 vertex_dirs[4];
840 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
842 v3s16 light_p = blockpos_nodes + p_corrected;
843 for (u16 i = 0; i < 4; i++)
844 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
850 translate_dir: unit vector with only one of x, y or z
851 face_dir: unit vector with only one of x, y or z
853 static void updateFastFaceRow(
855 const v3s16 &&startpos,
857 const v3f &&translate_dir_f,
858 const v3s16 &&face_dir,
859 std::vector<FastFace> &dest)
861 static thread_local const bool waving_liquids =
862 g_settings->getBool("enable_shaders") &&
863 g_settings->getBool("enable_waving_water");
865 static thread_local const bool force_not_tiling =
866 g_settings->getBool("enable_dynamic_shadows");
870 u16 continuous_tiles_count = 1;
872 bool makes_face = false;
874 v3s16 face_dir_corrected;
875 u16 lights[4] = {0, 0, 0, 0};
879 // Get info of first tile
880 getTileInfo(data, p, face_dir,
881 makes_face, p_corrected, face_dir_corrected,
882 lights, waving, tile);
884 // Unroll this variable which has a significant build cost
886 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
887 // If tiling can be done, this is set to false in the next step
888 bool next_is_different = true;
890 bool next_makes_face = false;
891 v3s16 next_p_corrected;
892 v3s16 next_face_dir_corrected;
893 u16 next_lights[4] = {0, 0, 0, 0};
895 // If at last position, there is nothing to compare to and
896 // the face must be drawn anyway
897 if (j != MAP_BLOCKSIZE - 1) {
900 getTileInfo(data, p, face_dir,
901 next_makes_face, next_p_corrected,
902 next_face_dir_corrected, next_lights,
906 if (!force_not_tiling
907 && next_makes_face == makes_face
908 && next_p_corrected == p_corrected + translate_dir
909 && next_face_dir_corrected == face_dir_corrected
910 && memcmp(next_lights, lights, sizeof(lights)) == 0
911 // Don't apply fast faces to waving water.
912 && (waving != 3 || !waving_liquids)
913 && next_tile.isTileable(tile)) {
914 next_is_different = false;
915 continuous_tiles_count++;
918 if (next_is_different) {
920 Create a face if there should be one
923 // Floating point conversion of the position vector
924 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
925 // Center point of face (kind of)
926 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
930 if (translate_dir.X != 0)
931 scale.X = continuous_tiles_count;
932 if (translate_dir.Y != 0)
933 scale.Y = continuous_tiles_count;
934 if (translate_dir.Z != 0)
935 scale.Z = continuous_tiles_count;
937 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
938 pf, sp, face_dir_corrected, scale, dest);
939 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
942 continuous_tiles_count = 1;
945 makes_face = next_makes_face;
946 p_corrected = next_p_corrected;
947 face_dir_corrected = next_face_dir_corrected;
948 memcpy(lights, next_lights, sizeof(lights));
949 if (next_is_different)
950 tile = std::move(next_tile); // faster than copy
954 static void updateAllFastFaceRows(MeshMakeData *data,
955 std::vector<FastFace> &dest)
958 Go through every y,z and get top(y+) faces in rows of x+
960 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
961 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
962 updateFastFaceRow(data,
964 v3s16(1, 0, 0), //dir
966 v3s16(0, 1, 0), //face dir
970 Go through every x,y and get right(x+) faces in rows of z+
972 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
973 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
974 updateFastFaceRow(data,
976 v3s16(0, 0, 1), //dir
978 v3s16(1, 0, 0), //face dir
982 Go through every y,z and get back(z+) faces in rows of x+
984 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
985 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
986 updateFastFaceRow(data,
988 v3s16(1, 0, 0), //dir
990 v3s16(0, 0, 1), //face dir
994 static void applyTileColor(PreMeshBuffer &pmb)
996 video::SColor tc = pmb.layer.color;
997 if (tc == video::SColor(0xFFFFFFFF))
999 for (video::S3DVertex &vertex : pmb.vertices) {
1000 video::SColor *c = &vertex.Color;
1001 c->set(c->getAlpha(),
1002 c->getRed() * tc.getRed() / 255,
1003 c->getGreen() * tc.getGreen() / 255,
1004 c->getBlue() * tc.getBlue() / 255);
1012 void MapBlockBspTree::buildTree(const std::vector<MeshTriangle> *triangles)
1014 this->triangles = triangles;
1018 // assert that triangle index can fit into s32
1019 assert(triangles->size() <= 0x7FFFFFFFL);
1020 std::vector<s32> indexes;
1021 indexes.reserve(triangles->size());
1022 for (u32 i = 0; i < triangles->size(); i++)
1023 indexes.push_back(i);
1025 if (!indexes.empty()) {
1026 // Start in the center of the block with increment of one quarter in each direction
1027 root = buildTree(v3f(1, 0, 0), v3f((MAP_BLOCKSIZE + 1) * 0.5f * BS), MAP_BLOCKSIZE * 0.25f * BS, indexes, 0);
1034 * @brief Find a candidate plane to split a set of triangles in two
1036 * The candidate plane is represented by one of the triangles from the set.
1038 * @param list Vector of indexes of the triangles in the set
1039 * @param triangles Vector of all triangles in the BSP tree
1040 * @return Address of the triangle that represents the proposed split plane
1042 static const MeshTriangle *findSplitCandidate(const std::vector<s32> &list, const std::vector<MeshTriangle> &triangles)
1044 // find the center of the cluster.
1045 v3f center(0, 0, 0);
1046 size_t n = list.size();
1047 for (s32 i : list) {
1048 center += triangles[i].centroid / n;
1051 // find the triangle with the largest area and closest to the center
1052 const MeshTriangle *candidate_triangle = &triangles[list[0]];
1053 const MeshTriangle *ith_triangle;
1054 for (s32 i : list) {
1055 ith_triangle = &triangles[i];
1056 if (ith_triangle->areaSQ > candidate_triangle->areaSQ ||
1057 (ith_triangle->areaSQ == candidate_triangle->areaSQ &&
1058 ith_triangle->centroid.getDistanceFromSQ(center) < candidate_triangle->centroid.getDistanceFromSQ(center))) {
1059 candidate_triangle = ith_triangle;
1062 return candidate_triangle;
1065 s32 MapBlockBspTree::buildTree(v3f normal, v3f origin, float delta, const std::vector<s32> &list, u32 depth)
1067 // if the list is empty, don't bother
1071 // if there is only one triangle, or the delta is insanely small, this is a leaf node
1072 if (list.size() == 1 || delta < 0.01) {
1073 nodes.emplace_back(normal, origin, list, -1, -1);
1074 return nodes.size() - 1;
1077 std::vector<s32> front_list;
1078 std::vector<s32> back_list;
1079 std::vector<s32> node_list;
1082 for (s32 i : list) {
1083 const MeshTriangle &triangle = (*triangles)[i];
1084 float factor = normal.dotProduct(triangle.centroid - origin);
1086 node_list.push_back(i);
1087 else if (factor > 0)
1088 front_list.push_back(i);
1090 back_list.push_back(i);
1093 // define the new split-plane
1094 v3f candidate_normal(normal.Z, normal.X, normal.Y);
1095 float candidate_delta = delta;
1097 candidate_delta /= 2;
1099 s32 front_index = -1;
1100 s32 back_index = -1;
1102 if (!front_list.empty()) {
1103 v3f next_normal = candidate_normal;
1104 v3f next_origin = origin + delta * normal;
1105 float next_delta = candidate_delta;
1106 if (next_delta < 5) {
1107 const MeshTriangle *candidate = findSplitCandidate(front_list, *triangles);
1108 next_normal = candidate->getNormal();
1109 next_origin = candidate->centroid;
1111 front_index = buildTree(next_normal, next_origin, next_delta, front_list, depth + 1);
1113 // if there are no other triangles, don't create a new node
1114 if (back_list.empty() && node_list.empty())
1118 if (!back_list.empty()) {
1119 v3f next_normal = candidate_normal;
1120 v3f next_origin = origin - delta * normal;
1121 float next_delta = candidate_delta;
1122 if (next_delta < 5) {
1123 const MeshTriangle *candidate = findSplitCandidate(back_list, *triangles);
1124 next_normal = candidate->getNormal();
1125 next_origin = candidate->centroid;
1128 back_index = buildTree(next_normal, next_origin, next_delta, back_list, depth + 1);
1130 // if there are no other triangles, don't create a new node
1131 if (front_list.empty() && node_list.empty())
1135 nodes.emplace_back(normal, origin, node_list, front_index, back_index);
1137 return nodes.size() - 1;
1140 void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector<s32> &output) const
1142 if (node < 0) return; // recursion break;
1144 const TreeNode &n = nodes[node];
1145 float factor = n.normal.dotProduct(viewpoint - n.origin);
1148 traverse(n.back_ref, viewpoint, output);
1150 traverse(n.front_ref, viewpoint, output);
1153 for (s32 i : n.triangle_refs)
1154 output.push_back(i);
1157 traverse(n.front_ref, viewpoint, output);
1159 traverse(n.back_ref, viewpoint, output);
1168 void PartialMeshBuffer::beforeDraw() const
1170 // Patch the indexes in the mesh buffer before draw
1171 m_buffer->Indices = std::move(m_vertex_indexes);
1172 m_buffer->setDirty(scene::EBT_INDEX);
1175 void PartialMeshBuffer::afterDraw() const
1177 // Take the data back
1178 m_vertex_indexes = m_buffer->Indices.steal();
1185 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1186 m_minimap_mapblock(NULL),
1187 m_tsrc(data->m_client->getTextureSource()),
1188 m_shdrsrc(data->m_client->getShaderSource()),
1189 m_animation_force_timer(0), // force initial animation
1191 m_last_daynight_ratio((u32) -1)
1193 for (auto &m : m_mesh)
1194 m = new scene::SMesh();
1195 m_enable_shaders = data->m_use_shaders;
1196 m_enable_vbo = g_settings->getBool("enable_vbo");
1198 if (data->m_client->getMinimap()) {
1199 m_minimap_mapblock = new MinimapMapblock;
1200 m_minimap_mapblock->getMinimapNodes(
1201 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1204 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1205 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1206 //TimeTaker timer1("MapBlockMesh()");
1208 std::vector<FastFace> fastfaces_new;
1209 fastfaces_new.reserve(512);
1212 We are including the faces of the trailing edges of the block.
1213 This means that when something changes, the caller must
1214 also update the meshes of the blocks at the leading edges.
1216 NOTE: This is the slowest part of this method.
1219 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1220 //TimeTaker timer2("updateAllFastFaceRows()");
1221 updateAllFastFaceRows(data, fastfaces_new);
1226 Convert FastFaces to MeshCollector
1229 MeshCollector collector(m_bounding_sphere_center);
1232 // avg 0ms (100ms spikes when loading textures the first time)
1233 // (NOTE: probably outdated)
1234 //TimeTaker timer2("MeshCollector building");
1236 for (const FastFace &f : fastfaces_new) {
1237 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1238 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1239 const u16 *indices_p =
1240 f.vertex_0_2_connected ? indices : indices_alternate;
1241 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1246 Add special graphics:
1254 MapblockMeshGenerator(data, &collector,
1255 data->m_client->getSceneManager()->getMeshManipulator()).generate();
1259 Convert MeshCollector to SMesh
1262 const bool desync_animations = g_settings->getBool(
1263 "desynchronize_mapblock_texture_animation");
1265 m_bounding_radius = std::sqrt(collector.m_bounding_radius_sq);
1267 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1268 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1270 PreMeshBuffer &p = collector.prebuffers[layer][i];
1274 // Generate animation data
1276 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1277 // Find the texture name plus ^[crack:N:
1278 std::ostringstream os(std::ios::binary);
1279 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1280 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1281 os << "o"; // use ^[cracko
1282 u8 tiles = p.layer.scale;
1284 os << ":" << (u32)tiles;
1285 os << ":" << (u32)p.layer.animation_frame_count << ":";
1286 m_crack_materials.insert(std::make_pair(
1287 std::pair<u8, u32>(layer, i), os.str()));
1288 // Replace tile texture with the cracked one
1289 p.layer.texture = m_tsrc->getTextureForMesh(
1291 &p.layer.texture_id);
1293 // - Texture animation
1294 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1295 // Add to MapBlockMesh in order to animate these tiles
1296 auto &info = m_animation_info[{layer, i}];
1297 info.tile = p.layer;
1299 if (desync_animations) {
1300 // Get starting position from noise
1302 100000 * (2.0 + noise3d(
1303 data->m_blockpos.X, data->m_blockpos.Y,
1304 data->m_blockpos.Z, 0));
1306 // Play all synchronized
1307 info.frame_offset = 0;
1309 // Replace tile texture with the first animation frame
1310 p.layer.texture = (*p.layer.frames)[0].texture;
1313 if (!m_enable_shaders) {
1314 // Extract colors for day-night animation
1315 // Dummy sunlight to handle non-sunlit areas
1316 video::SColorf sunlight;
1317 get_sunlight_color(&sunlight, 0);
1319 std::map<u32, video::SColor> colors;
1320 const u32 vertex_count = p.vertices.size();
1321 for (u32 j = 0; j < vertex_count; j++) {
1322 video::SColor *vc = &p.vertices[j].Color;
1323 video::SColor copy = *vc;
1324 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1325 final_color_blend(vc, copy, sunlight); // Finalize color
1326 else // Record color to animate
1329 // The sunlight ratio has been stored,
1330 // delete alpha (for the final rendering).
1333 if (!colors.empty())
1334 m_daynight_diffs[{layer, i}] = std::move(colors);
1338 video::SMaterial material;
1339 material.setFlag(video::EMF_LIGHTING, false);
1340 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1341 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1342 material.setFlag(video::EMF_FOG_ENABLE, true);
1343 material.setTexture(0, p.layer.texture);
1345 if (m_enable_shaders) {
1346 material.MaterialType = m_shdrsrc->getShaderInfo(
1347 p.layer.shader_id).material;
1348 p.layer.applyMaterialOptionsWithShaders(material);
1349 if (p.layer.normal_texture)
1350 material.setTexture(1, p.layer.normal_texture);
1351 material.setTexture(2, p.layer.flags_texture);
1353 p.layer.applyMaterialOptions(material);
1356 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1358 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1359 buf->Material = material;
1360 if (p.layer.isTransparent()) {
1361 buf->append(&p.vertices[0], p.vertices.size(), nullptr, 0);
1365 m_transparent_triangles.reserve(p.indices.size() / 3);
1366 for (u32 i = 0; i < p.indices.size(); i += 3) {
1367 t.p1 = p.indices[i];
1368 t.p2 = p.indices[i + 1];
1369 t.p3 = p.indices[i + 2];
1370 t.updateAttributes();
1371 m_transparent_triangles.push_back(t);
1374 buf->append(&p.vertices[0], p.vertices.size(),
1375 &p.indices[0], p.indices.size());
1377 mesh->addMeshBuffer(buf);
1381 if (m_mesh[layer]) {
1382 // Use VBO for mesh (this just would set this for ever buffer)
1384 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1388 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1389 m_bsp_tree.buildTree(&m_transparent_triangles);
1391 // Check if animation is required for this mesh
1393 !m_crack_materials.empty() ||
1394 !m_daynight_diffs.empty() ||
1395 !m_animation_info.empty();
1398 MapBlockMesh::~MapBlockMesh()
1400 for (scene::IMesh *m : m_mesh) {
1401 #if IRRLICHT_VERSION_MT_REVISION < 5
1403 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1404 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1405 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1411 delete m_minimap_mapblock;
1414 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1417 if (!m_has_animation) {
1418 m_animation_force_timer = 100000;
1422 m_animation_force_timer = myrand_range(5, 100);
1425 if (crack != m_last_crack) {
1426 for (auto &crack_material : m_crack_materials) {
1427 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1428 getMeshBuffer(crack_material.first.second);
1430 // Create new texture name from original
1431 std::string s = crack_material.second + itos(crack);
1432 u32 new_texture_id = 0;
1433 video::ITexture *new_texture =
1434 m_tsrc->getTextureForMesh(s, &new_texture_id);
1435 buf->getMaterial().setTexture(0, new_texture);
1437 // If the current material is also animated, update animation info
1438 auto anim_it = m_animation_info.find(crack_material.first);
1439 if (anim_it != m_animation_info.end()) {
1440 TileLayer &tile = anim_it->second.tile;
1441 tile.texture = new_texture;
1442 tile.texture_id = new_texture_id;
1443 // force animation update
1444 anim_it->second.frame = -1;
1448 m_last_crack = crack;
1451 // Texture animation
1452 for (auto &it : m_animation_info) {
1453 const TileLayer &tile = it.second.tile;
1454 // Figure out current frame
1455 int frameno = (int)(time * 1000 / tile.animation_frame_length_ms
1456 + it.second.frame_offset) % tile.animation_frame_count;
1457 // If frame doesn't change, skip
1458 if (frameno == it.second.frame)
1461 it.second.frame = frameno;
1463 scene::IMeshBuffer *buf = m_mesh[it.first.first]->getMeshBuffer(it.first.second);
1465 const FrameSpec &frame = (*tile.frames)[frameno];
1466 buf->getMaterial().setTexture(0, frame.texture);
1467 if (m_enable_shaders) {
1468 if (frame.normal_texture)
1469 buf->getMaterial().setTexture(1, frame.normal_texture);
1470 buf->getMaterial().setTexture(2, frame.flags_texture);
1474 // Day-night transition
1475 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1476 // Force reload mesh to VBO
1478 for (scene::IMesh *m : m_mesh)
1480 video::SColorf day_color;
1481 get_sunlight_color(&day_color, daynight_ratio);
1483 for (auto &daynight_diff : m_daynight_diffs) {
1484 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1485 getMeshBuffer(daynight_diff.first.second);
1486 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1487 for (const auto &j : daynight_diff.second)
1488 final_color_blend(&(vertices[j.first].Color), j.second,
1491 m_last_daynight_ratio = daynight_ratio;
1497 void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos)
1499 // nothing to do if the entire block is opaque
1500 if (m_transparent_triangles.empty())
1503 v3f block_posf = intToFloat(block_pos * MAP_BLOCKSIZE, BS);
1504 v3f rel_camera_pos = camera_pos - block_posf;
1506 std::vector<s32> triangle_refs;
1507 m_bsp_tree.traverse(rel_camera_pos, triangle_refs);
1509 // arrange index sequences into partial buffers
1510 m_transparent_buffers.clear();
1512 scene::SMeshBuffer *current_buffer = nullptr;
1513 std::vector<u16> current_strain;
1514 for (auto i : triangle_refs) {
1515 const auto &t = m_transparent_triangles[i];
1516 if (current_buffer != t.buffer) {
1517 if (current_buffer) {
1518 m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
1519 current_strain.clear();
1521 current_buffer = t.buffer;
1523 current_strain.push_back(t.p1);
1524 current_strain.push_back(t.p2);
1525 current_strain.push_back(t.p3);
1528 if (!current_strain.empty())
1529 m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
1532 void MapBlockMesh::consolidateTransparentBuffers()
1534 m_transparent_buffers.clear();
1536 scene::SMeshBuffer *current_buffer = nullptr;
1537 std::vector<u16> current_strain;
1539 // use the fact that m_transparent_triangles is already arranged by buffer
1540 for (const auto &t : m_transparent_triangles) {
1541 if (current_buffer != t.buffer) {
1542 if (current_buffer != nullptr) {
1543 this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
1544 current_strain.clear();
1546 current_buffer = t.buffer;
1548 current_strain.push_back(t.p1);
1549 current_strain.push_back(t.p2);
1550 current_strain.push_back(t.p3);
1553 if (!current_strain.empty()) {
1554 this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain));
1558 video::SColor encode_light(u16 light, u8 emissive_light)
1561 u32 day = (light & 0xff);
1562 u32 night = (light >> 8);
1563 // Add emissive light
1564 night += emissive_light * 2.5f;
1567 // Since we don't know if the day light is sunlight or
1568 // artificial light, assume it is artificial when the night
1569 // light bank is also lit.
1574 u32 sum = day + night;
1575 // Ratio of sunlight:
1578 r = day * 255 / sum;
1582 float b = (day + night) / 2;
1583 return video::SColor(r, b, b, b);
1586 u8 get_solid_sides(MeshMakeData *data)
1588 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
1589 const NodeDefManager *ndef = data->m_client->ndef();
1591 u8 result = 0x3F; // all sides solid;
1593 for (s16 i = 0; i < MAP_BLOCKSIZE && result != 0; i++)
1594 for (s16 j = 0; j < MAP_BLOCKSIZE && result != 0; j++) {
1595 v3s16 positions[6] = {
1597 v3s16(MAP_BLOCKSIZE - 1, i, j),
1599 v3s16(i, MAP_BLOCKSIZE - 1, j),
1601 v3s16(i, j, MAP_BLOCKSIZE - 1)
1604 for (u8 k = 0; k < 6; k++) {
1605 const MapNode &top = data->m_vmanip.getNodeRefUnsafe(blockpos_nodes + positions[k]);
1606 if (ndef->get(top).solidness != 2)
1607 result &= ~(1 << k);