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);
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,
801 VoxelManipulator &vmanip = data->m_vmanip;
802 const NodeDefManager *ndef = data->m_client->ndef();
803 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
805 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
807 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
808 if (n0.getContent() == CONTENT_IGNORE) {
813 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
815 if (n1.getContent() == CONTENT_IGNORE) {
821 bool equivalent = false;
822 u8 mf = face_contents(n0.getContent(), n1.getContent(),
836 face_dir_corrected = face_dir;
839 p_corrected = p + face_dir;
840 face_dir_corrected = -face_dir;
843 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
844 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)
881 u16 continuous_tiles_count = 1;
883 bool makes_face = false;
885 v3s16 face_dir_corrected;
886 u16 lights[4] = {0, 0, 0, 0};
888 getTileInfo(data, p, face_dir,
889 makes_face, p_corrected, face_dir_corrected,
892 // Unroll this variable which has a significant build cost
894 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
895 // If tiling can be done, this is set to false in the next step
896 bool next_is_different = true;
900 bool next_makes_face = false;
901 v3s16 next_p_corrected;
902 v3s16 next_face_dir_corrected;
903 u16 next_lights[4] = {0, 0, 0, 0};
905 // If at last position, there is nothing to compare to and
906 // the face must be drawn anyway
907 if (j != MAP_BLOCKSIZE - 1) {
908 p_next = p + translate_dir;
910 getTileInfo(data, p_next, face_dir,
911 next_makes_face, next_p_corrected,
912 next_face_dir_corrected, next_lights,
915 if (next_makes_face == makes_face
916 && next_p_corrected == p_corrected + translate_dir
917 && next_face_dir_corrected == face_dir_corrected
918 && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
919 && next_tile.isTileable(tile)) {
920 next_is_different = false;
921 continuous_tiles_count++;
924 if (next_is_different) {
926 Create a face if there should be one
929 // Floating point conversion of the position vector
930 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
931 // Center point of face (kind of)
932 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
936 if (translate_dir.X != 0)
937 scale.X = continuous_tiles_count;
938 if (translate_dir.Y != 0)
939 scale.Y = continuous_tiles_count;
940 if (translate_dir.Z != 0)
941 scale.Z = continuous_tiles_count;
943 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
944 pf, sp, face_dir_corrected, scale, dest);
946 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
947 for (int i = 1; i < continuous_tiles_count; i++)
948 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
951 continuous_tiles_count = 1;
954 makes_face = next_makes_face;
955 p_corrected = next_p_corrected;
956 face_dir_corrected = next_face_dir_corrected;
957 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
958 if (next_is_different)
964 static void updateAllFastFaceRows(MeshMakeData *data,
965 std::vector<FastFace> &dest)
968 Go through every y,z and get top(y+) faces in rows of x+
970 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
971 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
972 updateFastFaceRow(data,
974 v3s16(1, 0, 0), //dir
976 v3s16(0, 1, 0), //face dir
980 Go through every x,y and get right(x+) faces in rows of z+
982 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
983 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
984 updateFastFaceRow(data,
986 v3s16(0, 0, 1), //dir
988 v3s16(1, 0, 0), //face dir
992 Go through every y,z and get back(z+) faces in rows of x+
994 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
995 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
996 updateFastFaceRow(data,
998 v3s16(1, 0, 0), //dir
1000 v3s16(0, 0, 1), //face dir
1004 static void applyTileColor(PreMeshBuffer &pmb)
1006 video::SColor tc = pmb.layer.color;
1007 if (tc == video::SColor(0xFFFFFFFF))
1009 for (video::S3DVertex &vertex : pmb.vertices) {
1010 video::SColor *c = &vertex.Color;
1011 c->set(c->getAlpha(),
1012 c->getRed() * tc.getRed() / 255,
1013 c->getGreen() * tc.getGreen() / 255,
1014 c->getBlue() * tc.getBlue() / 255);
1022 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1023 m_minimap_mapblock(NULL),
1024 m_tsrc(data->m_client->getTextureSource()),
1025 m_shdrsrc(data->m_client->getShaderSource()),
1026 m_animation_force_timer(0), // force initial animation
1028 m_last_daynight_ratio((u32) -1)
1030 for (auto &m : m_mesh)
1031 m = new scene::SMesh();
1032 m_enable_shaders = data->m_use_shaders;
1033 m_use_tangent_vertices = data->m_use_tangent_vertices;
1034 m_enable_vbo = g_settings->getBool("enable_vbo");
1036 if (g_settings->getBool("enable_minimap")) {
1037 m_minimap_mapblock = new MinimapMapblock;
1038 m_minimap_mapblock->getMinimapNodes(
1039 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1042 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1043 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1044 //TimeTaker timer1("MapBlockMesh()");
1046 std::vector<FastFace> fastfaces_new;
1047 fastfaces_new.reserve(512);
1050 We are including the faces of the trailing edges of the block.
1051 This means that when something changes, the caller must
1052 also update the meshes of the blocks at the leading edges.
1054 NOTE: This is the slowest part of this method.
1057 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1058 //TimeTaker timer2("updateAllFastFaceRows()");
1059 updateAllFastFaceRows(data, fastfaces_new);
1064 Convert FastFaces to MeshCollector
1067 MeshCollector collector;
1070 // avg 0ms (100ms spikes when loading textures the first time)
1071 // (NOTE: probably outdated)
1072 //TimeTaker timer2("MeshCollector building");
1074 for (const FastFace &f : fastfaces_new) {
1075 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1076 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1077 const u16 *indices_p =
1078 f.vertex_0_2_connected ? indices : indices_alternate;
1079 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1084 Add special graphics:
1092 MapblockMeshGenerator generator(data, &collector);
1093 generator.generate();
1097 Convert MeshCollector to SMesh
1100 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1101 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1103 PreMeshBuffer &p = collector.prebuffers[layer][i];
1107 // Generate animation data
1109 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1110 // Find the texture name plus ^[crack:N:
1111 std::ostringstream os(std::ios::binary);
1112 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1113 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1114 os << "o"; // use ^[cracko
1115 u8 tiles = p.layer.scale;
1117 os << ":" << (u32)tiles;
1118 os << ":" << (u32)p.layer.animation_frame_count << ":";
1119 m_crack_materials.insert(std::make_pair(
1120 std::pair<u8, u32>(layer, i), os.str()));
1121 // Replace tile texture with the cracked one
1122 p.layer.texture = m_tsrc->getTextureForMesh(
1124 &p.layer.texture_id);
1126 // - Texture animation
1127 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1128 // Add to MapBlockMesh in order to animate these tiles
1129 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1130 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1131 if (g_settings->getBool(
1132 "desynchronize_mapblock_texture_animation")) {
1133 // Get starting position from noise
1134 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1135 100000 * (2.0 + noise3d(
1136 data->m_blockpos.X, data->m_blockpos.Y,
1137 data->m_blockpos.Z, 0));
1139 // Play all synchronized
1140 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1142 // Replace tile texture with the first animation frame
1143 p.layer.texture = (*p.layer.frames)[0].texture;
1146 if (!m_enable_shaders) {
1147 // Extract colors for day-night animation
1148 // Dummy sunlight to handle non-sunlit areas
1149 video::SColorf sunlight;
1150 get_sunlight_color(&sunlight, 0);
1151 u32 vertex_count = p.vertices.size();
1152 for (u32 j = 0; j < vertex_count; j++) {
1153 video::SColor *vc = &p.vertices[j].Color;
1154 video::SColor copy = *vc;
1155 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1156 final_color_blend(vc, copy, sunlight); // Finalize color
1157 else // Record color to animate
1158 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1160 // The sunlight ratio has been stored,
1161 // delete alpha (for the final rendering).
1167 video::SMaterial material;
1168 material.setFlag(video::EMF_LIGHTING, false);
1169 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1170 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1171 material.setFlag(video::EMF_FOG_ENABLE, true);
1172 material.setTexture(0, p.layer.texture);
1174 if (m_enable_shaders) {
1175 material.MaterialType = m_shdrsrc->getShaderInfo(
1176 p.layer.shader_id).material;
1177 p.layer.applyMaterialOptionsWithShaders(material);
1178 if (p.layer.normal_texture)
1179 material.setTexture(1, p.layer.normal_texture);
1180 material.setTexture(2, p.layer.flags_texture);
1182 p.layer.applyMaterialOptions(material);
1185 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1187 // Create meshbuffer, add to mesh
1188 if (m_use_tangent_vertices) {
1189 scene::SMeshBufferTangents *buf =
1190 new scene::SMeshBufferTangents();
1191 buf->Material = material;
1192 buf->Vertices.reallocate(p.vertices.size());
1193 buf->Indices.reallocate(p.indices.size());
1194 for (const video::S3DVertex &v: p.vertices)
1195 buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
1196 for (u16 i: p.indices)
1197 buf->Indices.push_back(i);
1198 buf->recalculateBoundingBox();
1199 mesh->addMeshBuffer(buf);
1202 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1203 buf->Material = material;
1204 buf->append(&p.vertices[0], p.vertices.size(),
1205 &p.indices[0], p.indices.size());
1206 mesh->addMeshBuffer(buf);
1212 Do some stuff to the mesh
1214 m_camera_offset = camera_offset;
1215 translateMesh(m_mesh[layer],
1216 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1218 if (m_use_tangent_vertices) {
1219 scene::IMeshManipulator* meshmanip =
1220 RenderingEngine::get_scene_manager()->getMeshManipulator();
1221 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1224 if (m_mesh[layer]) {
1226 // Usually 1-700 faces and 1-7 materials
1227 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1228 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1229 << " materials (meshbuffers)" << std::endl;
1232 // Use VBO for mesh (this just would set this for ever buffer)
1234 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1238 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1240 // Check if animation is required for this mesh
1242 !m_crack_materials.empty() ||
1243 !m_daynight_diffs.empty() ||
1244 !m_animation_tiles.empty();
1247 MapBlockMesh::~MapBlockMesh()
1249 for (scene::IMesh *m : m_mesh) {
1250 if (m_enable_vbo && m)
1251 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1252 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1253 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1258 delete m_minimap_mapblock;
1261 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1264 if (!m_has_animation) {
1265 m_animation_force_timer = 100000;
1269 m_animation_force_timer = myrand_range(5, 100);
1272 if (crack != m_last_crack) {
1273 for (auto &crack_material : m_crack_materials) {
1274 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1275 getMeshBuffer(crack_material.first.second);
1276 std::string basename = crack_material.second;
1278 // Create new texture name from original
1279 std::ostringstream os;
1280 os << basename << crack;
1281 u32 new_texture_id = 0;
1282 video::ITexture *new_texture =
1283 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1284 buf->getMaterial().setTexture(0, new_texture);
1286 // If the current material is also animated,
1287 // update animation info
1288 auto anim_iter = m_animation_tiles.find(crack_material.first);
1289 if (anim_iter != m_animation_tiles.end()) {
1290 TileLayer &tile = anim_iter->second;
1291 tile.texture = new_texture;
1292 tile.texture_id = new_texture_id;
1293 // force animation update
1294 m_animation_frames[crack_material.first] = -1;
1298 m_last_crack = crack;
1301 // Texture animation
1302 for (auto &animation_tile : m_animation_tiles) {
1303 const TileLayer &tile = animation_tile.second;
1304 // Figure out current frame
1305 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1306 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1307 + frameoffset) % tile.animation_frame_count;
1308 // If frame doesn't change, skip
1309 if (frame == m_animation_frames[animation_tile.first])
1312 m_animation_frames[animation_tile.first] = frame;
1314 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1315 getMeshBuffer(animation_tile.first.second);
1317 const FrameSpec &animation_frame = (*tile.frames)[frame];
1318 buf->getMaterial().setTexture(0, animation_frame.texture);
1319 if (m_enable_shaders) {
1320 if (animation_frame.normal_texture)
1321 buf->getMaterial().setTexture(1,
1322 animation_frame.normal_texture);
1323 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1327 // Day-night transition
1328 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1329 // Force reload mesh to VBO
1331 for (scene::IMesh *m : m_mesh)
1333 video::SColorf day_color;
1334 get_sunlight_color(&day_color, daynight_ratio);
1336 for (auto &daynight_diff : m_daynight_diffs) {
1337 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1338 getMeshBuffer(daynight_diff.first.second);
1339 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1340 for (const auto &j : daynight_diff.second)
1341 final_color_blend(&(vertices[j.first].Color), j.second,
1344 m_last_daynight_ratio = daynight_ratio;
1350 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1352 if (camera_offset != m_camera_offset) {
1353 for (scene::IMesh *layer : m_mesh) {
1354 translateMesh(layer,
1355 intToFloat(m_camera_offset - camera_offset, BS));
1359 m_camera_offset = camera_offset;
1363 video::SColor encode_light(u16 light, u8 emissive_light)
1366 u32 day = (light & 0xff);
1367 u32 night = (light >> 8);
1368 // Add emissive light
1369 night += emissive_light * 2.5f;
1372 // Since we don't know if the day light is sunlight or
1373 // artificial light, assume it is artificial when the night
1374 // light bank is also lit.
1379 u32 sum = day + night;
1380 // Ratio of sunlight:
1383 r = day * 255 / sum;
1387 float b = (day + night) / 2;
1388 return video::SColor(r, b, b, b);