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,
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);
945 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
948 continuous_tiles_count = 1;
951 makes_face = next_makes_face;
952 p_corrected = next_p_corrected;
953 face_dir_corrected = next_face_dir_corrected;
954 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
955 if (next_is_different)
961 static void updateAllFastFaceRows(MeshMakeData *data,
962 std::vector<FastFace> &dest)
965 Go through every y,z and get top(y+) faces in rows of x+
967 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
968 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
969 updateFastFaceRow(data,
971 v3s16(1, 0, 0), //dir
973 v3s16(0, 1, 0), //face dir
977 Go through every x,y and get right(x+) faces in rows of z+
979 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
980 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
981 updateFastFaceRow(data,
983 v3s16(0, 0, 1), //dir
985 v3s16(1, 0, 0), //face dir
989 Go through every y,z and get back(z+) faces in rows of x+
991 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
992 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
993 updateFastFaceRow(data,
995 v3s16(1, 0, 0), //dir
997 v3s16(0, 0, 1), //face dir
1001 static void applyTileColor(PreMeshBuffer &pmb)
1003 video::SColor tc = pmb.layer.color;
1004 if (tc == video::SColor(0xFFFFFFFF))
1006 for (video::S3DVertex &vertex : pmb.vertices) {
1007 video::SColor *c = &vertex.Color;
1008 c->set(c->getAlpha(),
1009 c->getRed() * tc.getRed() / 255,
1010 c->getGreen() * tc.getGreen() / 255,
1011 c->getBlue() * tc.getBlue() / 255);
1019 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1020 m_minimap_mapblock(NULL),
1021 m_tsrc(data->m_client->getTextureSource()),
1022 m_shdrsrc(data->m_client->getShaderSource()),
1023 m_animation_force_timer(0), // force initial animation
1025 m_last_daynight_ratio((u32) -1)
1027 for (auto &m : m_mesh)
1028 m = new scene::SMesh();
1029 m_enable_shaders = data->m_use_shaders;
1030 m_use_tangent_vertices = data->m_use_tangent_vertices;
1031 m_enable_vbo = g_settings->getBool("enable_vbo");
1033 if (g_settings->getBool("enable_minimap")) {
1034 m_minimap_mapblock = new MinimapMapblock;
1035 m_minimap_mapblock->getMinimapNodes(
1036 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1039 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1040 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1041 //TimeTaker timer1("MapBlockMesh()");
1043 std::vector<FastFace> fastfaces_new;
1044 fastfaces_new.reserve(512);
1047 We are including the faces of the trailing edges of the block.
1048 This means that when something changes, the caller must
1049 also update the meshes of the blocks at the leading edges.
1051 NOTE: This is the slowest part of this method.
1054 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1055 //TimeTaker timer2("updateAllFastFaceRows()");
1056 updateAllFastFaceRows(data, fastfaces_new);
1061 Convert FastFaces to MeshCollector
1064 MeshCollector collector;
1067 // avg 0ms (100ms spikes when loading textures the first time)
1068 // (NOTE: probably outdated)
1069 //TimeTaker timer2("MeshCollector building");
1071 for (const FastFace &f : fastfaces_new) {
1072 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1073 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1074 const u16 *indices_p =
1075 f.vertex_0_2_connected ? indices : indices_alternate;
1076 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1081 Add special graphics:
1089 MapblockMeshGenerator generator(data, &collector);
1090 generator.generate();
1094 Convert MeshCollector to SMesh
1097 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1098 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1100 PreMeshBuffer &p = collector.prebuffers[layer][i];
1104 // Generate animation data
1106 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1107 // Find the texture name plus ^[crack:N:
1108 std::ostringstream os(std::ios::binary);
1109 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1110 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1111 os << "o"; // use ^[cracko
1112 u8 tiles = p.layer.scale;
1114 os << ":" << (u32)tiles;
1115 os << ":" << (u32)p.layer.animation_frame_count << ":";
1116 m_crack_materials.insert(std::make_pair(
1117 std::pair<u8, u32>(layer, i), os.str()));
1118 // Replace tile texture with the cracked one
1119 p.layer.texture = m_tsrc->getTextureForMesh(
1121 &p.layer.texture_id);
1123 // - Texture animation
1124 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1125 // Add to MapBlockMesh in order to animate these tiles
1126 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1127 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1128 if (g_settings->getBool(
1129 "desynchronize_mapblock_texture_animation")) {
1130 // Get starting position from noise
1131 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1132 100000 * (2.0 + noise3d(
1133 data->m_blockpos.X, data->m_blockpos.Y,
1134 data->m_blockpos.Z, 0));
1136 // Play all synchronized
1137 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1139 // Replace tile texture with the first animation frame
1140 p.layer.texture = (*p.layer.frames)[0].texture;
1143 if (!m_enable_shaders) {
1144 // Extract colors for day-night animation
1145 // Dummy sunlight to handle non-sunlit areas
1146 video::SColorf sunlight;
1147 get_sunlight_color(&sunlight, 0);
1148 u32 vertex_count = p.vertices.size();
1149 for (u32 j = 0; j < vertex_count; j++) {
1150 video::SColor *vc = &p.vertices[j].Color;
1151 video::SColor copy = *vc;
1152 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1153 final_color_blend(vc, copy, sunlight); // Finalize color
1154 else // Record color to animate
1155 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1157 // The sunlight ratio has been stored,
1158 // delete alpha (for the final rendering).
1164 video::SMaterial material;
1165 material.setFlag(video::EMF_LIGHTING, false);
1166 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1167 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1168 material.setFlag(video::EMF_FOG_ENABLE, true);
1169 material.setTexture(0, p.layer.texture);
1171 if (m_enable_shaders) {
1172 material.MaterialType = m_shdrsrc->getShaderInfo(
1173 p.layer.shader_id).material;
1174 p.layer.applyMaterialOptionsWithShaders(material);
1175 if (p.layer.normal_texture)
1176 material.setTexture(1, p.layer.normal_texture);
1177 material.setTexture(2, p.layer.flags_texture);
1179 p.layer.applyMaterialOptions(material);
1182 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1184 // Create meshbuffer, add to mesh
1185 if (m_use_tangent_vertices) {
1186 scene::SMeshBufferTangents *buf =
1187 new scene::SMeshBufferTangents();
1188 buf->Material = material;
1189 buf->Vertices.reallocate(p.vertices.size());
1190 buf->Indices.reallocate(p.indices.size());
1191 for (const video::S3DVertex &v: p.vertices)
1192 buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
1193 for (u16 i: p.indices)
1194 buf->Indices.push_back(i);
1195 buf->recalculateBoundingBox();
1196 mesh->addMeshBuffer(buf);
1199 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1200 buf->Material = material;
1201 buf->append(&p.vertices[0], p.vertices.size(),
1202 &p.indices[0], p.indices.size());
1203 mesh->addMeshBuffer(buf);
1209 Do some stuff to the mesh
1211 m_camera_offset = camera_offset;
1212 translateMesh(m_mesh[layer],
1213 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1215 if (m_use_tangent_vertices) {
1216 scene::IMeshManipulator* meshmanip =
1217 RenderingEngine::get_scene_manager()->getMeshManipulator();
1218 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1221 if (m_mesh[layer]) {
1223 // Usually 1-700 faces and 1-7 materials
1224 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1225 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1226 << " materials (meshbuffers)" << std::endl;
1229 // Use VBO for mesh (this just would set this for ever buffer)
1231 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1235 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1237 // Check if animation is required for this mesh
1239 !m_crack_materials.empty() ||
1240 !m_daynight_diffs.empty() ||
1241 !m_animation_tiles.empty();
1244 MapBlockMesh::~MapBlockMesh()
1246 for (scene::IMesh *m : m_mesh) {
1247 if (m_enable_vbo && m)
1248 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1249 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1250 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1255 delete m_minimap_mapblock;
1258 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1261 if (!m_has_animation) {
1262 m_animation_force_timer = 100000;
1266 m_animation_force_timer = myrand_range(5, 100);
1269 if (crack != m_last_crack) {
1270 for (auto &crack_material : m_crack_materials) {
1271 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1272 getMeshBuffer(crack_material.first.second);
1273 std::string basename = crack_material.second;
1275 // Create new texture name from original
1276 std::ostringstream os;
1277 os << basename << crack;
1278 u32 new_texture_id = 0;
1279 video::ITexture *new_texture =
1280 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1281 buf->getMaterial().setTexture(0, new_texture);
1283 // If the current material is also animated,
1284 // update animation info
1285 auto anim_iter = m_animation_tiles.find(crack_material.first);
1286 if (anim_iter != m_animation_tiles.end()) {
1287 TileLayer &tile = anim_iter->second;
1288 tile.texture = new_texture;
1289 tile.texture_id = new_texture_id;
1290 // force animation update
1291 m_animation_frames[crack_material.first] = -1;
1295 m_last_crack = crack;
1298 // Texture animation
1299 for (auto &animation_tile : m_animation_tiles) {
1300 const TileLayer &tile = animation_tile.second;
1301 // Figure out current frame
1302 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1303 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1304 + frameoffset) % tile.animation_frame_count;
1305 // If frame doesn't change, skip
1306 if (frame == m_animation_frames[animation_tile.first])
1309 m_animation_frames[animation_tile.first] = frame;
1311 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1312 getMeshBuffer(animation_tile.first.second);
1314 const FrameSpec &animation_frame = (*tile.frames)[frame];
1315 buf->getMaterial().setTexture(0, animation_frame.texture);
1316 if (m_enable_shaders) {
1317 if (animation_frame.normal_texture)
1318 buf->getMaterial().setTexture(1,
1319 animation_frame.normal_texture);
1320 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1324 // Day-night transition
1325 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1326 // Force reload mesh to VBO
1328 for (scene::IMesh *m : m_mesh)
1330 video::SColorf day_color;
1331 get_sunlight_color(&day_color, daynight_ratio);
1333 for (auto &daynight_diff : m_daynight_diffs) {
1334 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1335 getMeshBuffer(daynight_diff.first.second);
1336 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1337 for (const auto &j : daynight_diff.second)
1338 final_color_blend(&(vertices[j.first].Color), j.second,
1341 m_last_daynight_ratio = daynight_ratio;
1347 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1349 if (camera_offset != m_camera_offset) {
1350 for (scene::IMesh *layer : m_mesh) {
1351 translateMesh(layer,
1352 intToFloat(m_camera_offset - camera_offset, BS));
1356 m_camera_offset = camera_offset;
1360 video::SColor encode_light(u16 light, u8 emissive_light)
1363 u32 day = (light & 0xff);
1364 u32 night = (light >> 8);
1365 // Add emissive light
1366 night += emissive_light * 2.5f;
1369 // Since we don't know if the day light is sunlight or
1370 // artificial light, assume it is artificial when the night
1371 // light bank is also lit.
1376 u32 sum = day + night;
1377 // Ratio of sunlight:
1380 r = day * 255 / sum;
1384 float b = (day + night) / 2;
1385 return video::SColor(r, b, b, b);