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, bool use_tangent_vertices) :
39 m_client(client), m_use_shaders(use_shaders),
40 m_use_tangent_vertices(use_tangent_vertices)
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 -
54 m_vmanip.addArea(voxel_area);
57 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
59 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
60 VoxelArea data_area(v3s16(0, 0, 0), data_size - v3s16(1, 1, 1));
62 v3s16 bp = m_blockpos + block_offset;
63 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
64 m_vmanip.copyFrom(data, data_area, v3s16(0, 0, 0), blockpos_nodes, data_size);
67 void MeshMakeData::fill(MapBlock *block)
69 fillBlockDataBegin(block->getPos());
71 fillBlockData(v3s16(0, 0, 0), block->getData());
73 // Get map for reading neighbor blocks
74 Map *map = block->getParent();
76 for (const v3s16 &dir : g_26dirs) {
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
80 fillBlockData(dir, b->getData());
84 void MeshMakeData::fillSingleNode(MapNode *node)
86 m_blockpos = v3s16(0, 0, 0);
88 v3s16 blockpos_nodes = v3s16(0, 0, 0);
89 VoxelArea area(blockpos_nodes - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
90 blockpos_nodes + v3s16(1, 1, 1) * MAP_BLOCKSIZE * 2 -
92 s32 volume = area.getVolume();
93 s32 our_node_index = area.index(1, 1, 1);
95 // Allocate this block + neighbors
97 m_vmanip.addArea(area);
100 MapNode *data = new MapNode[volume];
101 for (s32 i = 0; i < volume; i++) {
102 if (i == our_node_index)
105 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
107 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
111 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
113 if (crack_level >= 0)
114 m_crack_pos_relative = crack_pos - m_blockpos * MAP_BLOCKSIZE;
117 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
119 m_smooth_lighting = smooth_lighting && !g_settings->getBool("fullbright");
123 Light and vertex color functions
127 Calculate non-smooth lighting at interior of node.
130 static u8 getInteriorLight(
131 enum LightBank bank, MapNode n, s32 increment, const NodeDefManager *ndef)
133 u8 light = n.getLight(bank, ndef);
135 light = rangelim(light + increment, 0, LIGHT_SUN);
136 if (g_settings->getBool("fullbright"))
138 return decode_light(light);
142 Calculate non-smooth lighting at interior of node.
145 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
147 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
148 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
149 return day | (night << 8);
153 Calculate non-smooth lighting at face of node.
156 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2, v3s16 face_dir,
157 const NodeDefManager *ndef)
160 u8 l1 = n.getLight(bank, ndef);
161 u8 l2 = n2.getLight(bank, ndef);
167 // Boost light level for light sources
168 u8 light_source = MYMAX(ndef->get(n).light_source, ndef->get(n2).light_source);
169 if (light_source > light)
170 light = light_source;
171 if (g_settings->getBool("fullbright"))
173 return decode_light(light);
177 Calculate non-smooth lighting at face of node.
180 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir, const NodeDefManager *ndef)
182 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
183 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
184 return day | (night << 8);
188 Calculate smooth lighting at the XYZ- corner of p.
191 static u16 getSmoothLightCombined(
192 const v3s16 &p, const std::array<v3s16, 8> &dirs, MeshMakeData *data)
194 const NodeDefManager *ndef = data->m_client->ndef();
196 u16 ambient_occlusion = 0;
198 u8 light_source_max = 0;
201 bool direct_sunlight = false;
203 auto add_node = [&](u8 i, bool obstructed = false) -> bool {
208 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
209 if (n.getContent() == CONTENT_IGNORE)
211 const ContentFeatures &f = ndef->get(n);
212 if (f.light_source > light_source_max)
213 light_source_max = f.light_source;
214 // Check f.solidness because fast-style leaves look better this way
215 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
216 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
217 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
218 if (light_level_day == LIGHT_SUN)
219 direct_sunlight = true;
220 light_day += decode_light(light_level_day);
221 light_night += decode_light(light_level_night);
226 return f.light_propagates;
229 bool obstructed[4] = {true, true, true, true};
231 bool opaque1 = !add_node(1);
232 bool opaque2 = !add_node(2);
233 bool opaque3 = !add_node(3);
234 obstructed[0] = opaque1 && opaque2;
235 obstructed[1] = opaque1 && opaque3;
236 obstructed[2] = opaque2 && opaque3;
237 for (u8 k = 0; k < 3; ++k)
238 if (add_node(k + 4, obstructed[k]))
239 obstructed[3] = false;
240 if (add_node(7, obstructed[3])) { // wrap light around nodes
241 ambient_occlusion -= 3;
242 for (u8 k = 0; k < 3; ++k)
243 add_node(k + 4, !obstructed[k]);
246 if (light_count == 0) {
247 light_day = light_night = 0;
249 light_day /= light_count;
250 light_night /= light_count;
253 // boost direct sunlight, if any
257 // Boost brightness around light sources
258 bool skip_ambient_occlusion_day = false;
259 if (decode_light(light_source_max) >= light_day) {
260 light_day = decode_light(light_source_max);
261 skip_ambient_occlusion_day = true;
264 bool skip_ambient_occlusion_night = false;
265 if (decode_light(light_source_max) >= light_night) {
266 light_night = decode_light(light_source_max);
267 skip_ambient_occlusion_night = true;
270 if (ambient_occlusion > 4) {
271 static thread_local const float ao_gamma =
272 rangelim(g_settings->getFloat("ambient_occlusion_gamma"),
275 // Table of gamma space multiply factors.
276 static thread_local const float light_amount[3] = {
277 powf(0.75, 1.0 / ao_gamma), powf(0.5, 1.0 / ao_gamma),
278 powf(0.25, 1.0 / ao_gamma)};
280 // calculate table index for gamma space multiplier
281 ambient_occlusion -= 5;
283 if (!skip_ambient_occlusion_day)
284 light_day = rangelim(
285 core::round32(light_day *
286 light_amount[ambient_occlusion]),
288 if (!skip_ambient_occlusion_night)
289 light_night = rangelim(
290 core::round32(light_night *
291 light_amount[ambient_occlusion]),
295 return light_day | (light_night << 8);
299 Calculate smooth lighting at the given corner of p.
301 Node at p is solid, and thus the lighting is face-dependent.
303 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner,
306 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
310 Calculate smooth lighting at the given corner of p.
312 Node at p is not solid, and the lighting is not face-dependent.
314 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
316 const std::array<v3s16, 8> dirs = {{// Always shine light
317 v3s16(0, 0, 0), v3s16(corner.X, 0, 0), v3s16(0, corner.Y, 0),
318 v3s16(0, 0, corner.Z),
321 v3s16(corner.X, corner.Y, 0), v3s16(corner.X, 0, corner.Z),
322 v3s16(0, corner.Y, corner.Z),
323 v3s16(corner.X, corner.Y, corner.Z)}};
324 return getSmoothLightCombined(p, dirs, data);
327 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio)
329 f32 rg = daynight_ratio / 1000.0f - 0.04f;
330 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
336 void final_color_blend(video::SColor *result, u16 light, u32 daynight_ratio)
338 video::SColorf dayLight;
339 get_sunlight_color(&dayLight, daynight_ratio);
340 final_color_blend(result, encode_light(light, 0), dayLight);
343 void final_color_blend(video::SColor *result, const video::SColor &data,
344 const video::SColorf &dayLight)
346 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
348 video::SColorf c(data);
351 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
352 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
353 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
355 // Emphase blue a bit in darker places
356 // Each entry of this array represents a range of 8 blue levels
357 static const u8 emphase_blue_when_dark[32] = {
392 b += emphase_blue_when_dark[irr::core::clamp((s32)((r + g + b) / 3 * 255), 0,
397 result->setRed(core::clamp((s32)(r * 255.0f), 0, 255));
398 result->setGreen(core::clamp((s32)(g * 255.0f), 0, 255));
399 result->setBlue(core::clamp((s32)(b * 255.0f), 0, 255));
403 Mesh generation helpers
406 // This table is moved outside getNodeVertexDirs to avoid the compiler using
407 // a mutex to initialize this table at runtime right in the hot path.
408 // For details search the internet for "cxa_guard_acquire".
409 static const v3s16 vertex_dirs_table[] = {
411 v3s16(1, -1, 1), v3s16(1, -1, -1), v3s16(1, 1, -1), v3s16(1, 1, 1),
413 v3s16(1, 1, -1), v3s16(-1, 1, -1), v3s16(-1, 1, 1), v3s16(1, 1, 1),
415 v3s16(-1, -1, 1), v3s16(1, -1, 1), v3s16(1, 1, 1), v3s16(-1, 1, 1),
417 v3s16(), v3s16(), v3s16(), v3s16(),
419 v3s16(1, -1, -1), v3s16(-1, -1, -1), v3s16(-1, 1, -1), v3s16(1, 1, -1),
421 v3s16(1, -1, 1), v3s16(-1, -1, 1), v3s16(-1, -1, -1), v3s16(1, -1, -1),
423 v3s16(-1, -1, -1), v3s16(-1, -1, 1), v3s16(-1, 1, 1), v3s16(-1, 1, -1)};
426 vertex_dirs: v3s16[4]
428 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
431 If looked from outside the node towards the face, the corners are:
438 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
440 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
442 // Convert direction to single integer for table lookup
443 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
446 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
449 static void getNodeTextureCoords(
450 v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
452 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
454 if (dir == v3s16(0, 0, 1)) {
457 } else if (dir == v3s16(0, 0, -1)) {
460 } else if (dir == v3s16(1, 0, 0)) {
463 } else if (dir == v3s16(-1, 0, 0)) {
466 } else if (dir == v3s16(0, 1, 0)) {
469 } else if (dir == v3s16(0, -1, 0)) {
478 video::S3DVertex vertices[4]; // Precalculated vertices
480 * The face is divided into two triangles. If this is true,
481 * vertices 0 and 2 are connected, othervise vertices 1 and 3
484 bool vertex_0_2_connected;
487 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
488 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale,
489 std::vector<FastFace> &dest)
491 // Position is at the center of the cube.
500 v3s16 vertex_dirs[4];
501 getNodeVertexDirs(dir, vertex_dirs);
502 if (tile.world_aligned)
503 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
507 switch (tile.rotation) {
512 vertex_dirs[0] = vertex_dirs[3];
513 vertex_dirs[3] = vertex_dirs[2];
514 vertex_dirs[2] = vertex_dirs[1];
524 vertex_dirs[0] = vertex_dirs[2];
527 vertex_dirs[1] = vertex_dirs[3];
538 vertex_dirs[0] = vertex_dirs[1];
539 vertex_dirs[1] = vertex_dirs[2];
540 vertex_dirs[2] = vertex_dirs[3];
550 vertex_dirs[0] = vertex_dirs[3];
551 vertex_dirs[3] = vertex_dirs[2];
552 vertex_dirs[2] = vertex_dirs[1];
564 vertex_dirs[0] = vertex_dirs[1];
565 vertex_dirs[1] = vertex_dirs[2];
566 vertex_dirs[2] = vertex_dirs[3];
578 vertex_dirs[0] = vertex_dirs[3];
579 vertex_dirs[3] = vertex_dirs[2];
580 vertex_dirs[2] = vertex_dirs[1];
592 vertex_dirs[0] = vertex_dirs[1];
593 vertex_dirs[1] = vertex_dirs[2];
594 vertex_dirs[2] = vertex_dirs[3];
616 for (u16 i = 0; i < 4; i++) {
617 vertex_pos[i] = v3f(BS / 2 * vertex_dirs[i].X, BS / 2 * vertex_dirs[i].Y,
618 BS / 2 * vertex_dirs[i].Z);
621 for (v3f &vpos : vertex_pos) {
628 f32 abs_scale = 1.0f;
629 if (scale.X < 0.999f || scale.X > 1.001f)
631 else if (scale.Y < 0.999f || scale.Y > 1.001f)
633 else if (scale.Z < 0.999f || scale.Z > 1.001f)
636 v3f normal(dir.X, dir.Y, dir.Z);
638 u16 li[4] = {li0, li1, li2, li3};
642 for (u8 i = 0; i < 4; i++) {
644 night[i] = li[i] & 0xFF;
647 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2]) <
648 abs(day[1] - day[3]) + abs(night[1] - night[3]);
650 v2f32 f[4] = {core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
651 core::vector2d<f32>(x0, y0 + h), core::vector2d<f32>(x0, y0),
652 core::vector2d<f32>(x0 + w * abs_scale, y0)};
654 // equivalent to dest.push_back(FastFace()) but faster
656 FastFace &face = *dest.rbegin();
658 for (u8 i = 0; i < 4; i++) {
659 video::SColor c = encode_light(li[i], tile.emissive_light);
660 if (!tile.emissive_light)
661 applyFacesShading(c, normal);
663 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
667 Revert triangles for nicer looking gradient if the
668 brightness of vertices 1 and 3 differ less than
669 the brightness of vertices 0 and 2.
671 face.vertex_0_2_connected = vertex_0_2_connected;
676 Nodes make a face if contents differ and solidness differs.
679 1: Face uses m1's content
680 2: Face uses m2's content
681 equivalent: Whether the blocks share the same face (eg. water and glass)
683 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
685 static u8 face_contents(
686 content_t m1, content_t m2, bool *equivalent, const NodeDefManager *ndef)
690 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
693 const ContentFeatures &f1 = ndef->get(m1);
694 const ContentFeatures &f2 = ndef->get(m2);
696 // Contents don't differ for different forms of same liquid
697 if (f1.sameLiquid(f2))
700 u8 c1 = f1.solidness;
701 u8 c2 = f2.solidness;
707 c1 = f1.visual_solidness;
709 c2 = f2.visual_solidness;
713 // If same solidness, liquid takes precense
727 Gets nth node tile (0 <= n <= 5).
729 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data,
732 const NodeDefManager *ndef = data->m_client->ndef();
733 const ContentFeatures &f = ndef->get(mn);
734 tile = f.tiles[tileindex];
735 bool has_crack = p == data->m_crack_pos_relative;
736 for (TileLayer &layer : tile.layers) {
737 if (layer.texture_id == 0)
739 if (!layer.has_color)
740 mn.getColor(f, &(layer.color));
741 // Apply temporary crack
743 layer.material_flags |= MATERIAL_FLAG_CRACK;
748 Gets node tile given a face direction.
750 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data,
753 const NodeDefManager *ndef = data->m_client->ndef();
755 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
756 // (0,0,1), (0,0,-1) or (0,0,0)
757 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
759 // Convert direction to single integer for table lookup
764 // 4 = invalid, treat as (0,0,0)
768 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
770 // Get rotation for things like chests
771 u8 facedir = mn.getFaceDir(ndef, true);
773 static const u16 dir_to_tile[24 * 16] = {// 0 +X +Y +Z -Z -Y
774 // -X -> value=tile,rotation
775 0, 0, 2, 0, 0, 0, 4, 0, 0, 0, 5, 0, 1, 0, 3,
776 0, // rotate around y+ 0 - 3
777 0, 0, 4, 0, 0, 3, 3, 0, 0, 0, 2, 0, 1, 1, 5, 0, 0, 0, 3, 0, 0, 2,
778 5, 0, 0, 0, 4, 0, 1, 2, 2, 0, 0, 0, 5, 0, 0, 1, 2, 0, 0, 0, 3, 0,
781 0, 0, 2, 3, 5, 0, 0, 2, 0, 0, 1, 0, 4, 2, 3,
782 1, // rotate around z+ 4 - 7
783 0, 0, 4, 3, 2, 0, 0, 1, 0, 0, 1, 1, 3, 2, 5, 1, 0, 0, 3, 3, 4, 0,
784 0, 0, 0, 0, 1, 2, 5, 2, 2, 1, 0, 0, 5, 3, 3, 0, 0, 3, 0, 0, 1, 3,
787 0, 0, 2, 1, 4, 2, 1, 2, 0, 0, 0, 0, 5, 0, 3,
788 3, // rotate around z- 8 - 11
789 0, 0, 4, 1, 3, 2, 1, 3, 0, 0, 0, 3, 2, 0, 5, 3, 0, 0, 3, 1, 5, 2,
790 1, 0, 0, 0, 0, 2, 4, 0, 2, 3, 0, 0, 5, 1, 2, 2, 1, 1, 0, 0, 0, 1,
793 0, 0, 0, 3, 3, 3, 4, 1, 0, 0, 5, 3, 2, 3, 1,
794 3, // rotate around x+ 12 - 15
795 0, 0, 0, 2, 5, 3, 3, 1, 0, 0, 2, 3, 4, 3, 1, 0, 0, 0, 0, 1, 2, 3,
796 5, 1, 0, 0, 4, 3, 3, 3, 1, 1, 0, 0, 0, 0, 4, 3, 2, 1, 0, 0, 3, 3,
799 0, 0, 1, 1, 2, 1, 4, 3, 0, 0, 5, 1, 3, 1, 0,
800 1, // rotate around x- 16 - 19
801 0, 0, 1, 2, 4, 1, 3, 3, 0, 0, 2, 1, 5, 1, 0, 0, 0, 0, 1, 3, 3, 1,
802 5, 3, 0, 0, 4, 1, 2, 1, 0, 3, 0, 0, 1, 0, 5, 1, 2, 3, 0, 0, 3, 1,
805 0, 0, 3, 2, 1, 2, 4, 2, 0, 0, 5, 2, 0, 2, 2,
806 2, // rotate around y- 20 - 23
807 0, 0, 5, 2, 1, 3, 3, 2, 0, 0, 2, 2, 0, 1, 4, 2, 0, 0, 2, 2, 1, 0,
808 5, 2, 0, 0, 4, 2, 0, 0, 3, 2, 0, 0, 4, 2, 1, 1, 2, 2, 0, 0, 3, 2,
812 u16 tile_index = facedir * 16 + dir_i;
813 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
814 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
817 std::set<content_t> splitToContentT(std::string str, const NodeDefManager *ndef)
820 std::set<content_t> dat;
823 if (c == ',' || c == '\n') {
825 dat.insert(ndef->getId(buf));
828 } else if (c != ' ') {
835 static void getTileInfo(
837 MeshMakeData *data, const v3s16 &p, const v3s16 &face_dir,
839 bool &makes_face, v3s16 &p_corrected, v3s16 &face_dir_corrected,
840 u16 *lights, u8 &waving, TileSpec &tile, bool xray,
841 std::set<content_t> xraySet)
843 VoxelManipulator &vmanip = data->m_vmanip;
844 const NodeDefManager *ndef = data->m_client->ndef();
845 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
847 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
849 content_t c0 = n0.getContent();
850 if (xray && xraySet.find(c0) != xraySet.end())
853 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
854 if (c0 == CONTENT_IGNORE) {
860 vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
862 content_t c1 = n1.getContent();
863 if (xray && xraySet.find(c1) != xraySet.end())
866 if (c1 == CONTENT_IGNORE) {
872 bool equivalent = false;
873 u8 mf = face_contents(c0, c1, &equivalent, ndef);
886 face_dir_corrected = face_dir;
889 p_corrected = p + face_dir;
890 face_dir_corrected = -face_dir;
893 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
894 const ContentFeatures &f = ndef->get(n);
896 tile.emissive_light = f.light_source;
898 // eg. water and glass
900 for (TileLayer &layer : tile.layers)
901 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
904 if (!data->m_smooth_lighting) {
905 lights[0] = lights[1] = lights[2] = lights[3] =
906 getFaceLight(n0, n1, face_dir, ndef);
908 v3s16 vertex_dirs[4];
909 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
911 v3s16 light_p = blockpos_nodes + p_corrected;
912 for (u16 i = 0; i < 4; i++)
913 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected,
914 vertex_dirs[i], data);
920 translate_dir: unit vector with only one of x, y or z
921 face_dir: unit vector with only one of x, y or z
923 static void updateFastFaceRow(MeshMakeData *data, const v3s16 &&startpos,
924 v3s16 translate_dir, const v3f &&translate_dir_f, const v3s16 &&face_dir,
925 std::vector<FastFace> &dest, bool xray, std::set<content_t> xraySet)
927 static thread_local const bool waving_liquids =
928 g_settings->getBool("enable_shaders") &&
929 g_settings->getBool("enable_waving_water");
933 u16 continuous_tiles_count = 1;
935 bool makes_face = false;
937 v3s16 face_dir_corrected;
938 u16 lights[4] = {0, 0, 0, 0};
942 // Get info of first tile
943 getTileInfo(data, p, face_dir, makes_face, p_corrected, face_dir_corrected,
944 lights, waving, tile, xray, xraySet);
946 // Unroll this variable which has a significant build cost
948 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
949 // If tiling can be done, this is set to false in the next step
950 bool next_is_different = true;
952 bool next_makes_face = false;
953 v3s16 next_p_corrected;
954 v3s16 next_face_dir_corrected;
955 u16 next_lights[4] = {0, 0, 0, 0};
957 // If at last position, there is nothing to compare to and
958 // the face must be drawn anyway
959 if (j != MAP_BLOCKSIZE - 1) {
962 getTileInfo(data, p, face_dir, next_makes_face, next_p_corrected,
963 next_face_dir_corrected, next_lights, waving,
964 next_tile, xray, xraySet);
966 if (next_makes_face == makes_face &&
967 next_p_corrected == p_corrected + translate_dir &&
968 next_face_dir_corrected == face_dir_corrected &&
969 memcmp(next_lights, lights, sizeof(lights)) == 0
970 // Don't apply fast faces to waving water.
971 && (waving != 3 || !waving_liquids) &&
972 next_tile.isTileable(tile)) {
973 next_is_different = false;
974 continuous_tiles_count++;
977 if (next_is_different) {
979 Create a face if there should be one
982 // Floating point conversion of the position vector
983 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
984 // Center point of face (kind of)
986 ((f32)continuous_tiles_count * 0.5f - 0.5f) *
990 if (translate_dir.X != 0)
991 scale.X = continuous_tiles_count;
992 if (translate_dir.Y != 0)
993 scale.Y = continuous_tiles_count;
994 if (translate_dir.Z != 0)
995 scale.Z = continuous_tiles_count;
997 makeFastFace(tile, lights[0], lights[1], lights[2],
998 lights[3], pf, sp, face_dir_corrected,
1000 g_profiler->avg("Meshgen: Tiles per face [#]",
1001 continuous_tiles_count);
1004 continuous_tiles_count = 1;
1007 makes_face = next_makes_face;
1008 p_corrected = next_p_corrected;
1009 face_dir_corrected = next_face_dir_corrected;
1010 memcpy(lights, next_lights, sizeof(lights));
1011 if (next_is_different)
1012 tile = std::move(next_tile); // faster than copy
1016 static void updateAllFastFaceRows(MeshMakeData *data, std::vector<FastFace> &dest,
1017 bool xray, std::set<content_t> xraySet)
1020 Go through every y,z and get top(y+) faces in rows of x+
1022 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1023 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1024 updateFastFaceRow(data, v3s16(0, y, z), v3s16(1, 0, 0), // dir
1025 v3f(1, 0, 0), v3s16(0, 1, 0), // face dir
1026 dest, xray, xraySet);
1029 Go through every x,y and get right(x+) faces in rows of z+
1031 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
1032 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1033 updateFastFaceRow(data, v3s16(x, y, 0), v3s16(0, 0, 1), // dir
1034 v3f(0, 0, 1), v3s16(1, 0, 0), // face dir
1035 dest, xray, xraySet);
1038 Go through every y,z and get back(z+) faces in rows of x+
1040 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1041 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1042 updateFastFaceRow(data, v3s16(0, y, z), v3s16(1, 0, 0), // dir
1043 v3f(1, 0, 0), v3s16(0, 0, 1), // face dir
1044 dest, xray, xraySet);
1047 static void applyTileColor(PreMeshBuffer &pmb)
1049 video::SColor tc = pmb.layer.color;
1050 if (tc == video::SColor(0xFFFFFFFF))
1052 for (video::S3DVertex &vertex : pmb.vertices) {
1053 video::SColor *c = &vertex.Color;
1054 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1055 c->getGreen() * tc.getGreen() / 255,
1056 c->getBlue() * tc.getBlue() / 255);
1064 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset) :
1065 m_minimap_mapblock(NULL), m_tsrc(data->m_client->getTextureSource()),
1066 m_shdrsrc(data->m_client->getShaderSource()),
1067 m_animation_force_timer(0), // force initial animation
1068 m_last_crack(-1), m_last_daynight_ratio((u32)-1)
1070 for (auto &m : m_mesh)
1071 m = new scene::SMesh();
1072 m_enable_shaders = data->m_use_shaders;
1073 m_use_tangent_vertices = data->m_use_tangent_vertices;
1074 m_enable_vbo = g_settings->getBool("enable_vbo");
1076 if (g_settings->getBool("enable_minimap")) {
1077 m_minimap_mapblock = new MinimapMapblock;
1078 m_minimap_mapblock->getMinimapNodes(
1079 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1082 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1083 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1084 // TimeTaker timer1("MapBlockMesh()");
1086 std::vector<FastFace> fastfaces_new;
1087 fastfaces_new.reserve(512);
1091 bool xray = g_settings->getBool("xray");
1092 std::set<content_t> xraySet;
1094 xraySet = splitToContentT(
1095 g_settings->get("xray_nodes"), data->m_client->ndef());
1098 We are including the faces of the trailing edges of the block.
1099 This means that when something changes, the caller must
1100 also update the meshes of the blocks at the leading edges.
1102 NOTE: This is the slowest part of this method.
1105 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1106 // TimeTaker timer2("updateAllFastFaceRows()");
1107 updateAllFastFaceRows(data, fastfaces_new, xray, xraySet);
1112 Convert FastFaces to MeshCollector
1115 MeshCollector collector;
1118 // avg 0ms (100ms spikes when loading textures the first time)
1119 // (NOTE: probably outdated)
1120 // TimeTaker timer2("MeshCollector building");
1122 for (const FastFace &f : fastfaces_new) {
1123 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1124 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1125 const u16 *indices_p = f.vertex_0_2_connected ? indices
1126 : indices_alternate;
1127 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1132 Add special graphics:
1140 MapblockMeshGenerator generator(data, &collector);
1141 generator.generate();
1145 Convert MeshCollector to SMesh
1148 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1149 for (u32 i = 0; i < collector.prebuffers[layer].size(); i++) {
1150 PreMeshBuffer &p = collector.prebuffers[layer][i];
1154 // Generate animation data
1156 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1157 // Find the texture name plus ^[crack:N:
1158 std::ostringstream os(std::ios::binary);
1159 os << m_tsrc->getTextureName(p.layer.texture_id)
1161 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1162 os << "o"; // use ^[cracko
1163 u8 tiles = p.layer.scale;
1165 os << ":" << (u32)tiles;
1166 os << ":" << (u32)p.layer.animation_frame_count << ":";
1167 m_crack_materials.insert(std::make_pair(
1168 std::pair<u8, u32>(layer, i), os.str()));
1169 // Replace tile texture with the cracked one
1170 p.layer.texture = m_tsrc->getTextureForMesh(
1171 os.str() + "0", &p.layer.texture_id);
1173 // - Texture animation
1174 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1175 // Add to MapBlockMesh in order to animate these tiles
1176 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1177 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1178 if (g_settings->getBool("desynchronize_mapblock_texture_"
1180 // Get starting position from noise
1181 m_animation_frame_offsets[std::pair<u8, u32>(
1184 (2.0 + noise3d(data->m_blockpos.X,
1189 // Play all synchronized
1190 m_animation_frame_offsets[std::pair<u8, u32>(
1193 // Replace tile texture with the first animation frame
1194 p.layer.texture = (*p.layer.frames)[0].texture;
1197 if (!m_enable_shaders) {
1198 // Extract colors for day-night animation
1199 // Dummy sunlight to handle non-sunlit areas
1200 video::SColorf sunlight;
1201 get_sunlight_color(&sunlight, 0);
1202 u32 vertex_count = p.vertices.size();
1203 for (u32 j = 0; j < vertex_count; j++) {
1204 video::SColor *vc = &p.vertices[j].Color;
1205 video::SColor copy = *vc;
1206 if (vc->getAlpha() == 0) // No sunlight - no need
1208 final_color_blend(vc, copy,
1209 sunlight); // Finalize
1211 else // Record color to animate
1212 m_daynight_diffs[std::pair<u8, u32>(
1213 layer, i)][j] = copy;
1215 // The sunlight ratio has been stored,
1216 // delete alpha (for the final rendering).
1222 video::SMaterial material;
1223 material.setFlag(video::EMF_LIGHTING, false);
1224 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1225 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1226 material.setFlag(video::EMF_FOG_ENABLE, true);
1227 material.setTexture(0, p.layer.texture);
1229 if (m_enable_shaders) {
1230 material.MaterialType =
1231 m_shdrsrc->getShaderInfo(p.layer.shader_id)
1233 p.layer.applyMaterialOptionsWithShaders(material);
1234 if (p.layer.normal_texture)
1235 material.setTexture(1, p.layer.normal_texture);
1236 material.setTexture(2, p.layer.flags_texture);
1238 p.layer.applyMaterialOptions(material);
1241 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1243 // Create meshbuffer, add to mesh
1244 if (m_use_tangent_vertices) {
1245 scene::SMeshBufferTangents *buf =
1246 new scene::SMeshBufferTangents();
1247 buf->Material = material;
1248 buf->Vertices.reallocate(p.vertices.size());
1249 buf->Indices.reallocate(p.indices.size());
1250 for (const video::S3DVertex &v : p.vertices)
1251 buf->Vertices.push_back(video::S3DVertexTangents(
1252 v.Pos, v.Color, v.TCoords));
1253 for (u16 i : p.indices)
1254 buf->Indices.push_back(i);
1255 buf->recalculateBoundingBox();
1256 mesh->addMeshBuffer(buf);
1259 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1260 buf->Material = material;
1261 buf->append(&p.vertices[0], p.vertices.size(),
1262 &p.indices[0], p.indices.size());
1263 mesh->addMeshBuffer(buf);
1269 Do some stuff to the mesh
1271 m_camera_offset = camera_offset;
1272 translateMesh(m_mesh[layer],
1273 intToFloat(data->m_blockpos * MAP_BLOCKSIZE -
1277 if (m_use_tangent_vertices) {
1278 scene::IMeshManipulator *meshmanip =
1279 RenderingEngine::get_scene_manager()
1280 ->getMeshManipulator();
1281 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1284 if (m_mesh[layer]) {
1286 // Usually 1-700 faces and 1-7 materials
1287 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1288 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1289 << " materials (meshbuffers)" << std::endl;
1292 // Use VBO for mesh (this just would set this for ever buffer)
1294 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1298 // std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1300 // Check if animation is required for this mesh
1301 m_has_animation = !m_crack_materials.empty() || !m_daynight_diffs.empty() ||
1302 !m_animation_tiles.empty();
1305 MapBlockMesh::~MapBlockMesh()
1307 for (scene::IMesh *m : m_mesh) {
1308 if (m_enable_vbo && m)
1309 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1310 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1311 RenderingEngine::get_video_driver()->removeHardwareBuffer(
1317 delete m_minimap_mapblock;
1320 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1322 if (!m_has_animation) {
1323 m_animation_force_timer = 100000;
1327 m_animation_force_timer = myrand_range(5, 100);
1330 if (crack != m_last_crack) {
1331 for (auto &crack_material : m_crack_materials) {
1332 scene::IMeshBuffer *buf =
1333 m_mesh[crack_material.first.first]->getMeshBuffer(
1334 crack_material.first.second);
1335 std::string basename = crack_material.second;
1337 // Create new texture name from original
1338 std::ostringstream os;
1339 os << basename << crack;
1340 u32 new_texture_id = 0;
1341 video::ITexture *new_texture = m_tsrc->getTextureForMesh(
1342 os.str(), &new_texture_id);
1343 buf->getMaterial().setTexture(0, new_texture);
1345 // If the current material is also animated,
1346 // update animation info
1347 auto anim_iter = m_animation_tiles.find(crack_material.first);
1348 if (anim_iter != m_animation_tiles.end()) {
1349 TileLayer &tile = anim_iter->second;
1350 tile.texture = new_texture;
1351 tile.texture_id = new_texture_id;
1352 // force animation update
1353 m_animation_frames[crack_material.first] = -1;
1357 m_last_crack = crack;
1360 // Texture animation
1361 for (auto &animation_tile : m_animation_tiles) {
1362 const TileLayer &tile = animation_tile.second;
1363 // Figure out current frame
1364 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1365 int frame = (int)(time * 1000 / tile.animation_frame_length_ms +
1367 tile.animation_frame_count;
1368 // If frame doesn't change, skip
1369 if (frame == m_animation_frames[animation_tile.first])
1372 m_animation_frames[animation_tile.first] = frame;
1374 scene::IMeshBuffer *buf =
1375 m_mesh[animation_tile.first.first]->getMeshBuffer(
1376 animation_tile.first.second);
1378 const FrameSpec &animation_frame = (*tile.frames)[frame];
1379 buf->getMaterial().setTexture(0, animation_frame.texture);
1380 if (m_enable_shaders) {
1381 if (animation_frame.normal_texture)
1382 buf->getMaterial().setTexture(
1383 1, animation_frame.normal_texture);
1384 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1388 // Day-night transition
1389 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1390 // Force reload mesh to VBO
1392 for (scene::IMesh *m : m_mesh)
1394 video::SColorf day_color;
1395 get_sunlight_color(&day_color, daynight_ratio);
1397 for (auto &daynight_diff : m_daynight_diffs) {
1398 scene::IMeshBuffer *buf =
1399 m_mesh[daynight_diff.first.first]->getMeshBuffer(
1400 daynight_diff.first.second);
1401 video::S3DVertex *vertices =
1402 (video::S3DVertex *)buf->getVertices();
1403 for (const auto &j : daynight_diff.second)
1404 final_color_blend(&(vertices[j.first].Color), j.second,
1407 m_last_daynight_ratio = daynight_ratio;
1413 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1415 if (camera_offset != m_camera_offset) {
1416 for (scene::IMesh *layer : m_mesh) {
1417 translateMesh(layer,
1418 intToFloat(m_camera_offset - camera_offset, BS));
1422 m_camera_offset = camera_offset;
1426 video::SColor encode_light(u16 light, u8 emissive_light)
1429 u32 day = (light & 0xff);
1430 u32 night = (light >> 8);
1431 // Add emissive light
1432 night += emissive_light * 2.5f;
1435 // Since we don't know if the day light is sunlight or
1436 // artificial light, assume it is artificial when the night
1437 // light bank is also lit.
1442 u32 sum = day + night;
1443 // Ratio of sunlight:
1446 r = day * 255 / sum;
1450 float b = (day + night) / 2;
1451 return video::SColor(r, b, b, b);