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 std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
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
376 vertex_dirs: v3s16[4]
378 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
381 If looked from outside the node towards the face, the corners are:
387 if (dir == v3s16(0, 0, 1)) {
388 // If looking towards z+, this is the face that is behind
389 // the center point, facing towards z+.
390 vertex_dirs[0] = v3s16(-1,-1, 1);
391 vertex_dirs[1] = v3s16( 1,-1, 1);
392 vertex_dirs[2] = v3s16( 1, 1, 1);
393 vertex_dirs[3] = v3s16(-1, 1, 1);
394 } else if (dir == v3s16(0, 0, -1)) {
396 vertex_dirs[0] = v3s16( 1,-1,-1);
397 vertex_dirs[1] = v3s16(-1,-1,-1);
398 vertex_dirs[2] = v3s16(-1, 1,-1);
399 vertex_dirs[3] = v3s16( 1, 1,-1);
400 } else if (dir == v3s16(1, 0, 0)) {
402 vertex_dirs[0] = v3s16( 1,-1, 1);
403 vertex_dirs[1] = v3s16( 1,-1,-1);
404 vertex_dirs[2] = v3s16( 1, 1,-1);
405 vertex_dirs[3] = v3s16( 1, 1, 1);
406 } else if (dir == v3s16(-1, 0, 0)) {
408 vertex_dirs[0] = v3s16(-1,-1,-1);
409 vertex_dirs[1] = v3s16(-1,-1, 1);
410 vertex_dirs[2] = v3s16(-1, 1, 1);
411 vertex_dirs[3] = v3s16(-1, 1,-1);
412 } else if (dir == v3s16(0, 1, 0)) {
413 // faces towards Y+ (assume Z- as "down" in texture)
414 vertex_dirs[0] = v3s16( 1, 1,-1);
415 vertex_dirs[1] = v3s16(-1, 1,-1);
416 vertex_dirs[2] = v3s16(-1, 1, 1);
417 vertex_dirs[3] = v3s16( 1, 1, 1);
418 } else if (dir == v3s16(0, -1, 0)) {
419 // faces towards Y- (assume Z+ as "down" in texture)
420 vertex_dirs[0] = v3s16( 1,-1, 1);
421 vertex_dirs[1] = v3s16(-1,-1, 1);
422 vertex_dirs[2] = v3s16(-1,-1,-1);
423 vertex_dirs[3] = v3s16( 1,-1,-1);
427 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
429 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
431 if (dir == v3s16(0,0,1)) {
434 } else if (dir == v3s16(0,0,-1)) {
437 } else if (dir == v3s16(1,0,0)) {
440 } else if (dir == v3s16(-1,0,0)) {
443 } else if (dir == v3s16(0,1,0)) {
446 } else if (dir == v3s16(0,-1,0)) {
455 video::S3DVertex vertices[4]; // Precalculated vertices
457 * The face is divided into two triangles. If this is true,
458 * vertices 0 and 2 are connected, othervise vertices 1 and 3
461 bool vertex_0_2_connected;
464 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
465 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
467 // Position is at the center of the cube.
476 v3s16 vertex_dirs[4];
477 getNodeVertexDirs(dir, vertex_dirs);
478 if (tile.world_aligned)
479 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
483 switch (tile.rotation) {
488 vertex_dirs[0] = vertex_dirs[3];
489 vertex_dirs[3] = vertex_dirs[2];
490 vertex_dirs[2] = vertex_dirs[1];
500 vertex_dirs[0] = vertex_dirs[2];
503 vertex_dirs[1] = vertex_dirs[3];
514 vertex_dirs[0] = vertex_dirs[1];
515 vertex_dirs[1] = vertex_dirs[2];
516 vertex_dirs[2] = vertex_dirs[3];
526 vertex_dirs[0] = vertex_dirs[3];
527 vertex_dirs[3] = vertex_dirs[2];
528 vertex_dirs[2] = vertex_dirs[1];
540 vertex_dirs[0] = vertex_dirs[1];
541 vertex_dirs[1] = vertex_dirs[2];
542 vertex_dirs[2] = vertex_dirs[3];
554 vertex_dirs[0] = vertex_dirs[3];
555 vertex_dirs[3] = vertex_dirs[2];
556 vertex_dirs[2] = vertex_dirs[1];
568 vertex_dirs[0] = vertex_dirs[1];
569 vertex_dirs[1] = vertex_dirs[2];
570 vertex_dirs[2] = vertex_dirs[3];
592 for (u16 i = 0; i < 4; i++) {
594 BS / 2 * vertex_dirs[i].X,
595 BS / 2 * vertex_dirs[i].Y,
596 BS / 2 * vertex_dirs[i].Z
600 for (v3f &vpos : vertex_pos) {
607 f32 abs_scale = 1.0f;
608 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
609 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
610 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
612 v3f normal(dir.X, dir.Y, dir.Z);
614 u16 li[4] = { li0, li1, li2, li3 };
618 for (u8 i = 0; i < 4; i++) {
620 night[i] = li[i] & 0xFF;
623 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
624 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
627 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
628 core::vector2d<f32>(x0, y0 + h),
629 core::vector2d<f32>(x0, y0),
630 core::vector2d<f32>(x0 + w * abs_scale, y0) };
632 // equivalent to dest.push_back(FastFace()) but faster
634 FastFace& face = *dest.rbegin();
636 for (u8 i = 0; i < 4; i++) {
637 video::SColor c = encode_light(li[i], tile.emissive_light);
638 if (!tile.emissive_light)
639 applyFacesShading(c, normal);
641 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
645 Revert triangles for nicer looking gradient if the
646 brightness of vertices 1 and 3 differ less than
647 the brightness of vertices 0 and 2.
649 face.vertex_0_2_connected = vertex_0_2_connected;
654 Nodes make a face if contents differ and solidness differs.
657 1: Face uses m1's content
658 2: Face uses m2's content
659 equivalent: Whether the blocks share the same face (eg. water and glass)
661 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
663 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
664 const NodeDefManager *ndef)
668 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
671 const ContentFeatures &f1 = ndef->get(m1);
672 const ContentFeatures &f2 = ndef->get(m2);
674 // Contents don't differ for different forms of same liquid
675 if (f1.sameLiquid(f2))
678 u8 c1 = f1.solidness;
679 u8 c2 = f2.solidness;
685 c1 = f1.visual_solidness;
687 c2 = f2.visual_solidness;
691 // If same solidness, liquid takes precense
705 Gets nth node tile (0 <= n <= 5).
707 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
709 const NodeDefManager *ndef = data->m_client->ndef();
710 const ContentFeatures &f = ndef->get(mn);
711 tile = f.tiles[tileindex];
712 bool has_crack = p == data->m_crack_pos_relative;
713 for (TileLayer &layer : tile.layers) {
714 if (layer.texture_id == 0)
716 if (!layer.has_color)
717 mn.getColor(f, &(layer.color));
718 // Apply temporary crack
720 layer.material_flags |= MATERIAL_FLAG_CRACK;
725 Gets node tile given a face direction.
727 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
729 const NodeDefManager *ndef = data->m_client->ndef();
731 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
732 // (0,0,1), (0,0,-1) or (0,0,0)
733 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
735 // Convert direction to single integer for table lookup
740 // 4 = invalid, treat as (0,0,0)
744 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
746 // Get rotation for things like chests
747 u8 facedir = mn.getFaceDir(ndef, true);
749 static const u16 dir_to_tile[24 * 16] =
751 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
752 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
753 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
754 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
755 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
757 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
758 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
759 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
760 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
762 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
763 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
764 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
765 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
767 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
768 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
769 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
770 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
772 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
773 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
774 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
775 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
777 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
778 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
779 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
780 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
783 u16 tile_index = facedir * 16 + dir_i;
784 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
785 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
788 static void getTileInfo(
792 const v3s16 &face_dir,
796 v3s16 &face_dir_corrected,
802 VoxelManipulator &vmanip = data->m_vmanip;
803 const NodeDefManager *ndef = data->m_client->ndef();
804 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
806 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
808 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
809 if (n0.getContent() == CONTENT_IGNORE) {
814 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
816 if (n1.getContent() == CONTENT_IGNORE) {
822 bool equivalent = false;
823 u8 mf = face_contents(n0.getContent(), n1.getContent(),
837 face_dir_corrected = face_dir;
840 p_corrected = p + face_dir;
841 face_dir_corrected = -face_dir;
844 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
845 const ContentFeatures &f = ndef->get(n);
847 tile.emissive_light = f.light_source;
849 // eg. water and glass
851 for (TileLayer &layer : tile.layers)
852 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
855 if (!data->m_smooth_lighting) {
856 lights[0] = lights[1] = lights[2] = lights[3] =
857 getFaceLight(n0, n1, face_dir, ndef);
859 v3s16 vertex_dirs[4];
860 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
862 v3s16 light_p = blockpos_nodes + p_corrected;
863 for (u16 i = 0; i < 4; i++)
864 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
870 translate_dir: unit vector with only one of x, y or z
871 face_dir: unit vector with only one of x, y or z
873 static void updateFastFaceRow(
875 const v3s16 &&startpos,
877 const v3f &&translate_dir_f,
878 const v3s16 &&face_dir,
879 std::vector<FastFace> &dest)
881 static thread_local const bool waving_liquids =
882 g_settings->getBool("enable_shaders") &&
883 g_settings->getBool("enable_waving_water");
887 u16 continuous_tiles_count = 1;
889 bool makes_face = false;
891 v3s16 face_dir_corrected;
892 u16 lights[4] = {0, 0, 0, 0};
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;
907 bool next_makes_face = false;
908 v3s16 next_p_corrected;
909 v3s16 next_face_dir_corrected;
910 u16 next_lights[4] = {0, 0, 0, 0};
912 // If at last position, there is nothing to compare to and
913 // the face must be drawn anyway
914 if (j != MAP_BLOCKSIZE - 1) {
915 p_next = p + translate_dir;
917 getTileInfo(data, p_next, face_dir,
918 next_makes_face, next_p_corrected,
919 next_face_dir_corrected, next_lights,
923 if (next_makes_face == makes_face
924 && next_p_corrected == p_corrected + translate_dir
925 && next_face_dir_corrected == face_dir_corrected
926 && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
927 // Don't apply fast faces to waving water.
928 && (waving != 3 || !waving_liquids)
929 && next_tile.isTileable(tile)) {
930 next_is_different = false;
931 continuous_tiles_count++;
934 if (next_is_different) {
936 Create a face if there should be one
939 // Floating point conversion of the position vector
940 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
941 // Center point of face (kind of)
942 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
946 if (translate_dir.X != 0)
947 scale.X = continuous_tiles_count;
948 if (translate_dir.Y != 0)
949 scale.Y = continuous_tiles_count;
950 if (translate_dir.Z != 0)
951 scale.Z = continuous_tiles_count;
953 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
954 pf, sp, face_dir_corrected, scale, dest);
955 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
958 continuous_tiles_count = 1;
961 makes_face = next_makes_face;
962 p_corrected = next_p_corrected;
963 face_dir_corrected = next_face_dir_corrected;
964 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
965 if (next_is_different)
971 static void updateAllFastFaceRows(MeshMakeData *data,
972 std::vector<FastFace> &dest)
975 Go through every y,z and get top(y+) faces in rows of x+
977 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
978 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
979 updateFastFaceRow(data,
981 v3s16(1, 0, 0), //dir
983 v3s16(0, 1, 0), //face dir
987 Go through every x,y and get right(x+) faces in rows of z+
989 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
990 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
991 updateFastFaceRow(data,
993 v3s16(0, 0, 1), //dir
995 v3s16(1, 0, 0), //face dir
999 Go through every y,z and get back(z+) faces in rows of x+
1001 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1002 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1003 updateFastFaceRow(data,
1005 v3s16(1, 0, 0), //dir
1007 v3s16(0, 0, 1), //face dir
1011 static void applyTileColor(PreMeshBuffer &pmb)
1013 video::SColor tc = pmb.layer.color;
1014 if (tc == video::SColor(0xFFFFFFFF))
1016 for (video::S3DVertex &vertex : pmb.vertices) {
1017 video::SColor *c = &vertex.Color;
1018 c->set(c->getAlpha(),
1019 c->getRed() * tc.getRed() / 255,
1020 c->getGreen() * tc.getGreen() / 255,
1021 c->getBlue() * tc.getBlue() / 255);
1029 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1030 m_minimap_mapblock(NULL),
1031 m_tsrc(data->m_client->getTextureSource()),
1032 m_shdrsrc(data->m_client->getShaderSource()),
1033 m_animation_force_timer(0), // force initial animation
1035 m_last_daynight_ratio((u32) -1)
1037 for (auto &m : m_mesh)
1038 m = new scene::SMesh();
1039 m_enable_shaders = data->m_use_shaders;
1040 m_use_tangent_vertices = data->m_use_tangent_vertices;
1041 m_enable_vbo = g_settings->getBool("enable_vbo");
1043 if (g_settings->getBool("enable_minimap")) {
1044 m_minimap_mapblock = new MinimapMapblock;
1045 m_minimap_mapblock->getMinimapNodes(
1046 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1049 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1050 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1051 //TimeTaker timer1("MapBlockMesh()");
1053 std::vector<FastFace> fastfaces_new;
1054 fastfaces_new.reserve(512);
1057 We are including the faces of the trailing edges of the block.
1058 This means that when something changes, the caller must
1059 also update the meshes of the blocks at the leading edges.
1061 NOTE: This is the slowest part of this method.
1064 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1065 //TimeTaker timer2("updateAllFastFaceRows()");
1066 updateAllFastFaceRows(data, fastfaces_new);
1071 Convert FastFaces to MeshCollector
1074 MeshCollector collector;
1077 // avg 0ms (100ms spikes when loading textures the first time)
1078 // (NOTE: probably outdated)
1079 //TimeTaker timer2("MeshCollector building");
1081 for (const FastFace &f : fastfaces_new) {
1082 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1083 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1084 const u16 *indices_p =
1085 f.vertex_0_2_connected ? indices : indices_alternate;
1086 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1091 Add special graphics:
1099 MapblockMeshGenerator generator(data, &collector);
1100 generator.generate();
1104 Convert MeshCollector to SMesh
1107 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1108 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1110 PreMeshBuffer &p = collector.prebuffers[layer][i];
1114 // Generate animation data
1116 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1117 // Find the texture name plus ^[crack:N:
1118 std::ostringstream os(std::ios::binary);
1119 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1120 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1121 os << "o"; // use ^[cracko
1122 u8 tiles = p.layer.scale;
1124 os << ":" << (u32)tiles;
1125 os << ":" << (u32)p.layer.animation_frame_count << ":";
1126 m_crack_materials.insert(std::make_pair(
1127 std::pair<u8, u32>(layer, i), os.str()));
1128 // Replace tile texture with the cracked one
1129 p.layer.texture = m_tsrc->getTextureForMesh(
1131 &p.layer.texture_id);
1133 // - Texture animation
1134 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1135 // Add to MapBlockMesh in order to animate these tiles
1136 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1137 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1138 if (g_settings->getBool(
1139 "desynchronize_mapblock_texture_animation")) {
1140 // Get starting position from noise
1141 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1142 100000 * (2.0 + noise3d(
1143 data->m_blockpos.X, data->m_blockpos.Y,
1144 data->m_blockpos.Z, 0));
1146 // Play all synchronized
1147 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1149 // Replace tile texture with the first animation frame
1150 p.layer.texture = (*p.layer.frames)[0].texture;
1153 if (!m_enable_shaders) {
1154 // Extract colors for day-night animation
1155 // Dummy sunlight to handle non-sunlit areas
1156 video::SColorf sunlight;
1157 get_sunlight_color(&sunlight, 0);
1158 u32 vertex_count = p.vertices.size();
1159 for (u32 j = 0; j < vertex_count; j++) {
1160 video::SColor *vc = &p.vertices[j].Color;
1161 video::SColor copy = *vc;
1162 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1163 final_color_blend(vc, copy, sunlight); // Finalize color
1164 else // Record color to animate
1165 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1167 // The sunlight ratio has been stored,
1168 // delete alpha (for the final rendering).
1174 video::SMaterial material;
1175 material.setFlag(video::EMF_LIGHTING, false);
1176 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1177 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1178 material.setFlag(video::EMF_FOG_ENABLE, true);
1179 material.setTexture(0, p.layer.texture);
1181 if (m_enable_shaders) {
1182 material.MaterialType = m_shdrsrc->getShaderInfo(
1183 p.layer.shader_id).material;
1184 p.layer.applyMaterialOptionsWithShaders(material);
1185 if (p.layer.normal_texture)
1186 material.setTexture(1, p.layer.normal_texture);
1187 material.setTexture(2, p.layer.flags_texture);
1189 p.layer.applyMaterialOptions(material);
1192 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1194 // Create meshbuffer, add to mesh
1195 if (m_use_tangent_vertices) {
1196 scene::SMeshBufferTangents *buf =
1197 new scene::SMeshBufferTangents();
1198 buf->Material = material;
1199 buf->Vertices.reallocate(p.vertices.size());
1200 buf->Indices.reallocate(p.indices.size());
1201 for (const video::S3DVertex &v: p.vertices)
1202 buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
1203 for (u16 i: p.indices)
1204 buf->Indices.push_back(i);
1205 buf->recalculateBoundingBox();
1206 mesh->addMeshBuffer(buf);
1209 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1210 buf->Material = material;
1211 buf->append(&p.vertices[0], p.vertices.size(),
1212 &p.indices[0], p.indices.size());
1213 mesh->addMeshBuffer(buf);
1219 Do some stuff to the mesh
1221 m_camera_offset = camera_offset;
1222 translateMesh(m_mesh[layer],
1223 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1225 if (m_use_tangent_vertices) {
1226 scene::IMeshManipulator* meshmanip =
1227 RenderingEngine::get_scene_manager()->getMeshManipulator();
1228 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1231 if (m_mesh[layer]) {
1233 // Usually 1-700 faces and 1-7 materials
1234 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1235 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1236 << " materials (meshbuffers)" << std::endl;
1239 // Use VBO for mesh (this just would set this for ever buffer)
1241 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1245 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1247 // Check if animation is required for this mesh
1249 !m_crack_materials.empty() ||
1250 !m_daynight_diffs.empty() ||
1251 !m_animation_tiles.empty();
1254 MapBlockMesh::~MapBlockMesh()
1256 for (scene::IMesh *m : m_mesh) {
1257 if (m_enable_vbo && m)
1258 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1259 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1260 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1265 delete m_minimap_mapblock;
1268 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1271 if (!m_has_animation) {
1272 m_animation_force_timer = 100000;
1276 m_animation_force_timer = myrand_range(5, 100);
1279 if (crack != m_last_crack) {
1280 for (auto &crack_material : m_crack_materials) {
1281 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1282 getMeshBuffer(crack_material.first.second);
1283 std::string basename = crack_material.second;
1285 // Create new texture name from original
1286 std::ostringstream os;
1287 os << basename << crack;
1288 u32 new_texture_id = 0;
1289 video::ITexture *new_texture =
1290 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1291 buf->getMaterial().setTexture(0, new_texture);
1293 // If the current material is also animated,
1294 // update animation info
1295 auto anim_iter = m_animation_tiles.find(crack_material.first);
1296 if (anim_iter != m_animation_tiles.end()) {
1297 TileLayer &tile = anim_iter->second;
1298 tile.texture = new_texture;
1299 tile.texture_id = new_texture_id;
1300 // force animation update
1301 m_animation_frames[crack_material.first] = -1;
1305 m_last_crack = crack;
1308 // Texture animation
1309 for (auto &animation_tile : m_animation_tiles) {
1310 const TileLayer &tile = animation_tile.second;
1311 // Figure out current frame
1312 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1313 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1314 + frameoffset) % tile.animation_frame_count;
1315 // If frame doesn't change, skip
1316 if (frame == m_animation_frames[animation_tile.first])
1319 m_animation_frames[animation_tile.first] = frame;
1321 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1322 getMeshBuffer(animation_tile.first.second);
1324 const FrameSpec &animation_frame = (*tile.frames)[frame];
1325 buf->getMaterial().setTexture(0, animation_frame.texture);
1326 if (m_enable_shaders) {
1327 if (animation_frame.normal_texture)
1328 buf->getMaterial().setTexture(1,
1329 animation_frame.normal_texture);
1330 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1334 // Day-night transition
1335 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1336 // Force reload mesh to VBO
1338 for (scene::IMesh *m : m_mesh)
1340 video::SColorf day_color;
1341 get_sunlight_color(&day_color, daynight_ratio);
1343 for (auto &daynight_diff : m_daynight_diffs) {
1344 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1345 getMeshBuffer(daynight_diff.first.second);
1346 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1347 for (const auto &j : daynight_diff.second)
1348 final_color_blend(&(vertices[j.first].Color), j.second,
1351 m_last_daynight_ratio = daynight_ratio;
1357 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1359 if (camera_offset != m_camera_offset) {
1360 for (scene::IMesh *layer : m_mesh) {
1361 translateMesh(layer,
1362 intToFloat(m_camera_offset - camera_offset, BS));
1366 m_camera_offset = camera_offset;
1370 video::SColor encode_light(u16 light, u8 emissive_light)
1373 u32 day = (light & 0xff);
1374 u32 night = (light >> 8);
1375 // Add emissive light
1376 night += emissive_light * 2.5f;
1379 // Since we don't know if the day light is sunlight or
1380 // artificial light, assume it is artificial when the night
1381 // light bank is also lit.
1386 u32 sum = day + night;
1387 // Ratio of sunlight:
1390 r = day * 255 / sum;
1394 float b = (day + night) / 2;
1395 return video::SColor(r, b, b, b);