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);
107 light = rangelim(light + increment, 0, LIGHT_SUN);
108 return decode_light(light);
112 Calculate non-smooth lighting at interior of node.
115 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
117 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
118 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
119 return day | (night << 8);
123 Calculate non-smooth lighting at face of node.
126 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
127 v3s16 face_dir, const NodeDefManager *ndef)
130 u8 l1 = n.getLight(bank, ndef);
131 u8 l2 = n2.getLight(bank, ndef);
137 // Boost light level for light sources
138 u8 light_source = MYMAX(ndef->get(n).light_source,
139 ndef->get(n2).light_source);
140 if(light_source > light)
141 light = light_source;
143 return decode_light(light);
147 Calculate non-smooth lighting at face of node.
150 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
151 const NodeDefManager *ndef)
153 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
154 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
155 return day | (night << 8);
159 Calculate smooth lighting at the XYZ- corner of p.
162 static u16 getSmoothLightCombined(const v3s16 &p,
163 const std::array<v3s16,8> &dirs, MeshMakeData *data)
165 const NodeDefManager *ndef = data->m_client->ndef();
167 u16 ambient_occlusion = 0;
169 u8 light_source_max = 0;
172 bool direct_sunlight = false;
174 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
179 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
180 if (n.getContent() == CONTENT_IGNORE)
182 const ContentFeatures &f = ndef->get(n);
183 if (f.light_source > light_source_max)
184 light_source_max = f.light_source;
185 // Check f.solidness because fast-style leaves look better this way
186 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
187 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
188 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
189 if (light_level_day == LIGHT_SUN)
190 direct_sunlight = true;
191 light_day += decode_light(light_level_day);
192 light_night += decode_light(light_level_night);
197 return f.light_propagates;
200 bool obstructed[4] = { true, true, true, true };
202 bool opaque1 = !add_node(1);
203 bool opaque2 = !add_node(2);
204 bool opaque3 = !add_node(3);
205 obstructed[0] = opaque1 && opaque2;
206 obstructed[1] = opaque1 && opaque3;
207 obstructed[2] = opaque2 && opaque3;
208 for (u8 k = 0; k < 3; ++k)
209 if (add_node(k + 4, obstructed[k]))
210 obstructed[3] = false;
211 if (add_node(7, obstructed[3])) { // wrap light around nodes
212 ambient_occlusion -= 3;
213 for (u8 k = 0; k < 3; ++k)
214 add_node(k + 4, !obstructed[k]);
217 if (light_count == 0) {
218 light_day = light_night = 0;
220 light_day /= light_count;
221 light_night /= light_count;
224 // boost direct sunlight, if any
228 // Boost brightness around light sources
229 bool skip_ambient_occlusion_day = false;
230 if (decode_light(light_source_max) >= light_day) {
231 light_day = decode_light(light_source_max);
232 skip_ambient_occlusion_day = true;
235 bool skip_ambient_occlusion_night = false;
236 if(decode_light(light_source_max) >= light_night) {
237 light_night = decode_light(light_source_max);
238 skip_ambient_occlusion_night = true;
241 if (ambient_occlusion > 4) {
242 static thread_local const float ao_gamma = rangelim(
243 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
245 // Table of gamma space multiply factors.
246 static thread_local const float light_amount[3] = {
247 powf(0.75, 1.0 / ao_gamma),
248 powf(0.5, 1.0 / ao_gamma),
249 powf(0.25, 1.0 / ao_gamma)
252 //calculate table index for gamma space multiplier
253 ambient_occlusion -= 5;
255 if (!skip_ambient_occlusion_day)
256 light_day = rangelim(core::round32(
257 light_day * light_amount[ambient_occlusion]), 0, 255);
258 if (!skip_ambient_occlusion_night)
259 light_night = rangelim(core::round32(
260 light_night * light_amount[ambient_occlusion]), 0, 255);
263 return light_day | (light_night << 8);
267 Calculate smooth lighting at the given corner of p.
269 Node at p is solid, and thus the lighting is face-dependent.
271 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
273 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
277 Calculate smooth lighting at the given corner of p.
279 Node at p is not solid, and the lighting is not face-dependent.
281 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
283 const std::array<v3s16,8> dirs = {{
284 // Always shine light
291 v3s16(corner.X,corner.Y,0),
292 v3s16(corner.X,0,corner.Z),
293 v3s16(0,corner.Y,corner.Z),
294 v3s16(corner.X,corner.Y,corner.Z)
296 return getSmoothLightCombined(p, dirs, data);
299 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
300 f32 rg = daynight_ratio / 1000.0f - 0.04f;
301 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
307 void final_color_blend(video::SColor *result,
308 u16 light, u32 daynight_ratio)
310 video::SColorf dayLight;
311 get_sunlight_color(&dayLight, daynight_ratio);
312 final_color_blend(result,
313 encode_light(light, 0), dayLight);
316 void final_color_blend(video::SColor *result,
317 const video::SColor &data, const video::SColorf &dayLight)
319 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
321 video::SColorf c(data);
324 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
325 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
326 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
328 // Emphase blue a bit in darker places
329 // Each entry of this array represents a range of 8 blue levels
330 static const u8 emphase_blue_when_dark[32] = {
331 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
332 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
335 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
336 0, 255) / 8] / 255.0f;
338 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
339 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
340 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
344 Mesh generation helpers
347 // This table is moved outside getNodeVertexDirs to avoid the compiler using
348 // a mutex to initialize this table at runtime right in the hot path.
349 // For details search the internet for "cxa_guard_acquire".
350 static const v3s16 vertex_dirs_table[] = {
352 v3s16( 1,-1, 1), v3s16( 1,-1,-1),
353 v3s16( 1, 1,-1), v3s16( 1, 1, 1),
355 v3s16( 1, 1,-1), v3s16(-1, 1,-1),
356 v3s16(-1, 1, 1), v3s16( 1, 1, 1),
358 v3s16(-1,-1, 1), v3s16( 1,-1, 1),
359 v3s16( 1, 1, 1), v3s16(-1, 1, 1),
361 v3s16(), v3s16(), v3s16(), v3s16(),
363 v3s16( 1,-1,-1), v3s16(-1,-1,-1),
364 v3s16(-1, 1,-1), v3s16( 1, 1,-1),
366 v3s16( 1,-1, 1), v3s16(-1,-1, 1),
367 v3s16(-1,-1,-1), v3s16( 1,-1,-1),
369 v3s16(-1,-1,-1), v3s16(-1,-1, 1),
370 v3s16(-1, 1, 1), v3s16(-1, 1,-1)
374 vertex_dirs: v3s16[4]
376 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
379 If looked from outside the node towards the face, the corners are:
386 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
388 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
390 // Convert direction to single integer for table lookup
391 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
394 #if defined(__GNUC__) && !defined(__clang__)
395 #pragma GCC diagnostic push
397 #pragma GCC diagnostic ignored "-Wclass-memaccess"
400 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
401 #if defined(__GNUC__) && !defined(__clang__)
402 #pragma GCC diagnostic pop
406 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
408 if (dir.X > 0 || dir.Y != 0 || dir.Z < 0)
410 if (dir == v3s16(0,0,1)) {
413 } else if (dir == v3s16(0,0,-1)) {
416 } else if (dir == v3s16(1,0,0)) {
419 } else if (dir == v3s16(-1,0,0)) {
422 } else if (dir == v3s16(0,1,0)) {
425 } else if (dir == v3s16(0,-1,0)) {
434 video::S3DVertex vertices[4]; // Precalculated vertices
436 * The face is divided into two triangles. If this is true,
437 * vertices 0 and 2 are connected, othervise vertices 1 and 3
440 bool vertex_0_2_connected;
443 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
444 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
446 // Position is at the center of the cube.
455 v3s16 vertex_dirs[4];
456 getNodeVertexDirs(dir, vertex_dirs);
457 if (tile.world_aligned)
458 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
462 switch (tile.rotation) {
467 vertex_dirs[0] = vertex_dirs[3];
468 vertex_dirs[3] = vertex_dirs[2];
469 vertex_dirs[2] = vertex_dirs[1];
479 vertex_dirs[0] = vertex_dirs[2];
482 vertex_dirs[1] = vertex_dirs[3];
493 vertex_dirs[0] = vertex_dirs[1];
494 vertex_dirs[1] = vertex_dirs[2];
495 vertex_dirs[2] = vertex_dirs[3];
505 vertex_dirs[0] = vertex_dirs[3];
506 vertex_dirs[3] = vertex_dirs[2];
507 vertex_dirs[2] = vertex_dirs[1];
519 vertex_dirs[0] = vertex_dirs[1];
520 vertex_dirs[1] = vertex_dirs[2];
521 vertex_dirs[2] = vertex_dirs[3];
533 vertex_dirs[0] = vertex_dirs[3];
534 vertex_dirs[3] = vertex_dirs[2];
535 vertex_dirs[2] = vertex_dirs[1];
547 vertex_dirs[0] = vertex_dirs[1];
548 vertex_dirs[1] = vertex_dirs[2];
549 vertex_dirs[2] = vertex_dirs[3];
571 for (u16 i = 0; i < 4; i++) {
573 BS / 2 * vertex_dirs[i].X,
574 BS / 2 * vertex_dirs[i].Y,
575 BS / 2 * vertex_dirs[i].Z
579 for (v3f &vpos : vertex_pos) {
586 f32 abs_scale = 1.0f;
587 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
588 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
589 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
591 v3f normal(dir.X, dir.Y, dir.Z);
593 u16 li[4] = { li0, li1, li2, li3 };
597 for (u8 i = 0; i < 4; i++) {
599 night[i] = li[i] & 0xFF;
602 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
603 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
606 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
607 core::vector2d<f32>(x0, y0 + h),
608 core::vector2d<f32>(x0, y0),
609 core::vector2d<f32>(x0 + w * abs_scale, y0) };
611 // equivalent to dest.push_back(FastFace()) but faster
613 FastFace& face = *dest.rbegin();
615 for (u8 i = 0; i < 4; i++) {
616 video::SColor c = encode_light(li[i], tile.emissive_light);
617 if (!tile.emissive_light)
618 applyFacesShading(c, normal);
620 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
624 Revert triangles for nicer looking gradient if the
625 brightness of vertices 1 and 3 differ less than
626 the brightness of vertices 0 and 2.
628 face.vertex_0_2_connected = vertex_0_2_connected;
633 Nodes make a face if contents differ and solidness differs.
636 1: Face uses m1's content
637 2: Face uses m2's content
638 equivalent: Whether the blocks share the same face (eg. water and glass)
640 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
642 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
643 const NodeDefManager *ndef)
647 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
650 const ContentFeatures &f1 = ndef->get(m1);
651 const ContentFeatures &f2 = ndef->get(m2);
653 // Contents don't differ for different forms of same liquid
654 if (f1.sameLiquid(f2))
657 u8 c1 = f1.solidness;
658 u8 c2 = f2.solidness;
664 c1 = f1.visual_solidness;
666 c2 = f2.visual_solidness;
670 // If same solidness, liquid takes precense
684 Gets nth node tile (0 <= n <= 5).
686 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
688 const NodeDefManager *ndef = data->m_client->ndef();
689 const ContentFeatures &f = ndef->get(mn);
690 tile = f.tiles[tileindex];
691 bool has_crack = p == data->m_crack_pos_relative;
692 for (TileLayer &layer : tile.layers) {
693 if (layer.texture_id == 0)
695 if (!layer.has_color)
696 mn.getColor(f, &(layer.color));
697 // Apply temporary crack
699 layer.material_flags |= MATERIAL_FLAG_CRACK;
704 Gets node tile given a face direction.
706 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
708 const NodeDefManager *ndef = data->m_client->ndef();
710 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
711 // (0,0,1), (0,0,-1) or (0,0,0)
712 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
714 // Convert direction to single integer for table lookup
719 // 4 = invalid, treat as (0,0,0)
723 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
725 // Get rotation for things like chests
726 u8 facedir = mn.getFaceDir(ndef, true);
728 static const u16 dir_to_tile[24 * 16] =
730 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
731 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
732 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
733 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
734 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
736 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
737 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
738 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
739 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
741 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
742 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
743 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
744 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
746 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
747 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
748 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
749 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
751 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
752 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
753 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
754 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
756 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
757 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
758 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
759 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
762 u16 tile_index = facedir * 16 + dir_i;
763 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
764 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
767 static void getTileInfo(
771 const v3s16 &face_dir,
775 v3s16 &face_dir_corrected,
781 VoxelManipulator &vmanip = data->m_vmanip;
782 const NodeDefManager *ndef = data->m_client->ndef();
783 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
785 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
787 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
788 if (n0.getContent() == CONTENT_IGNORE) {
793 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
795 if (n1.getContent() == CONTENT_IGNORE) {
801 bool equivalent = false;
802 u8 mf = face_contents(n0.getContent(), n1.getContent(),
816 face_dir_corrected = face_dir;
819 p_corrected = p + face_dir;
820 face_dir_corrected = -face_dir;
823 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
824 const ContentFeatures &f = ndef->get(n);
826 tile.emissive_light = f.light_source;
828 // eg. water and glass
830 for (TileLayer &layer : tile.layers)
831 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
834 if (!data->m_smooth_lighting) {
835 lights[0] = lights[1] = lights[2] = lights[3] =
836 getFaceLight(n0, n1, face_dir, ndef);
838 v3s16 vertex_dirs[4];
839 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
841 v3s16 light_p = blockpos_nodes + p_corrected;
842 for (u16 i = 0; i < 4; i++)
843 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
849 translate_dir: unit vector with only one of x, y or z
850 face_dir: unit vector with only one of x, y or z
852 static void updateFastFaceRow(
854 const v3s16 &&startpos,
856 const v3f &&translate_dir_f,
857 const v3s16 &&face_dir,
858 std::vector<FastFace> &dest)
860 static thread_local const bool waving_liquids =
861 g_settings->getBool("enable_shaders") &&
862 g_settings->getBool("enable_waving_water");
864 static thread_local const bool force_not_tiling =
865 g_settings->getBool("enable_dynamic_shadows");
869 u16 continuous_tiles_count = 1;
871 bool makes_face = false;
873 v3s16 face_dir_corrected;
874 u16 lights[4] = {0, 0, 0, 0};
878 // Get info of first tile
879 getTileInfo(data, p, face_dir,
880 makes_face, p_corrected, face_dir_corrected,
881 lights, waving, tile);
883 // Unroll this variable which has a significant build cost
885 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
886 // If tiling can be done, this is set to false in the next step
887 bool next_is_different = true;
889 bool next_makes_face = false;
890 v3s16 next_p_corrected;
891 v3s16 next_face_dir_corrected;
892 u16 next_lights[4] = {0, 0, 0, 0};
894 // If at last position, there is nothing to compare to and
895 // the face must be drawn anyway
896 if (j != MAP_BLOCKSIZE - 1) {
899 getTileInfo(data, p, face_dir,
900 next_makes_face, next_p_corrected,
901 next_face_dir_corrected, next_lights,
905 if (!force_not_tiling
906 && next_makes_face == makes_face
907 && next_p_corrected == p_corrected + translate_dir
908 && next_face_dir_corrected == face_dir_corrected
909 && memcmp(next_lights, lights, sizeof(lights)) == 0
910 // Don't apply fast faces to waving water.
911 && (waving != 3 || !waving_liquids)
912 && next_tile.isTileable(tile)) {
913 next_is_different = false;
914 continuous_tiles_count++;
917 if (next_is_different) {
919 Create a face if there should be one
922 // Floating point conversion of the position vector
923 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
924 // Center point of face (kind of)
925 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
929 if (translate_dir.X != 0)
930 scale.X = continuous_tiles_count;
931 if (translate_dir.Y != 0)
932 scale.Y = continuous_tiles_count;
933 if (translate_dir.Z != 0)
934 scale.Z = continuous_tiles_count;
936 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
937 pf, sp, face_dir_corrected, scale, dest);
938 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
941 continuous_tiles_count = 1;
944 makes_face = next_makes_face;
945 p_corrected = next_p_corrected;
946 face_dir_corrected = next_face_dir_corrected;
947 memcpy(lights, next_lights, sizeof(lights));
948 if (next_is_different)
949 tile = std::move(next_tile); // faster than copy
953 static void updateAllFastFaceRows(MeshMakeData *data,
954 std::vector<FastFace> &dest)
957 Go through every y,z and get top(y+) faces in rows of x+
959 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
960 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
961 updateFastFaceRow(data,
963 v3s16(1, 0, 0), //dir
965 v3s16(0, 1, 0), //face dir
969 Go through every x,y and get right(x+) faces in rows of z+
971 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
972 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
973 updateFastFaceRow(data,
975 v3s16(0, 0, 1), //dir
977 v3s16(1, 0, 0), //face dir
981 Go through every y,z and get back(z+) faces in rows of x+
983 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
984 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
985 updateFastFaceRow(data,
987 v3s16(1, 0, 0), //dir
989 v3s16(0, 0, 1), //face dir
993 static void applyTileColor(PreMeshBuffer &pmb)
995 video::SColor tc = pmb.layer.color;
996 if (tc == video::SColor(0xFFFFFFFF))
998 for (video::S3DVertex &vertex : pmb.vertices) {
999 video::SColor *c = &vertex.Color;
1000 c->set(c->getAlpha(),
1001 c->getRed() * tc.getRed() / 255,
1002 c->getGreen() * tc.getGreen() / 255,
1003 c->getBlue() * tc.getBlue() / 255);
1011 void MapBlockBspTree::buildTree(const std::vector<MeshTriangle> *triangles)
1013 this->triangles = triangles;
1017 // assert that triangle index can fit into s32
1018 assert(triangles->size() <= 0x7FFFFFFFL);
1019 std::vector<s32> indexes;
1020 indexes.reserve(triangles->size());
1021 for (u32 i = 0; i < triangles->size(); i++)
1022 indexes.push_back(i);
1024 root = buildTree(v3f(1, 0, 0), v3f(85, 85, 85), 40, indexes, 0);
1028 * @brief Find a candidate plane to split a set of triangles in two
1030 * The candidate plane is represented by one of the triangles from the set.
1032 * @param list Vector of indexes of the triangles in the set
1033 * @param triangles Vector of all triangles in the BSP tree
1034 * @return Address of the triangle that represents the proposed split plane
1036 static const MeshTriangle *findSplitCandidate(const std::vector<s32> &list, const std::vector<MeshTriangle> &triangles)
1038 // find the center of the cluster.
1039 v3f center(0, 0, 0);
1040 size_t n = list.size();
1041 for (s32 i : list) {
1042 center += triangles[i].centroid / n;
1045 // find the triangle with the largest area and closest to the center
1046 const MeshTriangle *candidate_triangle = &triangles[list[0]];
1047 const MeshTriangle *ith_triangle;
1048 for (s32 i : list) {
1049 ith_triangle = &triangles[i];
1050 if (ith_triangle->areaSQ > candidate_triangle->areaSQ ||
1051 (ith_triangle->areaSQ == candidate_triangle->areaSQ &&
1052 ith_triangle->centroid.getDistanceFromSQ(center) < candidate_triangle->centroid.getDistanceFromSQ(center))) {
1053 candidate_triangle = ith_triangle;
1056 return candidate_triangle;
1059 s32 MapBlockBspTree::buildTree(v3f normal, v3f origin, float delta, const std::vector<s32> &list, u32 depth)
1061 // if the list is empty, don't bother
1065 // if there is only one triangle, or the delta is insanely small, this is a leaf node
1066 if (list.size() == 1 || delta < 0.01) {
1067 nodes.emplace_back(normal, origin, list, -1, -1);
1068 return nodes.size() - 1;
1071 std::vector<s32> front_list;
1072 std::vector<s32> back_list;
1073 std::vector<s32> node_list;
1076 for (s32 i : list) {
1077 const MeshTriangle &triangle = (*triangles)[i];
1078 float factor = normal.dotProduct(triangle.centroid - origin);
1080 node_list.push_back(i);
1081 else if (factor > 0)
1082 front_list.push_back(i);
1084 back_list.push_back(i);
1087 // define the new split-plane
1088 v3f candidate_normal(normal.Z, normal.X, normal.Y);
1089 float candidate_delta = delta;
1091 candidate_delta /= 2;
1093 s32 front_index = -1;
1094 s32 back_index = -1;
1096 if (!front_list.empty()) {
1097 v3f next_normal = candidate_normal;
1098 v3f next_origin = origin + delta * normal;
1099 float next_delta = candidate_delta;
1100 if (next_delta < 10) {
1101 const MeshTriangle *candidate = findSplitCandidate(front_list, *triangles);
1102 next_normal = candidate->getNormal();
1103 next_origin = candidate->centroid;
1105 front_index = buildTree(next_normal, next_origin, next_delta, front_list, depth + 1);
1107 // if there are no other triangles, don't create a new node
1108 if (back_list.empty() && node_list.empty())
1112 if (!back_list.empty()) {
1113 v3f next_normal = candidate_normal;
1114 v3f next_origin = origin - delta * normal;
1115 float next_delta = candidate_delta;
1116 if (next_delta < 10) {
1117 const MeshTriangle *candidate = findSplitCandidate(back_list, *triangles);
1118 next_normal = candidate->getNormal();
1119 next_origin = candidate->centroid;
1122 back_index = buildTree(next_normal, next_origin, next_delta, back_list, depth + 1);
1124 // if there are no other triangles, don't create a new node
1125 if (front_list.empty() && node_list.empty())
1129 nodes.emplace_back(normal, origin, node_list, front_index, back_index);
1131 return nodes.size() - 1;
1134 void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector<s32> &output) const
1136 if (node < 0) return; // recursion break;
1138 const TreeNode &n = nodes[node];
1139 float factor = n.normal.dotProduct(viewpoint - n.origin);
1142 traverse(n.back_ref, viewpoint, output);
1144 traverse(n.front_ref, viewpoint, output);
1147 for (s32 i : n.triangle_refs)
1148 output.push_back(i);
1151 traverse(n.front_ref, viewpoint, output);
1153 traverse(n.back_ref, viewpoint, output);
1162 void PartialMeshBuffer::beforeDraw() const
1164 // Patch the indexes in the mesh buffer before draw
1165 m_buffer->Indices = std::move(m_vertex_indexes);
1166 m_buffer->setDirty(scene::EBT_INDEX);
1169 void PartialMeshBuffer::afterDraw() const
1171 // Take the data back
1172 m_vertex_indexes = std::move(m_buffer->Indices.steal());
1179 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1180 m_minimap_mapblock(NULL),
1181 m_tsrc(data->m_client->getTextureSource()),
1182 m_shdrsrc(data->m_client->getShaderSource()),
1183 m_animation_force_timer(0), // force initial animation
1185 m_last_daynight_ratio((u32) -1)
1187 for (auto &m : m_mesh)
1188 m = new scene::SMesh();
1189 m_enable_shaders = data->m_use_shaders;
1190 m_enable_vbo = g_settings->getBool("enable_vbo");
1192 if (data->m_client->getMinimap()) {
1193 m_minimap_mapblock = new MinimapMapblock;
1194 m_minimap_mapblock->getMinimapNodes(
1195 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1198 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1199 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1200 //TimeTaker timer1("MapBlockMesh()");
1202 std::vector<FastFace> fastfaces_new;
1203 fastfaces_new.reserve(512);
1206 We are including the faces of the trailing edges of the block.
1207 This means that when something changes, the caller must
1208 also update the meshes of the blocks at the leading edges.
1210 NOTE: This is the slowest part of this method.
1213 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1214 //TimeTaker timer2("updateAllFastFaceRows()");
1215 updateAllFastFaceRows(data, fastfaces_new);
1220 Convert FastFaces to MeshCollector
1223 MeshCollector collector;
1226 // avg 0ms (100ms spikes when loading textures the first time)
1227 // (NOTE: probably outdated)
1228 //TimeTaker timer2("MeshCollector building");
1230 for (const FastFace &f : fastfaces_new) {
1231 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1232 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1233 const u16 *indices_p =
1234 f.vertex_0_2_connected ? indices : indices_alternate;
1235 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1240 Add special graphics:
1248 MapblockMeshGenerator(data, &collector,
1249 data->m_client->getSceneManager()->getMeshManipulator()).generate();
1253 Convert MeshCollector to SMesh
1256 const bool desync_animations = g_settings->getBool(
1257 "desynchronize_mapblock_texture_animation");
1259 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1260 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1262 PreMeshBuffer &p = collector.prebuffers[layer][i];
1266 // Generate animation data
1268 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1269 // Find the texture name plus ^[crack:N:
1270 std::ostringstream os(std::ios::binary);
1271 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1272 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1273 os << "o"; // use ^[cracko
1274 u8 tiles = p.layer.scale;
1276 os << ":" << (u32)tiles;
1277 os << ":" << (u32)p.layer.animation_frame_count << ":";
1278 m_crack_materials.insert(std::make_pair(
1279 std::pair<u8, u32>(layer, i), os.str()));
1280 // Replace tile texture with the cracked one
1281 p.layer.texture = m_tsrc->getTextureForMesh(
1283 &p.layer.texture_id);
1285 // - Texture animation
1286 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1287 // Add to MapBlockMesh in order to animate these tiles
1288 auto &info = m_animation_info[{layer, i}];
1289 info.tile = p.layer;
1291 if (desync_animations) {
1292 // Get starting position from noise
1294 100000 * (2.0 + noise3d(
1295 data->m_blockpos.X, data->m_blockpos.Y,
1296 data->m_blockpos.Z, 0));
1298 // Play all synchronized
1299 info.frame_offset = 0;
1301 // Replace tile texture with the first animation frame
1302 p.layer.texture = (*p.layer.frames)[0].texture;
1305 if (!m_enable_shaders) {
1306 // Extract colors for day-night animation
1307 // Dummy sunlight to handle non-sunlit areas
1308 video::SColorf sunlight;
1309 get_sunlight_color(&sunlight, 0);
1311 std::map<u32, video::SColor> colors;
1312 const u32 vertex_count = p.vertices.size();
1313 for (u32 j = 0; j < vertex_count; j++) {
1314 video::SColor *vc = &p.vertices[j].Color;
1315 video::SColor copy = *vc;
1316 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1317 final_color_blend(vc, copy, sunlight); // Finalize color
1318 else // Record color to animate
1321 // The sunlight ratio has been stored,
1322 // delete alpha (for the final rendering).
1325 if (!colors.empty())
1326 m_daynight_diffs[{layer, i}] = std::move(colors);
1330 video::SMaterial material;
1331 material.setFlag(video::EMF_LIGHTING, false);
1332 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1333 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1334 material.setFlag(video::EMF_FOG_ENABLE, true);
1335 material.setTexture(0, p.layer.texture);
1337 if (m_enable_shaders) {
1338 material.MaterialType = m_shdrsrc->getShaderInfo(
1339 p.layer.shader_id).material;
1340 p.layer.applyMaterialOptionsWithShaders(material);
1341 if (p.layer.normal_texture)
1342 material.setTexture(1, p.layer.normal_texture);
1343 material.setTexture(2, p.layer.flags_texture);
1345 p.layer.applyMaterialOptions(material);
1348 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1350 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1351 buf->Material = material;
1352 switch (p.layer.material_type) {
1353 // list of transparent materials taken from tile.h
1354 case TILE_MATERIAL_ALPHA:
1355 case TILE_MATERIAL_LIQUID_TRANSPARENT:
1356 case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
1358 buf->append(&p.vertices[0], p.vertices.size(),
1363 for (u32 i = 0; i < p.indices.size(); i += 3) {
1364 t.p1 = p.indices[i];
1365 t.p2 = p.indices[i + 1];
1366 t.p3 = p.indices[i + 2];
1367 t.updateAttributes();
1368 m_transparent_triangles.push_back(t);
1373 buf->append(&p.vertices[0], p.vertices.size(),
1374 &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);