3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "mapblock_mesh.h"
28 #include "content_mapblock.h"
29 #include "util/directiontables.h"
30 #include "client/meshgen/collector.h"
31 #include "client/renderingengine.h"
38 MeshMakeData::MeshMakeData(Client *client, bool use_shaders,
39 bool use_tangent_vertices):
41 m_use_shaders(use_shaders),
42 m_use_tangent_vertices(use_tangent_vertices)
45 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
47 m_blockpos = blockpos;
49 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
52 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
53 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
54 m_vmanip.addArea(voxel_area);
57 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
59 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
60 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
62 v3s16 bp = m_blockpos + block_offset;
63 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
64 m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
67 void MeshMakeData::fill(MapBlock *block)
69 fillBlockDataBegin(block->getPos());
71 fillBlockData(v3s16(0,0,0), block->getData());
73 // Get map for reading neighbor blocks
74 Map *map = block->getParent();
76 for (const v3s16 &dir : g_26dirs) {
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
80 fillBlockData(dir, b->getData());
84 void MeshMakeData::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-v3s16(1,1,1));
91 s32 volume = area.getVolume();
92 s32 our_node_index = area.index(1,1,1);
94 // Allocate this block + neighbors
96 m_vmanip.addArea(area);
99 MapNode *data = new MapNode[volume];
100 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(enum LightBank bank, MapNode n, s32 increment,
131 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,
157 v3s16 face_dir, 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,
169 ndef->get(n2).light_source);
170 if(light_source > light)
171 light = light_source;
172 if(g_settings->getBool("fullbright"))
174 return decode_light(light);
178 Calculate non-smooth lighting at face of node.
181 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
182 const NodeDefManager *ndef)
184 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
185 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
186 return day | (night << 8);
190 Calculate smooth lighting at the XYZ- corner of p.
193 static u16 getSmoothLightCombined(const v3s16 &p,
194 const std::array<v3s16,8> &dirs, MeshMakeData *data)
196 const NodeDefManager *ndef = data->m_client->ndef();
198 u16 ambient_occlusion = 0;
200 u8 light_source_max = 0;
203 bool direct_sunlight = false;
205 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
210 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
211 if (n.getContent() == CONTENT_IGNORE)
213 const ContentFeatures &f = ndef->get(n);
214 if (f.light_source > light_source_max)
215 light_source_max = f.light_source;
216 // Check f.solidness because fast-style leaves look better this way
217 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
218 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
219 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
220 if (light_level_day == LIGHT_SUN)
221 direct_sunlight = true;
222 light_day += decode_light(light_level_day);
223 light_night += decode_light(light_level_night);
228 return f.light_propagates;
231 bool obstructed[4] = { true, true, true, true };
233 bool opaque1 = !add_node(1);
234 bool opaque2 = !add_node(2);
235 bool opaque3 = !add_node(3);
236 obstructed[0] = opaque1 && opaque2;
237 obstructed[1] = opaque1 && opaque3;
238 obstructed[2] = opaque2 && opaque3;
239 for (u8 k = 0; k < 3; ++k)
240 if (add_node(k + 4, obstructed[k]))
241 obstructed[3] = false;
242 if (add_node(7, obstructed[3])) { // wrap light around nodes
243 ambient_occlusion -= 3;
244 for (u8 k = 0; k < 3; ++k)
245 add_node(k + 4, !obstructed[k]);
248 if (light_count == 0) {
249 light_day = light_night = 0;
251 light_day /= light_count;
252 light_night /= light_count;
255 // boost direct sunlight, if any
259 // Boost brightness around light sources
260 bool skip_ambient_occlusion_day = false;
261 if (decode_light(light_source_max) >= light_day) {
262 light_day = decode_light(light_source_max);
263 skip_ambient_occlusion_day = true;
266 bool skip_ambient_occlusion_night = false;
267 if(decode_light(light_source_max) >= light_night) {
268 light_night = decode_light(light_source_max);
269 skip_ambient_occlusion_night = true;
272 if (ambient_occlusion > 4) {
273 static thread_local const float ao_gamma = rangelim(
274 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
276 // Table of gamma space multiply factors.
277 static thread_local const float light_amount[3] = {
278 powf(0.75, 1.0 / ao_gamma),
279 powf(0.5, 1.0 / ao_gamma),
280 powf(0.25, 1.0 / ao_gamma)
283 //calculate table index for gamma space multiplier
284 ambient_occlusion -= 5;
286 if (!skip_ambient_occlusion_day)
287 light_day = rangelim(core::round32(
288 light_day * light_amount[ambient_occlusion]), 0, 255);
289 if (!skip_ambient_occlusion_night)
290 light_night = rangelim(core::round32(
291 light_night * light_amount[ambient_occlusion]), 0, 255);
294 return light_day | (light_night << 8);
298 Calculate smooth lighting at the given corner of p.
300 Node at p is solid, and thus the lighting is face-dependent.
302 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
304 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
308 Calculate smooth lighting at the given corner of p.
310 Node at p is not solid, and the lighting is not face-dependent.
312 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
314 const std::array<v3s16,8> dirs = {{
315 // Always shine light
322 v3s16(corner.X,corner.Y,0),
323 v3s16(corner.X,0,corner.Z),
324 v3s16(0,corner.Y,corner.Z),
325 v3s16(corner.X,corner.Y,corner.Z)
327 return getSmoothLightCombined(p, dirs, data);
330 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
331 f32 rg = daynight_ratio / 1000.0f - 0.04f;
332 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
338 void final_color_blend(video::SColor *result,
339 u16 light, u32 daynight_ratio)
341 video::SColorf dayLight;
342 get_sunlight_color(&dayLight, daynight_ratio);
343 final_color_blend(result,
344 encode_light(light, 0), dayLight);
347 void final_color_blend(video::SColor *result,
348 const video::SColor &data, const video::SColorf &dayLight)
350 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
352 video::SColorf c(data);
355 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
356 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
357 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
359 // Emphase blue a bit in darker places
360 // Each entry of this array represents a range of 8 blue levels
361 static const u8 emphase_blue_when_dark[32] = {
362 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
366 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
367 0, 255) / 8] / 255.0f;
369 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
370 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
371 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
375 Mesh generation helpers
378 // This table is moved outside getNodeVertexDirs to avoid the compiler using
379 // a mutex to initialize this table at runtime right in the hot path.
380 // For details search the internet for "cxa_guard_acquire".
381 static const v3s16 vertex_dirs_table[] = {
383 v3s16( 1,-1, 1), v3s16( 1,-1,-1),
384 v3s16( 1, 1,-1), v3s16( 1, 1, 1),
386 v3s16( 1, 1,-1), v3s16(-1, 1,-1),
387 v3s16(-1, 1, 1), v3s16( 1, 1, 1),
389 v3s16(-1,-1, 1), v3s16( 1,-1, 1),
390 v3s16( 1, 1, 1), v3s16(-1, 1, 1),
392 v3s16(), v3s16(), v3s16(), v3s16(),
394 v3s16( 1,-1,-1), v3s16(-1,-1,-1),
395 v3s16(-1, 1,-1), v3s16( 1, 1,-1),
397 v3s16( 1,-1, 1), v3s16(-1,-1, 1),
398 v3s16(-1,-1,-1), v3s16( 1,-1,-1),
400 v3s16(-1,-1,-1), v3s16(-1,-1, 1),
401 v3s16(-1, 1, 1), v3s16(-1, 1,-1)
405 vertex_dirs: v3s16[4]
407 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
410 If looked from outside the node towards the face, the corners are:
417 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
419 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
421 // Convert direction to single integer for table lookup
422 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
425 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
428 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
430 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
432 if (dir == v3s16(0,0,1)) {
435 } else if (dir == v3s16(0,0,-1)) {
438 } else if (dir == v3s16(1,0,0)) {
441 } else if (dir == v3s16(-1,0,0)) {
444 } else if (dir == v3s16(0,1,0)) {
447 } else if (dir == v3s16(0,-1,0)) {
456 video::S3DVertex vertices[4]; // Precalculated vertices
458 * The face is divided into two triangles. If this is true,
459 * vertices 0 and 2 are connected, othervise vertices 1 and 3
462 bool vertex_0_2_connected;
465 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
466 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
468 // Position is at the center of the cube.
477 v3s16 vertex_dirs[4];
478 getNodeVertexDirs(dir, vertex_dirs);
479 if (tile.world_aligned)
480 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
484 switch (tile.rotation) {
489 vertex_dirs[0] = vertex_dirs[3];
490 vertex_dirs[3] = vertex_dirs[2];
491 vertex_dirs[2] = vertex_dirs[1];
501 vertex_dirs[0] = vertex_dirs[2];
504 vertex_dirs[1] = vertex_dirs[3];
515 vertex_dirs[0] = vertex_dirs[1];
516 vertex_dirs[1] = vertex_dirs[2];
517 vertex_dirs[2] = vertex_dirs[3];
527 vertex_dirs[0] = vertex_dirs[3];
528 vertex_dirs[3] = vertex_dirs[2];
529 vertex_dirs[2] = vertex_dirs[1];
541 vertex_dirs[0] = vertex_dirs[1];
542 vertex_dirs[1] = vertex_dirs[2];
543 vertex_dirs[2] = vertex_dirs[3];
555 vertex_dirs[0] = vertex_dirs[3];
556 vertex_dirs[3] = vertex_dirs[2];
557 vertex_dirs[2] = vertex_dirs[1];
569 vertex_dirs[0] = vertex_dirs[1];
570 vertex_dirs[1] = vertex_dirs[2];
571 vertex_dirs[2] = vertex_dirs[3];
593 for (u16 i = 0; i < 4; i++) {
595 BS / 2 * vertex_dirs[i].X,
596 BS / 2 * vertex_dirs[i].Y,
597 BS / 2 * vertex_dirs[i].Z
601 for (v3f &vpos : vertex_pos) {
608 f32 abs_scale = 1.0f;
609 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
610 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
611 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
613 v3f normal(dir.X, dir.Y, dir.Z);
615 u16 li[4] = { li0, li1, li2, li3 };
619 for (u8 i = 0; i < 4; i++) {
621 night[i] = li[i] & 0xFF;
624 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
625 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
628 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
629 core::vector2d<f32>(x0, y0 + h),
630 core::vector2d<f32>(x0, y0),
631 core::vector2d<f32>(x0 + w * abs_scale, y0) };
633 // equivalent to dest.push_back(FastFace()) but faster
635 FastFace& face = *dest.rbegin();
637 for (u8 i = 0; i < 4; i++) {
638 video::SColor c = encode_light(li[i], tile.emissive_light);
639 if (!tile.emissive_light)
640 applyFacesShading(c, normal);
642 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
646 Revert triangles for nicer looking gradient if the
647 brightness of vertices 1 and 3 differ less than
648 the brightness of vertices 0 and 2.
650 face.vertex_0_2_connected = vertex_0_2_connected;
655 Nodes make a face if contents differ and solidness differs.
658 1: Face uses m1's content
659 2: Face uses m2's content
660 equivalent: Whether the blocks share the same face (eg. water and glass)
662 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
664 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
665 const NodeDefManager *ndef)
669 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
672 const ContentFeatures &f1 = ndef->get(m1);
673 const ContentFeatures &f2 = ndef->get(m2);
675 // Contents don't differ for different forms of same liquid
676 if (f1.sameLiquid(f2))
679 u8 c1 = f1.solidness;
680 u8 c2 = f2.solidness;
687 c1 = f1.visual_solidness;
689 c2 = f2.visual_solidness;
694 // If same solidness, liquid takes precense
708 Gets nth node tile (0 <= n <= 5).
710 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
712 const NodeDefManager *ndef = data->m_client->ndef();
713 const ContentFeatures &f = ndef->get(mn);
714 tile = f.tiles[tileindex];
715 bool has_crack = p == data->m_crack_pos_relative;
716 for (TileLayer &layer : tile.layers) {
717 if (layer.texture_id == 0)
719 if (!layer.has_color)
720 mn.getColor(f, &(layer.color));
721 // Apply temporary crack
723 layer.material_flags |= MATERIAL_FLAG_CRACK;
728 Gets node tile given a face direction.
730 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
732 const NodeDefManager *ndef = data->m_client->ndef();
734 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
735 // (0,0,1), (0,0,-1) or (0,0,0)
736 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
738 // Convert direction to single integer for table lookup
743 // 4 = invalid, treat as (0,0,0)
747 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
749 // Get rotation for things like chests
750 u8 facedir = mn.getFaceDir(ndef, true);
752 static const u16 dir_to_tile[24 * 16] =
754 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
755 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
756 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
757 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
758 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
760 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
761 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
762 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
763 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
765 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
766 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
767 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
768 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
770 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
771 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
772 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
773 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
775 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
776 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
777 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
778 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
780 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
781 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
782 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
783 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
786 u16 tile_index = facedir * 16 + dir_i;
787 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
788 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
791 std::set<content_t> splitToContentT(std::string str, const NodeDefManager *ndef)
794 std::set<content_t> dat;
797 if (c == ',' || c == '\n') {
799 dat.insert(ndef->getId(buf));
802 } else if (c != ' ') {
809 static void getTileInfo(
813 const v3s16 &face_dir,
817 v3s16 &face_dir_corrected,
823 std::set<content_t> xraySet)
825 VoxelManipulator &vmanip = data->m_vmanip;
826 const NodeDefManager *ndef = data->m_client->ndef();
827 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
829 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
831 content_t c0 = n0.getContent();
832 if (xray && xraySet.find(c0) != xraySet.end())
834 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
835 if (c0 == CONTENT_IGNORE) {
840 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
842 content_t c1 = n1.getContent();
843 if (xray && xraySet.find(c1) != xraySet.end())
846 if (c1 == CONTENT_IGNORE) {
852 bool equivalent = false;
853 u8 mf = face_contents(c0, c1,
867 face_dir_corrected = face_dir;
870 p_corrected = p + face_dir;
871 face_dir_corrected = -face_dir;
874 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
875 const ContentFeatures &f = ndef->get(n);
877 tile.emissive_light = f.light_source;
879 // eg. water and glass
881 for (TileLayer &layer : tile.layers)
882 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
885 if (!data->m_smooth_lighting) {
886 lights[0] = lights[1] = lights[2] = lights[3] =
887 getFaceLight(n0, n1, face_dir, ndef);
889 v3s16 vertex_dirs[4];
890 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
892 v3s16 light_p = blockpos_nodes + p_corrected;
893 for (u16 i = 0; i < 4; i++)
894 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
900 translate_dir: unit vector with only one of x, y or z
901 face_dir: unit vector with only one of x, y or z
903 static void updateFastFaceRow(
905 const v3s16 &&startpos,
907 const v3f &&translate_dir_f,
908 const v3s16 &&face_dir,
909 std::vector<FastFace> &dest,
911 std::set<content_t> xraySet)
913 static thread_local const bool waving_liquids =
914 g_settings->getBool("enable_shaders") &&
915 g_settings->getBool("enable_waving_water");
919 u16 continuous_tiles_count = 1;
921 bool makes_face = false;
923 v3s16 face_dir_corrected;
924 u16 lights[4] = {0, 0, 0, 0};
928 // Get info of first tile
929 getTileInfo(data, p, face_dir,
930 makes_face, p_corrected, face_dir_corrected,
931 lights, waving, tile, xray, xraySet);
933 // Unroll this variable which has a significant build cost
935 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
936 // If tiling can be done, this is set to false in the next step
937 bool next_is_different = true;
939 bool next_makes_face = false;
940 v3s16 next_p_corrected;
941 v3s16 next_face_dir_corrected;
942 u16 next_lights[4] = {0, 0, 0, 0};
944 // If at last position, there is nothing to compare to and
945 // the face must be drawn anyway
946 if (j != MAP_BLOCKSIZE - 1) {
949 getTileInfo(data, p, face_dir,
950 next_makes_face, next_p_corrected,
951 next_face_dir_corrected, next_lights,
957 if (next_makes_face == makes_face
958 && next_p_corrected == p_corrected + translate_dir
959 && next_face_dir_corrected == face_dir_corrected
960 && memcmp(next_lights, lights, sizeof(lights)) == 0
961 // Don't apply fast faces to waving water.
962 && (waving != 3 || !waving_liquids)
963 && next_tile.isTileable(tile)) {
964 next_is_different = false;
965 continuous_tiles_count++;
968 if (next_is_different) {
970 Create a face if there should be one
973 // Floating point conversion of the position vector
974 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
975 // Center point of face (kind of)
976 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
980 if (translate_dir.X != 0)
981 scale.X = continuous_tiles_count;
982 if (translate_dir.Y != 0)
983 scale.Y = continuous_tiles_count;
984 if (translate_dir.Z != 0)
985 scale.Z = continuous_tiles_count;
987 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
988 pf, sp, face_dir_corrected, scale, dest);
989 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
992 continuous_tiles_count = 1;
995 makes_face = next_makes_face;
996 p_corrected = next_p_corrected;
997 face_dir_corrected = next_face_dir_corrected;
998 memcpy(lights, next_lights, sizeof(lights));
999 if (next_is_different)
1000 tile = std::move(next_tile); // faster than copy
1004 static void updateAllFastFaceRows(MeshMakeData *data,
1005 std::vector<FastFace> &dest, bool xray, std::set<content_t> xraySet)
1008 Go through every y,z and get top(y+) faces in rows of x+
1010 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1011 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1012 updateFastFaceRow(data,
1014 v3s16(1, 0, 0), //dir
1016 v3s16(0, 1, 0), //face dir
1022 Go through every x,y and get right(x+) faces in rows of z+
1024 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
1025 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1026 updateFastFaceRow(data,
1028 v3s16(0, 0, 1), //dir
1030 v3s16(1, 0, 0), //face dir
1036 Go through every y,z and get back(z+) faces in rows of x+
1038 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1039 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1040 updateFastFaceRow(data,
1042 v3s16(1, 0, 0), //dir
1044 v3s16(0, 0, 1), //face dir
1050 static void applyTileColor(PreMeshBuffer &pmb)
1052 video::SColor tc = pmb.layer.color;
1053 if (tc == video::SColor(0xFFFFFFFF))
1055 for (video::S3DVertex &vertex : pmb.vertices) {
1056 video::SColor *c = &vertex.Color;
1057 c->set(c->getAlpha(),
1058 c->getRed() * tc.getRed() / 255,
1059 c->getGreen() * tc.getGreen() / 255,
1060 c->getBlue() * tc.getBlue() / 255);
1068 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1069 m_minimap_mapblock(NULL),
1070 m_tsrc(data->m_client->getTextureSource()),
1071 m_shdrsrc(data->m_client->getShaderSource()),
1072 m_animation_force_timer(0), // force initial animation
1074 m_last_daynight_ratio((u32) -1)
1076 for (auto &m : m_mesh)
1077 m = new scene::SMesh();
1078 m_enable_shaders = data->m_use_shaders;
1079 m_use_tangent_vertices = data->m_use_tangent_vertices;
1080 m_enable_vbo = g_settings->getBool("enable_vbo");
1082 if (g_settings->getBool("enable_minimap")) {
1083 m_minimap_mapblock = new MinimapMapblock;
1084 m_minimap_mapblock->getMinimapNodes(
1085 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1088 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1089 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1090 //TimeTaker timer1("MapBlockMesh()");
1092 std::vector<FastFace> fastfaces_new;
1093 fastfaces_new.reserve(512);
1097 bool xray = g_settings->getBool("xray");
1098 std::set<content_t> xraySet, nodeESPSet;
1100 xraySet = splitToContentT(g_settings->get("xray_nodes"), data->m_client->ndef());
1102 nodeESPSet = splitToContentT(g_settings->get("node_esp_nodes"), data->m_client->ndef());
1105 We are including the faces of the trailing edges of the block.
1106 This means that when something changes, the caller must
1107 also update the meshes of the blocks at the leading edges.
1109 NOTE: This is the slowest part of this method.
1112 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1113 //TimeTaker timer2("updateAllFastFaceRows()");
1114 updateAllFastFaceRows(data, fastfaces_new, xray, xraySet);
1122 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
1123 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1124 for (s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1125 for (s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1126 v3s16 pos = v3s16(x, y, z) + blockpos_nodes;
1127 const MapNode &node = data->m_vmanip.getNodeRefUnsafeCheckFlags(pos);
1128 if (nodeESPSet.find(node.getContent()) != nodeESPSet.end())
1129 esp_nodes.insert(pos);
1136 Convert FastFaces to MeshCollector
1139 MeshCollector collector;
1142 // avg 0ms (100ms spikes when loading textures the first time)
1143 // (NOTE: probably outdated)
1144 //TimeTaker timer2("MeshCollector building");
1146 for (const FastFace &f : fastfaces_new) {
1147 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1148 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1149 const u16 *indices_p =
1150 f.vertex_0_2_connected ? indices : indices_alternate;
1151 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1156 Add special graphics:
1164 MapblockMeshGenerator generator(data, &collector);
1165 generator.generate();
1169 Convert MeshCollector to SMesh
1172 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1173 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1175 PreMeshBuffer &p = collector.prebuffers[layer][i];
1179 // Generate animation data
1181 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1182 // Find the texture name plus ^[crack:N:
1183 std::ostringstream os(std::ios::binary);
1184 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1185 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1186 os << "o"; // use ^[cracko
1187 u8 tiles = p.layer.scale;
1189 os << ":" << (u32)tiles;
1190 os << ":" << (u32)p.layer.animation_frame_count << ":";
1191 m_crack_materials.insert(std::make_pair(
1192 std::pair<u8, u32>(layer, i), os.str()));
1193 // Replace tile texture with the cracked one
1194 p.layer.texture = m_tsrc->getTextureForMesh(
1196 &p.layer.texture_id);
1198 // - Texture animation
1199 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1200 // Add to MapBlockMesh in order to animate these tiles
1201 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1202 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1203 if (g_settings->getBool(
1204 "desynchronize_mapblock_texture_animation")) {
1205 // Get starting position from noise
1206 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1207 100000 * (2.0 + noise3d(
1208 data->m_blockpos.X, data->m_blockpos.Y,
1209 data->m_blockpos.Z, 0));
1211 // Play all synchronized
1212 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1214 // Replace tile texture with the first animation frame
1215 p.layer.texture = (*p.layer.frames)[0].texture;
1218 if (!m_enable_shaders) {
1219 // Extract colors for day-night animation
1220 // Dummy sunlight to handle non-sunlit areas
1221 video::SColorf sunlight;
1222 get_sunlight_color(&sunlight, 0);
1223 u32 vertex_count = p.vertices.size();
1224 for (u32 j = 0; j < vertex_count; j++) {
1225 video::SColor *vc = &p.vertices[j].Color;
1226 video::SColor copy = *vc;
1227 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1228 final_color_blend(vc, copy, sunlight); // Finalize color
1229 else // Record color to animate
1230 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1232 // The sunlight ratio has been stored,
1233 // delete alpha (for the final rendering).
1239 video::SMaterial material;
1240 material.setFlag(video::EMF_LIGHTING, false);
1241 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1242 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1243 material.setFlag(video::EMF_FOG_ENABLE, true);
1244 material.setTexture(0, p.layer.texture);
1246 if (m_enable_shaders) {
1247 material.MaterialType = m_shdrsrc->getShaderInfo(
1248 p.layer.shader_id).material;
1249 p.layer.applyMaterialOptionsWithShaders(material);
1250 if (p.layer.normal_texture)
1251 material.setTexture(1, p.layer.normal_texture);
1252 material.setTexture(2, p.layer.flags_texture);
1254 p.layer.applyMaterialOptions(material);
1257 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1259 // Create meshbuffer, add to mesh
1260 if (m_use_tangent_vertices) {
1261 scene::SMeshBufferTangents *buf =
1262 new scene::SMeshBufferTangents();
1263 buf->Material = material;
1264 buf->Vertices.reallocate(p.vertices.size());
1265 buf->Indices.reallocate(p.indices.size());
1266 for (const video::S3DVertex &v: p.vertices)
1267 buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
1268 for (u16 i: p.indices)
1269 buf->Indices.push_back(i);
1270 buf->recalculateBoundingBox();
1271 mesh->addMeshBuffer(buf);
1274 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1275 buf->Material = material;
1276 buf->append(&p.vertices[0], p.vertices.size(),
1277 &p.indices[0], p.indices.size());
1278 mesh->addMeshBuffer(buf);
1284 Do some stuff to the mesh
1286 m_camera_offset = camera_offset;
1287 translateMesh(m_mesh[layer],
1288 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1290 if (m_use_tangent_vertices) {
1291 scene::IMeshManipulator* meshmanip =
1292 RenderingEngine::get_scene_manager()->getMeshManipulator();
1293 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1296 if (m_mesh[layer]) {
1298 // Usually 1-700 faces and 1-7 materials
1299 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1300 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1301 << " materials (meshbuffers)" << std::endl;
1304 // Use VBO for mesh (this just would set this for ever buffer)
1306 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1310 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1312 // Check if animation is required for this mesh
1314 !m_crack_materials.empty() ||
1315 !m_daynight_diffs.empty() ||
1316 !m_animation_tiles.empty();
1319 MapBlockMesh::~MapBlockMesh()
1321 for (scene::IMesh *m : m_mesh) {
1322 if (m_enable_vbo && m)
1323 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1324 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1325 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1330 delete m_minimap_mapblock;
1333 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1336 if (!m_has_animation) {
1337 m_animation_force_timer = 100000;
1341 m_animation_force_timer = myrand_range(5, 100);
1344 if (crack != m_last_crack) {
1345 for (auto &crack_material : m_crack_materials) {
1346 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1347 getMeshBuffer(crack_material.first.second);
1348 std::string basename = crack_material.second;
1350 // Create new texture name from original
1351 std::ostringstream os;
1352 os << basename << crack;
1353 u32 new_texture_id = 0;
1354 video::ITexture *new_texture =
1355 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1356 buf->getMaterial().setTexture(0, new_texture);
1358 // If the current material is also animated,
1359 // update animation info
1360 auto anim_iter = m_animation_tiles.find(crack_material.first);
1361 if (anim_iter != m_animation_tiles.end()) {
1362 TileLayer &tile = anim_iter->second;
1363 tile.texture = new_texture;
1364 tile.texture_id = new_texture_id;
1365 // force animation update
1366 m_animation_frames[crack_material.first] = -1;
1370 m_last_crack = crack;
1373 // Texture animation
1374 for (auto &animation_tile : m_animation_tiles) {
1375 const TileLayer &tile = animation_tile.second;
1376 // Figure out current frame
1377 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1378 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1379 + frameoffset) % tile.animation_frame_count;
1380 // If frame doesn't change, skip
1381 if (frame == m_animation_frames[animation_tile.first])
1384 m_animation_frames[animation_tile.first] = frame;
1386 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1387 getMeshBuffer(animation_tile.first.second);
1389 const FrameSpec &animation_frame = (*tile.frames)[frame];
1390 buf->getMaterial().setTexture(0, animation_frame.texture);
1391 if (m_enable_shaders) {
1392 if (animation_frame.normal_texture)
1393 buf->getMaterial().setTexture(1,
1394 animation_frame.normal_texture);
1395 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1399 // Day-night transition
1400 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1401 // Force reload mesh to VBO
1403 for (scene::IMesh *m : m_mesh)
1405 video::SColorf day_color;
1406 get_sunlight_color(&day_color, daynight_ratio);
1408 for (auto &daynight_diff : m_daynight_diffs) {
1409 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1410 getMeshBuffer(daynight_diff.first.second);
1411 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1412 for (const auto &j : daynight_diff.second)
1413 final_color_blend(&(vertices[j.first].Color), j.second,
1416 m_last_daynight_ratio = daynight_ratio;
1422 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1424 if (camera_offset != m_camera_offset) {
1425 for (scene::IMesh *layer : m_mesh) {
1426 translateMesh(layer,
1427 intToFloat(m_camera_offset - camera_offset, BS));
1431 m_camera_offset = camera_offset;
1435 video::SColor encode_light(u16 light, u8 emissive_light)
1438 u32 day = (light & 0xff);
1439 u32 night = (light >> 8);
1440 // Add emissive light
1441 night += emissive_light * 2.5f;
1444 // Since we don't know if the day light is sunlight or
1445 // artificial light, assume it is artificial when the night
1446 // light bank is also lit.
1451 u32 sum = day + night;
1452 // Ratio of sunlight:
1455 r = day * 255 / sum;
1459 float b = (day + night) / 2;
1460 return video::SColor(r, b, b, b);