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;
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 return decode_light(light);
140 Calculate non-smooth lighting at interior of node.
143 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
145 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
146 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
147 return day | (night << 8);
151 Calculate non-smooth lighting at face of node.
154 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
155 v3s16 face_dir, const NodeDefManager *ndef)
158 u8 l1 = n.getLight(bank, ndef);
159 u8 l2 = n2.getLight(bank, ndef);
165 // Boost light level for light sources
166 u8 light_source = MYMAX(ndef->get(n).light_source,
167 ndef->get(n2).light_source);
168 if(light_source > light)
169 light = light_source;
171 return decode_light(light);
175 Calculate non-smooth lighting at face of node.
178 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
179 const NodeDefManager *ndef)
181 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
182 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
183 return day | (night << 8);
187 Calculate smooth lighting at the XYZ- corner of p.
190 static u16 getSmoothLightCombined(const v3s16 &p,
191 const std::array<v3s16,8> &dirs, MeshMakeData *data)
193 const NodeDefManager *ndef = data->m_client->ndef();
195 u16 ambient_occlusion = 0;
197 u8 light_source_max = 0;
200 bool direct_sunlight = false;
202 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
207 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
208 if (n.getContent() == CONTENT_IGNORE)
210 const ContentFeatures &f = ndef->get(n);
211 if (f.light_source > light_source_max)
212 light_source_max = f.light_source;
213 // Check f.solidness because fast-style leaves look better this way
214 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
215 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
216 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
217 if (light_level_day == LIGHT_SUN)
218 direct_sunlight = true;
219 light_day += decode_light(light_level_day);
220 light_night += decode_light(light_level_night);
225 return f.light_propagates;
228 bool obstructed[4] = { true, true, true, true };
230 bool opaque1 = !add_node(1);
231 bool opaque2 = !add_node(2);
232 bool opaque3 = !add_node(3);
233 obstructed[0] = opaque1 && opaque2;
234 obstructed[1] = opaque1 && opaque3;
235 obstructed[2] = opaque2 && opaque3;
236 for (u8 k = 0; k < 3; ++k)
237 if (add_node(k + 4, obstructed[k]))
238 obstructed[3] = false;
239 if (add_node(7, obstructed[3])) { // wrap light around nodes
240 ambient_occlusion -= 3;
241 for (u8 k = 0; k < 3; ++k)
242 add_node(k + 4, !obstructed[k]);
245 if (light_count == 0) {
246 light_day = light_night = 0;
248 light_day /= light_count;
249 light_night /= light_count;
252 // boost direct sunlight, if any
256 // Boost brightness around light sources
257 bool skip_ambient_occlusion_day = false;
258 if (decode_light(light_source_max) >= light_day) {
259 light_day = decode_light(light_source_max);
260 skip_ambient_occlusion_day = true;
263 bool skip_ambient_occlusion_night = false;
264 if(decode_light(light_source_max) >= light_night) {
265 light_night = decode_light(light_source_max);
266 skip_ambient_occlusion_night = true;
269 if (ambient_occlusion > 4) {
270 static thread_local const float ao_gamma = rangelim(
271 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
273 // Table of gamma space multiply factors.
274 static thread_local const float light_amount[3] = {
275 powf(0.75, 1.0 / ao_gamma),
276 powf(0.5, 1.0 / ao_gamma),
277 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(core::round32(
285 light_day * light_amount[ambient_occlusion]), 0, 255);
286 if (!skip_ambient_occlusion_night)
287 light_night = rangelim(core::round32(
288 light_night * light_amount[ambient_occlusion]), 0, 255);
291 return light_day | (light_night << 8);
295 Calculate smooth lighting at the given corner of p.
297 Node at p is solid, and thus the lighting is face-dependent.
299 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
301 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
305 Calculate smooth lighting at the given corner of p.
307 Node at p is not solid, and the lighting is not face-dependent.
309 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
311 const std::array<v3s16,8> dirs = {{
312 // Always shine light
319 v3s16(corner.X,corner.Y,0),
320 v3s16(corner.X,0,corner.Z),
321 v3s16(0,corner.Y,corner.Z),
322 v3s16(corner.X,corner.Y,corner.Z)
324 return getSmoothLightCombined(p, dirs, data);
327 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
328 f32 rg = daynight_ratio / 1000.0f - 0.04f;
329 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
335 void final_color_blend(video::SColor *result,
336 u16 light, u32 daynight_ratio)
338 video::SColorf dayLight;
339 get_sunlight_color(&dayLight, daynight_ratio);
340 final_color_blend(result,
341 encode_light(light, 0), dayLight);
344 void final_color_blend(video::SColor *result,
345 const video::SColor &data, const video::SColorf &dayLight)
347 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
349 video::SColorf c(data);
352 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
353 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
354 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
356 // Emphase blue a bit in darker places
357 // Each entry of this array represents a range of 8 blue levels
358 static const u8 emphase_blue_when_dark[32] = {
359 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
360 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
363 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
364 0, 255) / 8] / 255.0f;
366 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
367 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
368 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
372 Mesh generation helpers
375 // This table is moved outside getNodeVertexDirs to avoid the compiler using
376 // a mutex to initialize this table at runtime right in the hot path.
377 // For details search the internet for "cxa_guard_acquire".
378 static const v3s16 vertex_dirs_table[] = {
380 v3s16( 1,-1, 1), v3s16( 1,-1,-1),
381 v3s16( 1, 1,-1), v3s16( 1, 1, 1),
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(), v3s16(), v3s16(), v3s16(),
391 v3s16( 1,-1,-1), v3s16(-1,-1,-1),
392 v3s16(-1, 1,-1), v3s16( 1, 1,-1),
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)
402 vertex_dirs: v3s16[4]
404 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
407 If looked from outside the node towards the face, the corners are:
414 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
416 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
418 // Convert direction to single integer for table lookup
419 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
422 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
425 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
427 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
429 if (dir == v3s16(0,0,1)) {
432 } else if (dir == v3s16(0,0,-1)) {
435 } else if (dir == v3s16(1,0,0)) {
438 } else if (dir == v3s16(-1,0,0)) {
441 } else if (dir == v3s16(0,1,0)) {
444 } else if (dir == v3s16(0,-1,0)) {
453 video::S3DVertex vertices[4]; // Precalculated vertices
455 * The face is divided into two triangles. If this is true,
456 * vertices 0 and 2 are connected, othervise vertices 1 and 3
459 bool vertex_0_2_connected;
462 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
463 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
465 // Position is at the center of the cube.
474 v3s16 vertex_dirs[4];
475 getNodeVertexDirs(dir, vertex_dirs);
476 if (tile.world_aligned)
477 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
481 switch (tile.rotation) {
486 vertex_dirs[0] = vertex_dirs[3];
487 vertex_dirs[3] = vertex_dirs[2];
488 vertex_dirs[2] = vertex_dirs[1];
498 vertex_dirs[0] = vertex_dirs[2];
501 vertex_dirs[1] = vertex_dirs[3];
512 vertex_dirs[0] = vertex_dirs[1];
513 vertex_dirs[1] = vertex_dirs[2];
514 vertex_dirs[2] = vertex_dirs[3];
524 vertex_dirs[0] = vertex_dirs[3];
525 vertex_dirs[3] = vertex_dirs[2];
526 vertex_dirs[2] = vertex_dirs[1];
538 vertex_dirs[0] = vertex_dirs[1];
539 vertex_dirs[1] = vertex_dirs[2];
540 vertex_dirs[2] = vertex_dirs[3];
552 vertex_dirs[0] = vertex_dirs[3];
553 vertex_dirs[3] = vertex_dirs[2];
554 vertex_dirs[2] = vertex_dirs[1];
566 vertex_dirs[0] = vertex_dirs[1];
567 vertex_dirs[1] = vertex_dirs[2];
568 vertex_dirs[2] = vertex_dirs[3];
590 for (u16 i = 0; i < 4; i++) {
592 BS / 2 * vertex_dirs[i].X,
593 BS / 2 * vertex_dirs[i].Y,
594 BS / 2 * vertex_dirs[i].Z
598 for (v3f &vpos : vertex_pos) {
605 f32 abs_scale = 1.0f;
606 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
607 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
608 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
610 v3f normal(dir.X, dir.Y, dir.Z);
612 u16 li[4] = { li0, li1, li2, li3 };
616 for (u8 i = 0; i < 4; i++) {
618 night[i] = li[i] & 0xFF;
621 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
622 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
625 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
626 core::vector2d<f32>(x0, y0 + h),
627 core::vector2d<f32>(x0, y0),
628 core::vector2d<f32>(x0 + w * abs_scale, y0) };
630 // equivalent to dest.push_back(FastFace()) but faster
632 FastFace& face = *dest.rbegin();
634 for (u8 i = 0; i < 4; i++) {
635 video::SColor c = encode_light(li[i], tile.emissive_light);
636 if (!tile.emissive_light)
637 applyFacesShading(c, normal);
639 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
643 Revert triangles for nicer looking gradient if the
644 brightness of vertices 1 and 3 differ less than
645 the brightness of vertices 0 and 2.
647 face.vertex_0_2_connected = vertex_0_2_connected;
652 Nodes make a face if contents differ and solidness differs.
655 1: Face uses m1's content
656 2: Face uses m2's content
657 equivalent: Whether the blocks share the same face (eg. water and glass)
659 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
661 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
662 const NodeDefManager *ndef)
666 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
669 const ContentFeatures &f1 = ndef->get(m1);
670 const ContentFeatures &f2 = ndef->get(m2);
672 // Contents don't differ for different forms of same liquid
673 if (f1.sameLiquid(f2))
676 u8 c1 = f1.solidness;
677 u8 c2 = f2.solidness;
683 c1 = f1.visual_solidness;
685 c2 = f2.visual_solidness;
689 // If same solidness, liquid takes precense
703 Gets nth node tile (0 <= n <= 5).
705 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
707 const NodeDefManager *ndef = data->m_client->ndef();
708 const ContentFeatures &f = ndef->get(mn);
709 tile = f.tiles[tileindex];
710 bool has_crack = p == data->m_crack_pos_relative;
711 for (TileLayer &layer : tile.layers) {
712 if (layer.texture_id == 0)
714 if (!layer.has_color)
715 mn.getColor(f, &(layer.color));
716 // Apply temporary crack
718 layer.material_flags |= MATERIAL_FLAG_CRACK;
723 Gets node tile given a face direction.
725 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
727 const NodeDefManager *ndef = data->m_client->ndef();
729 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
730 // (0,0,1), (0,0,-1) or (0,0,0)
731 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
733 // Convert direction to single integer for table lookup
738 // 4 = invalid, treat as (0,0,0)
742 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
744 // Get rotation for things like chests
745 u8 facedir = mn.getFaceDir(ndef, true);
747 static const u16 dir_to_tile[24 * 16] =
749 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
750 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
751 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
752 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
753 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
755 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
756 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
757 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
758 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
760 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
761 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
762 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
763 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
765 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
766 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
767 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
768 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
770 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
771 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
772 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
773 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
775 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
776 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
777 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
778 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
781 u16 tile_index = facedir * 16 + dir_i;
782 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
783 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
786 static void getTileInfo(
790 const v3s16 &face_dir,
794 v3s16 &face_dir_corrected,
800 VoxelManipulator &vmanip = data->m_vmanip;
801 const NodeDefManager *ndef = data->m_client->ndef();
802 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
804 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
806 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
807 if (n0.getContent() == CONTENT_IGNORE) {
812 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
814 if (n1.getContent() == CONTENT_IGNORE) {
820 bool equivalent = false;
821 u8 mf = face_contents(n0.getContent(), n1.getContent(),
835 face_dir_corrected = face_dir;
838 p_corrected = p + face_dir;
839 face_dir_corrected = -face_dir;
842 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
843 const ContentFeatures &f = ndef->get(n);
845 tile.emissive_light = f.light_source;
847 // eg. water and glass
849 for (TileLayer &layer : tile.layers)
850 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
853 if (!data->m_smooth_lighting) {
854 lights[0] = lights[1] = lights[2] = lights[3] =
855 getFaceLight(n0, n1, face_dir, ndef);
857 v3s16 vertex_dirs[4];
858 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
860 v3s16 light_p = blockpos_nodes + p_corrected;
861 for (u16 i = 0; i < 4; i++)
862 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
868 translate_dir: unit vector with only one of x, y or z
869 face_dir: unit vector with only one of x, y or z
871 static void updateFastFaceRow(
873 const v3s16 &&startpos,
875 const v3f &&translate_dir_f,
876 const v3s16 &&face_dir,
877 std::vector<FastFace> &dest)
879 static thread_local const bool waving_liquids =
880 g_settings->getBool("enable_shaders") &&
881 g_settings->getBool("enable_waving_water");
885 u16 continuous_tiles_count = 1;
887 bool makes_face = false;
889 v3s16 face_dir_corrected;
890 u16 lights[4] = {0, 0, 0, 0};
894 // Get info of first tile
895 getTileInfo(data, p, face_dir,
896 makes_face, p_corrected, face_dir_corrected,
897 lights, waving, tile);
899 // Unroll this variable which has a significant build cost
901 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
902 // If tiling can be done, this is set to false in the next step
903 bool next_is_different = true;
905 bool next_makes_face = false;
906 v3s16 next_p_corrected;
907 v3s16 next_face_dir_corrected;
908 u16 next_lights[4] = {0, 0, 0, 0};
910 // If at last position, there is nothing to compare to and
911 // the face must be drawn anyway
912 if (j != MAP_BLOCKSIZE - 1) {
915 getTileInfo(data, p, face_dir,
916 next_makes_face, next_p_corrected,
917 next_face_dir_corrected, next_lights,
921 if (next_makes_face == makes_face
922 && next_p_corrected == p_corrected + translate_dir
923 && next_face_dir_corrected == face_dir_corrected
924 && memcmp(next_lights, lights, sizeof(lights)) == 0
925 // Don't apply fast faces to waving water.
926 && (waving != 3 || !waving_liquids)
927 && next_tile.isTileable(tile)) {
928 next_is_different = false;
929 continuous_tiles_count++;
932 if (next_is_different) {
934 Create a face if there should be one
937 // Floating point conversion of the position vector
938 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
939 // Center point of face (kind of)
940 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
944 if (translate_dir.X != 0)
945 scale.X = continuous_tiles_count;
946 if (translate_dir.Y != 0)
947 scale.Y = continuous_tiles_count;
948 if (translate_dir.Z != 0)
949 scale.Z = continuous_tiles_count;
951 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
952 pf, sp, face_dir_corrected, scale, dest);
953 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
956 continuous_tiles_count = 1;
959 makes_face = next_makes_face;
960 p_corrected = next_p_corrected;
961 face_dir_corrected = next_face_dir_corrected;
962 memcpy(lights, next_lights, sizeof(lights));
963 if (next_is_different)
964 tile = std::move(next_tile); // faster than copy
968 static void updateAllFastFaceRows(MeshMakeData *data,
969 std::vector<FastFace> &dest)
972 Go through every y,z and get top(y+) faces in rows of x+
974 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
975 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
976 updateFastFaceRow(data,
978 v3s16(1, 0, 0), //dir
980 v3s16(0, 1, 0), //face dir
984 Go through every x,y and get right(x+) faces in rows of z+
986 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
987 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
988 updateFastFaceRow(data,
990 v3s16(0, 0, 1), //dir
992 v3s16(1, 0, 0), //face dir
996 Go through every y,z and get back(z+) faces in rows of x+
998 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
999 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1000 updateFastFaceRow(data,
1002 v3s16(1, 0, 0), //dir
1004 v3s16(0, 0, 1), //face dir
1008 static void applyTileColor(PreMeshBuffer &pmb)
1010 video::SColor tc = pmb.layer.color;
1011 if (tc == video::SColor(0xFFFFFFFF))
1013 for (video::S3DVertex &vertex : pmb.vertices) {
1014 video::SColor *c = &vertex.Color;
1015 c->set(c->getAlpha(),
1016 c->getRed() * tc.getRed() / 255,
1017 c->getGreen() * tc.getGreen() / 255,
1018 c->getBlue() * tc.getBlue() / 255);
1026 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1027 m_minimap_mapblock(NULL),
1028 m_tsrc(data->m_client->getTextureSource()),
1029 m_shdrsrc(data->m_client->getShaderSource()),
1030 m_animation_force_timer(0), // force initial animation
1032 m_last_daynight_ratio((u32) -1)
1034 for (auto &m : m_mesh)
1035 m = new scene::SMesh();
1036 m_enable_shaders = data->m_use_shaders;
1037 m_use_tangent_vertices = data->m_use_tangent_vertices;
1038 m_enable_vbo = g_settings->getBool("enable_vbo");
1040 if (g_settings->getBool("enable_minimap")) {
1041 m_minimap_mapblock = new MinimapMapblock;
1042 m_minimap_mapblock->getMinimapNodes(
1043 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1046 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1047 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1048 //TimeTaker timer1("MapBlockMesh()");
1050 std::vector<FastFace> fastfaces_new;
1051 fastfaces_new.reserve(512);
1054 We are including the faces of the trailing edges of the block.
1055 This means that when something changes, the caller must
1056 also update the meshes of the blocks at the leading edges.
1058 NOTE: This is the slowest part of this method.
1061 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1062 //TimeTaker timer2("updateAllFastFaceRows()");
1063 updateAllFastFaceRows(data, fastfaces_new);
1068 Convert FastFaces to MeshCollector
1071 MeshCollector collector;
1074 // avg 0ms (100ms spikes when loading textures the first time)
1075 // (NOTE: probably outdated)
1076 //TimeTaker timer2("MeshCollector building");
1078 for (const FastFace &f : fastfaces_new) {
1079 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1080 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1081 const u16 *indices_p =
1082 f.vertex_0_2_connected ? indices : indices_alternate;
1083 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1088 Add special graphics:
1096 MapblockMeshGenerator generator(data, &collector);
1097 generator.generate();
1101 Convert MeshCollector to SMesh
1104 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1105 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1107 PreMeshBuffer &p = collector.prebuffers[layer][i];
1111 // Generate animation data
1113 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1114 // Find the texture name plus ^[crack:N:
1115 std::ostringstream os(std::ios::binary);
1116 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1117 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1118 os << "o"; // use ^[cracko
1119 u8 tiles = p.layer.scale;
1121 os << ":" << (u32)tiles;
1122 os << ":" << (u32)p.layer.animation_frame_count << ":";
1123 m_crack_materials.insert(std::make_pair(
1124 std::pair<u8, u32>(layer, i), os.str()));
1125 // Replace tile texture with the cracked one
1126 p.layer.texture = m_tsrc->getTextureForMesh(
1128 &p.layer.texture_id);
1130 // - Texture animation
1131 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1132 // Add to MapBlockMesh in order to animate these tiles
1133 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1134 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1135 if (g_settings->getBool(
1136 "desynchronize_mapblock_texture_animation")) {
1137 // Get starting position from noise
1138 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1139 100000 * (2.0 + noise3d(
1140 data->m_blockpos.X, data->m_blockpos.Y,
1141 data->m_blockpos.Z, 0));
1143 // Play all synchronized
1144 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1146 // Replace tile texture with the first animation frame
1147 p.layer.texture = (*p.layer.frames)[0].texture;
1150 if (!m_enable_shaders) {
1151 // Extract colors for day-night animation
1152 // Dummy sunlight to handle non-sunlit areas
1153 video::SColorf sunlight;
1154 get_sunlight_color(&sunlight, 0);
1155 u32 vertex_count = p.vertices.size();
1156 for (u32 j = 0; j < vertex_count; j++) {
1157 video::SColor *vc = &p.vertices[j].Color;
1158 video::SColor copy = *vc;
1159 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1160 final_color_blend(vc, copy, sunlight); // Finalize color
1161 else // Record color to animate
1162 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1164 // The sunlight ratio has been stored,
1165 // delete alpha (for the final rendering).
1171 video::SMaterial material;
1172 material.setFlag(video::EMF_LIGHTING, false);
1173 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1174 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1175 material.setFlag(video::EMF_FOG_ENABLE, true);
1176 material.setTexture(0, p.layer.texture);
1178 if (m_enable_shaders) {
1179 material.MaterialType = m_shdrsrc->getShaderInfo(
1180 p.layer.shader_id).material;
1181 p.layer.applyMaterialOptionsWithShaders(material);
1182 if (p.layer.normal_texture)
1183 material.setTexture(1, p.layer.normal_texture);
1184 material.setTexture(2, p.layer.flags_texture);
1186 p.layer.applyMaterialOptions(material);
1189 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1191 // Create meshbuffer, add to mesh
1192 if (m_use_tangent_vertices) {
1193 scene::SMeshBufferTangents *buf =
1194 new scene::SMeshBufferTangents();
1195 buf->Material = material;
1196 buf->Vertices.reallocate(p.vertices.size());
1197 buf->Indices.reallocate(p.indices.size());
1198 for (const video::S3DVertex &v: p.vertices)
1199 buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
1200 for (u16 i: p.indices)
1201 buf->Indices.push_back(i);
1202 buf->recalculateBoundingBox();
1203 mesh->addMeshBuffer(buf);
1206 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1207 buf->Material = material;
1208 buf->append(&p.vertices[0], p.vertices.size(),
1209 &p.indices[0], p.indices.size());
1210 mesh->addMeshBuffer(buf);
1216 Do some stuff to the mesh
1218 m_camera_offset = camera_offset;
1219 translateMesh(m_mesh[layer],
1220 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1222 if (m_use_tangent_vertices) {
1223 scene::IMeshManipulator* meshmanip =
1224 RenderingEngine::get_scene_manager()->getMeshManipulator();
1225 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1228 if (m_mesh[layer]) {
1230 // Usually 1-700 faces and 1-7 materials
1231 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1232 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1233 << " materials (meshbuffers)" << std::endl;
1236 // Use VBO for mesh (this just would set this for ever buffer)
1238 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1242 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1244 // Check if animation is required for this mesh
1246 !m_crack_materials.empty() ||
1247 !m_daynight_diffs.empty() ||
1248 !m_animation_tiles.empty();
1251 MapBlockMesh::~MapBlockMesh()
1253 for (scene::IMesh *m : m_mesh) {
1254 if (m_enable_vbo && m)
1255 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1256 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1257 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1262 delete m_minimap_mapblock;
1265 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1268 if (!m_has_animation) {
1269 m_animation_force_timer = 100000;
1273 m_animation_force_timer = myrand_range(5, 100);
1276 if (crack != m_last_crack) {
1277 for (auto &crack_material : m_crack_materials) {
1278 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1279 getMeshBuffer(crack_material.first.second);
1280 std::string basename = crack_material.second;
1282 // Create new texture name from original
1283 std::ostringstream os;
1284 os << basename << crack;
1285 u32 new_texture_id = 0;
1286 video::ITexture *new_texture =
1287 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1288 buf->getMaterial().setTexture(0, new_texture);
1290 // If the current material is also animated,
1291 // update animation info
1292 auto anim_iter = m_animation_tiles.find(crack_material.first);
1293 if (anim_iter != m_animation_tiles.end()) {
1294 TileLayer &tile = anim_iter->second;
1295 tile.texture = new_texture;
1296 tile.texture_id = new_texture_id;
1297 // force animation update
1298 m_animation_frames[crack_material.first] = -1;
1302 m_last_crack = crack;
1305 // Texture animation
1306 for (auto &animation_tile : m_animation_tiles) {
1307 const TileLayer &tile = animation_tile.second;
1308 // Figure out current frame
1309 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1310 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1311 + frameoffset) % tile.animation_frame_count;
1312 // If frame doesn't change, skip
1313 if (frame == m_animation_frames[animation_tile.first])
1316 m_animation_frames[animation_tile.first] = frame;
1318 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1319 getMeshBuffer(animation_tile.first.second);
1321 const FrameSpec &animation_frame = (*tile.frames)[frame];
1322 buf->getMaterial().setTexture(0, animation_frame.texture);
1323 if (m_enable_shaders) {
1324 if (animation_frame.normal_texture)
1325 buf->getMaterial().setTexture(1,
1326 animation_frame.normal_texture);
1327 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1331 // Day-night transition
1332 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1333 // Force reload mesh to VBO
1335 for (scene::IMesh *m : m_mesh)
1337 video::SColorf day_color;
1338 get_sunlight_color(&day_color, daynight_ratio);
1340 for (auto &daynight_diff : m_daynight_diffs) {
1341 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1342 getMeshBuffer(daynight_diff.first.second);
1343 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1344 for (const auto &j : daynight_diff.second)
1345 final_color_blend(&(vertices[j.first].Color), j.second,
1348 m_last_daynight_ratio = daynight_ratio;
1354 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1356 if (camera_offset != m_camera_offset) {
1357 for (scene::IMesh *layer : m_mesh) {
1358 translateMesh(layer,
1359 intToFloat(m_camera_offset - camera_offset, BS));
1363 m_camera_offset = camera_offset;
1367 video::SColor encode_light(u16 light, u8 emissive_light)
1370 u32 day = (light & 0xff);
1371 u32 night = (light >> 8);
1372 // Add emissive light
1373 night += emissive_light * 2.5f;
1376 // Since we don't know if the day light is sunlight or
1377 // artificial light, assume it is artificial when the night
1378 // light bank is also lit.
1383 u32 sum = day + night;
1384 // Ratio of sunlight:
1387 r = day * 255 / sum;
1391 float b = (day + night) / 2;
1392 return video::SColor(r, b, b, b);