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):
40 m_use_shaders(use_shaders)
43 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
45 m_blockpos = blockpos;
47 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
50 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
51 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
52 m_vmanip.addArea(voxel_area);
55 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
57 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
58 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
60 v3s16 bp = m_blockpos + block_offset;
61 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
62 m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
65 void MeshMakeData::fill(MapBlock *block)
67 fillBlockDataBegin(block->getPos());
69 fillBlockData(v3s16(0,0,0), block->getData());
71 // Get map for reading neighbor blocks
72 Map *map = block->getParent();
74 for (const v3s16 &dir : g_26dirs) {
75 v3s16 bp = m_blockpos + dir;
76 MapBlock *b = map->getBlockNoCreateNoEx(bp);
78 fillBlockData(dir, b->getData());
82 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
85 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
88 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
90 m_smooth_lighting = smooth_lighting;
94 Light and vertex color functions
98 Calculate non-smooth lighting at interior of node.
101 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
102 const NodeDefManager *ndef)
104 u8 light = n.getLight(bank, ndef);
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)
129 u8 l1 = n.getLight(bank, ndef);
130 u8 l2 = n2.getLight(bank, ndef);
136 // Boost light level for light sources
137 u8 light_source = MYMAX(ndef->get(n).light_source,
138 ndef->get(n2).light_source);
139 if(light_source > light)
140 light = light_source;
142 return decode_light(light);
146 Calculate non-smooth lighting at face of node.
149 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
150 const NodeDefManager *ndef)
152 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
153 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
154 return day | (night << 8);
158 Calculate smooth lighting at the XYZ- corner of p.
161 static u16 getSmoothLightCombined(const v3s16 &p,
162 const std::array<v3s16,8> &dirs, MeshMakeData *data)
164 const NodeDefManager *ndef = data->m_client->ndef();
166 u16 ambient_occlusion = 0;
168 u8 light_source_max = 0;
171 bool direct_sunlight = false;
173 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
178 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
179 if (n.getContent() == CONTENT_IGNORE)
181 const ContentFeatures &f = ndef->get(n);
182 if (f.light_source > light_source_max)
183 light_source_max = f.light_source;
184 // Check f.solidness because fast-style leaves look better this way
185 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
186 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
187 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
188 if (light_level_day == LIGHT_SUN)
189 direct_sunlight = true;
190 light_day += decode_light(light_level_day);
191 light_night += decode_light(light_level_night);
196 return f.light_propagates;
199 bool obstructed[4] = { true, true, true, true };
201 bool opaque1 = !add_node(1);
202 bool opaque2 = !add_node(2);
203 bool opaque3 = !add_node(3);
204 obstructed[0] = opaque1 && opaque2;
205 obstructed[1] = opaque1 && opaque3;
206 obstructed[2] = opaque2 && opaque3;
207 for (u8 k = 0; k < 3; ++k)
208 if (add_node(k + 4, obstructed[k]))
209 obstructed[3] = false;
210 if (add_node(7, obstructed[3])) { // wrap light around nodes
211 ambient_occlusion -= 3;
212 for (u8 k = 0; k < 3; ++k)
213 add_node(k + 4, !obstructed[k]);
216 if (light_count == 0) {
217 light_day = light_night = 0;
219 light_day /= light_count;
220 light_night /= light_count;
223 // boost direct sunlight, if any
227 // Boost brightness around light sources
228 bool skip_ambient_occlusion_day = false;
229 if (decode_light(light_source_max) >= light_day) {
230 light_day = decode_light(light_source_max);
231 skip_ambient_occlusion_day = true;
234 bool skip_ambient_occlusion_night = false;
235 if(decode_light(light_source_max) >= light_night) {
236 light_night = decode_light(light_source_max);
237 skip_ambient_occlusion_night = true;
240 if (ambient_occlusion > 4) {
241 static thread_local const float ao_gamma = rangelim(
242 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
244 // Table of gamma space multiply factors.
245 static thread_local const float light_amount[3] = {
246 powf(0.75, 1.0 / ao_gamma),
247 powf(0.5, 1.0 / ao_gamma),
248 powf(0.25, 1.0 / ao_gamma)
251 //calculate table index for gamma space multiplier
252 ambient_occlusion -= 5;
254 if (!skip_ambient_occlusion_day)
255 light_day = rangelim(core::round32(
256 light_day * light_amount[ambient_occlusion]), 0, 255);
257 if (!skip_ambient_occlusion_night)
258 light_night = rangelim(core::round32(
259 light_night * light_amount[ambient_occlusion]), 0, 255);
262 return light_day | (light_night << 8);
266 Calculate smooth lighting at the given corner of p.
268 Node at p is solid, and thus the lighting is face-dependent.
270 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
272 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
276 Calculate smooth lighting at the given corner of p.
278 Node at p is not solid, and the lighting is not face-dependent.
280 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
282 const std::array<v3s16,8> dirs = {{
283 // Always shine light
290 v3s16(corner.X,corner.Y,0),
291 v3s16(corner.X,0,corner.Z),
292 v3s16(0,corner.Y,corner.Z),
293 v3s16(corner.X,corner.Y,corner.Z)
295 return getSmoothLightCombined(p, dirs, data);
298 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
299 f32 rg = daynight_ratio / 1000.0f - 0.04f;
300 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
306 void final_color_blend(video::SColor *result,
307 u16 light, u32 daynight_ratio)
309 video::SColorf dayLight;
310 get_sunlight_color(&dayLight, daynight_ratio);
311 final_color_blend(result,
312 encode_light(light, 0), dayLight);
315 void final_color_blend(video::SColor *result,
316 const video::SColor &data, const video::SColorf &dayLight)
318 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
320 video::SColorf c(data);
323 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
324 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
325 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
327 // Emphase blue a bit in darker places
328 // Each entry of this array represents a range of 8 blue levels
329 static const u8 emphase_blue_when_dark[32] = {
330 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
331 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
334 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
335 0, 255) / 8] / 255.0f;
337 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
338 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
339 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
343 Mesh generation helpers
346 // This table is moved outside getNodeVertexDirs to avoid the compiler using
347 // a mutex to initialize this table at runtime right in the hot path.
348 // For details search the internet for "cxa_guard_acquire".
349 static const v3s16 vertex_dirs_table[] = {
351 v3s16( 1,-1, 1), v3s16( 1,-1,-1),
352 v3s16( 1, 1,-1), v3s16( 1, 1, 1),
354 v3s16( 1, 1,-1), v3s16(-1, 1,-1),
355 v3s16(-1, 1, 1), v3s16( 1, 1, 1),
357 v3s16(-1,-1, 1), v3s16( 1,-1, 1),
358 v3s16( 1, 1, 1), v3s16(-1, 1, 1),
360 v3s16(), v3s16(), v3s16(), v3s16(),
362 v3s16( 1,-1,-1), v3s16(-1,-1,-1),
363 v3s16(-1, 1,-1), v3s16( 1, 1,-1),
365 v3s16( 1,-1, 1), v3s16(-1,-1, 1),
366 v3s16(-1,-1,-1), v3s16( 1,-1,-1),
368 v3s16(-1,-1,-1), v3s16(-1,-1, 1),
369 v3s16(-1, 1, 1), v3s16(-1, 1,-1)
373 vertex_dirs: v3s16[4]
375 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
378 If looked from outside the node towards the face, the corners are:
385 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
387 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
389 // Convert direction to single integer for table lookup
390 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
393 #if defined(__GNUC__) && !defined(__clang__)
394 #pragma GCC diagnostic push
396 #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");
863 static thread_local const bool force_not_tiling =
864 g_settings->getBool("enable_dynamic_shadows");
868 u16 continuous_tiles_count = 1;
870 bool makes_face = false;
872 v3s16 face_dir_corrected;
873 u16 lights[4] = {0, 0, 0, 0};
877 // Get info of first tile
878 getTileInfo(data, p, face_dir,
879 makes_face, p_corrected, face_dir_corrected,
880 lights, waving, tile);
882 // Unroll this variable which has a significant build cost
884 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
885 // If tiling can be done, this is set to false in the next step
886 bool next_is_different = true;
888 bool next_makes_face = false;
889 v3s16 next_p_corrected;
890 v3s16 next_face_dir_corrected;
891 u16 next_lights[4] = {0, 0, 0, 0};
893 // If at last position, there is nothing to compare to and
894 // the face must be drawn anyway
895 if (j != MAP_BLOCKSIZE - 1) {
898 getTileInfo(data, p, face_dir,
899 next_makes_face, next_p_corrected,
900 next_face_dir_corrected, next_lights,
904 if (!force_not_tiling
905 && next_makes_face == makes_face
906 && next_p_corrected == p_corrected + translate_dir
907 && next_face_dir_corrected == face_dir_corrected
908 && memcmp(next_lights, lights, sizeof(lights)) == 0
909 // Don't apply fast faces to waving water.
910 && (waving != 3 || !waving_liquids)
911 && next_tile.isTileable(tile)) {
912 next_is_different = false;
913 continuous_tiles_count++;
916 if (next_is_different) {
918 Create a face if there should be one
921 // Floating point conversion of the position vector
922 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
923 // Center point of face (kind of)
924 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
928 if (translate_dir.X != 0)
929 scale.X = continuous_tiles_count;
930 if (translate_dir.Y != 0)
931 scale.Y = continuous_tiles_count;
932 if (translate_dir.Z != 0)
933 scale.Z = continuous_tiles_count;
935 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
936 pf, sp, face_dir_corrected, scale, dest);
937 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
940 continuous_tiles_count = 1;
943 makes_face = next_makes_face;
944 p_corrected = next_p_corrected;
945 face_dir_corrected = next_face_dir_corrected;
946 memcpy(lights, next_lights, sizeof(lights));
947 if (next_is_different)
948 tile = std::move(next_tile); // faster than copy
952 static void updateAllFastFaceRows(MeshMakeData *data,
953 std::vector<FastFace> &dest)
956 Go through every y,z and get top(y+) faces in rows of x+
958 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
959 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
960 updateFastFaceRow(data,
962 v3s16(1, 0, 0), //dir
964 v3s16(0, 1, 0), //face dir
968 Go through every x,y and get right(x+) faces in rows of z+
970 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
971 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
972 updateFastFaceRow(data,
974 v3s16(0, 0, 1), //dir
976 v3s16(1, 0, 0), //face dir
980 Go through every y,z and get back(z+) faces in rows of x+
982 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
983 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
984 updateFastFaceRow(data,
986 v3s16(1, 0, 0), //dir
988 v3s16(0, 0, 1), //face dir
992 static void applyTileColor(PreMeshBuffer &pmb)
994 video::SColor tc = pmb.layer.color;
995 if (tc == video::SColor(0xFFFFFFFF))
997 for (video::S3DVertex &vertex : pmb.vertices) {
998 video::SColor *c = &vertex.Color;
999 c->set(c->getAlpha(),
1000 c->getRed() * tc.getRed() / 255,
1001 c->getGreen() * tc.getGreen() / 255,
1002 c->getBlue() * tc.getBlue() / 255);
1010 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1011 m_minimap_mapblock(NULL),
1012 m_tsrc(data->m_client->getTextureSource()),
1013 m_shdrsrc(data->m_client->getShaderSource()),
1014 m_animation_force_timer(0), // force initial animation
1016 m_last_daynight_ratio((u32) -1)
1018 for (auto &m : m_mesh)
1019 m = new scene::SMesh();
1020 m_enable_shaders = data->m_use_shaders;
1021 m_enable_vbo = g_settings->getBool("enable_vbo");
1023 if (data->m_client->getMinimap()) {
1024 m_minimap_mapblock = new MinimapMapblock;
1025 m_minimap_mapblock->getMinimapNodes(
1026 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1029 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1030 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1031 //TimeTaker timer1("MapBlockMesh()");
1033 std::vector<FastFace> fastfaces_new;
1034 fastfaces_new.reserve(512);
1037 We are including the faces of the trailing edges of the block.
1038 This means that when something changes, the caller must
1039 also update the meshes of the blocks at the leading edges.
1041 NOTE: This is the slowest part of this method.
1044 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1045 //TimeTaker timer2("updateAllFastFaceRows()");
1046 updateAllFastFaceRows(data, fastfaces_new);
1051 Convert FastFaces to MeshCollector
1054 MeshCollector collector;
1057 // avg 0ms (100ms spikes when loading textures the first time)
1058 // (NOTE: probably outdated)
1059 //TimeTaker timer2("MeshCollector building");
1061 for (const FastFace &f : fastfaces_new) {
1062 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1063 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1064 const u16 *indices_p =
1065 f.vertex_0_2_connected ? indices : indices_alternate;
1066 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1071 Add special graphics:
1079 MapblockMeshGenerator(data, &collector,
1080 data->m_client->getSceneManager()->getMeshManipulator()).generate();
1084 Convert MeshCollector to SMesh
1087 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1088 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1090 PreMeshBuffer &p = collector.prebuffers[layer][i];
1094 // Generate animation data
1096 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1097 // Find the texture name plus ^[crack:N:
1098 std::ostringstream os(std::ios::binary);
1099 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1100 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1101 os << "o"; // use ^[cracko
1102 u8 tiles = p.layer.scale;
1104 os << ":" << (u32)tiles;
1105 os << ":" << (u32)p.layer.animation_frame_count << ":";
1106 m_crack_materials.insert(std::make_pair(
1107 std::pair<u8, u32>(layer, i), os.str()));
1108 // Replace tile texture with the cracked one
1109 p.layer.texture = m_tsrc->getTextureForMesh(
1111 &p.layer.texture_id);
1113 // - Texture animation
1114 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1115 // Add to MapBlockMesh in order to animate these tiles
1116 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1117 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1118 if (g_settings->getBool(
1119 "desynchronize_mapblock_texture_animation")) {
1120 // Get starting position from noise
1121 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1122 100000 * (2.0 + noise3d(
1123 data->m_blockpos.X, data->m_blockpos.Y,
1124 data->m_blockpos.Z, 0));
1126 // Play all synchronized
1127 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1129 // Replace tile texture with the first animation frame
1130 p.layer.texture = (*p.layer.frames)[0].texture;
1133 if (!m_enable_shaders) {
1134 // Extract colors for day-night animation
1135 // Dummy sunlight to handle non-sunlit areas
1136 video::SColorf sunlight;
1137 get_sunlight_color(&sunlight, 0);
1138 u32 vertex_count = p.vertices.size();
1139 for (u32 j = 0; j < vertex_count; j++) {
1140 video::SColor *vc = &p.vertices[j].Color;
1141 video::SColor copy = *vc;
1142 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1143 final_color_blend(vc, copy, sunlight); // Finalize color
1144 else // Record color to animate
1145 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1147 // The sunlight ratio has been stored,
1148 // delete alpha (for the final rendering).
1154 video::SMaterial material;
1155 material.setFlag(video::EMF_LIGHTING, false);
1156 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1157 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1158 material.setFlag(video::EMF_FOG_ENABLE, true);
1159 material.setTexture(0, p.layer.texture);
1161 if (m_enable_shaders) {
1162 material.MaterialType = m_shdrsrc->getShaderInfo(
1163 p.layer.shader_id).material;
1164 p.layer.applyMaterialOptionsWithShaders(material);
1165 if (p.layer.normal_texture)
1166 material.setTexture(1, p.layer.normal_texture);
1167 material.setTexture(2, p.layer.flags_texture);
1169 p.layer.applyMaterialOptions(material);
1172 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1174 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1175 buf->Material = material;
1176 buf->append(&p.vertices[0], p.vertices.size(),
1177 &p.indices[0], p.indices.size());
1178 mesh->addMeshBuffer(buf);
1182 if (m_mesh[layer]) {
1183 // Use VBO for mesh (this just would set this for ever buffer)
1185 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1189 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1191 // Check if animation is required for this mesh
1193 !m_crack_materials.empty() ||
1194 !m_daynight_diffs.empty() ||
1195 !m_animation_tiles.empty();
1198 MapBlockMesh::~MapBlockMesh()
1200 for (scene::IMesh *m : m_mesh) {
1202 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1203 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1204 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1209 delete m_minimap_mapblock;
1212 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1215 if (!m_has_animation) {
1216 m_animation_force_timer = 100000;
1220 m_animation_force_timer = myrand_range(5, 100);
1223 if (crack != m_last_crack) {
1224 for (auto &crack_material : m_crack_materials) {
1225 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1226 getMeshBuffer(crack_material.first.second);
1227 std::string basename = crack_material.second;
1229 // Create new texture name from original
1230 std::ostringstream os;
1231 os << basename << crack;
1232 u32 new_texture_id = 0;
1233 video::ITexture *new_texture =
1234 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1235 buf->getMaterial().setTexture(0, new_texture);
1237 // If the current material is also animated,
1238 // update animation info
1239 auto anim_iter = m_animation_tiles.find(crack_material.first);
1240 if (anim_iter != m_animation_tiles.end()) {
1241 TileLayer &tile = anim_iter->second;
1242 tile.texture = new_texture;
1243 tile.texture_id = new_texture_id;
1244 // force animation update
1245 m_animation_frames[crack_material.first] = -1;
1249 m_last_crack = crack;
1252 // Texture animation
1253 for (auto &animation_tile : m_animation_tiles) {
1254 const TileLayer &tile = animation_tile.second;
1255 // Figure out current frame
1256 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1257 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1258 + frameoffset) % tile.animation_frame_count;
1259 // If frame doesn't change, skip
1260 if (frame == m_animation_frames[animation_tile.first])
1263 m_animation_frames[animation_tile.first] = frame;
1265 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1266 getMeshBuffer(animation_tile.first.second);
1268 const FrameSpec &animation_frame = (*tile.frames)[frame];
1269 buf->getMaterial().setTexture(0, animation_frame.texture);
1270 if (m_enable_shaders) {
1271 if (animation_frame.normal_texture)
1272 buf->getMaterial().setTexture(1,
1273 animation_frame.normal_texture);
1274 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1278 // Day-night transition
1279 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1280 // Force reload mesh to VBO
1282 for (scene::IMesh *m : m_mesh)
1284 video::SColorf day_color;
1285 get_sunlight_color(&day_color, daynight_ratio);
1287 for (auto &daynight_diff : m_daynight_diffs) {
1288 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1289 getMeshBuffer(daynight_diff.first.second);
1290 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1291 for (const auto &j : daynight_diff.second)
1292 final_color_blend(&(vertices[j.first].Color), j.second,
1295 m_last_daynight_ratio = daynight_ratio;
1301 video::SColor encode_light(u16 light, u8 emissive_light)
1304 u32 day = (light & 0xff);
1305 u32 night = (light >> 8);
1306 // Add emissive light
1307 night += emissive_light * 2.5f;
1310 // Since we don't know if the day light is sunlight or
1311 // artificial light, assume it is artificial when the night
1312 // light bank is also lit.
1317 u32 sum = day + night;
1318 // Ratio of sunlight:
1321 r = day * 255 / sum;
1325 float b = (day + night) / 2;
1326 return video::SColor(r, b, b, b);