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 if(g_settings->getBool("fullbright"))
138 return decode_light(light);
142 Calculate non-smooth lighting at interior of node.
145 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
147 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
148 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
149 return day | (night << 8);
153 Calculate non-smooth lighting at face of node.
156 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
157 v3s16 face_dir, const NodeDefManager *ndef)
160 u8 l1 = n.getLight(bank, ndef);
161 u8 l2 = n2.getLight(bank, ndef);
167 // Boost light level for light sources
168 u8 light_source = MYMAX(ndef->get(n).light_source,
169 ndef->get(n2).light_source);
170 if(light_source > light)
171 light = light_source;
172 if(g_settings->getBool("fullbright"))
174 return decode_light(light);
178 Calculate non-smooth lighting at face of node.
181 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
182 const NodeDefManager *ndef)
184 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
185 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
186 return day | (night << 8);
190 Calculate smooth lighting at the XYZ- corner of p.
193 static u16 getSmoothLightCombined(const v3s16 &p,
194 const std::array<v3s16,8> &dirs, MeshMakeData *data)
196 const NodeDefManager *ndef = data->m_client->ndef();
198 u16 ambient_occlusion = 0;
200 u8 light_source_max = 0;
203 bool direct_sunlight = false;
205 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
210 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
211 if (n.getContent() == CONTENT_IGNORE)
213 const ContentFeatures &f = ndef->get(n);
214 if (f.light_source > light_source_max)
215 light_source_max = f.light_source;
216 // Check f.solidness because fast-style leaves look better this way
217 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
218 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
219 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
220 if (light_level_day == LIGHT_SUN)
221 direct_sunlight = true;
222 light_day += decode_light(light_level_day);
223 light_night += decode_light(light_level_night);
228 return f.light_propagates;
231 std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
233 bool opaque1 = !add_node(1);
234 bool opaque2 = !add_node(2);
235 bool opaque3 = !add_node(3);
236 obstructed[0] = opaque1 && opaque2;
237 obstructed[1] = opaque1 && opaque3;
238 obstructed[2] = opaque2 && opaque3;
239 for (u8 k = 0; k < 3; ++k)
240 if (add_node(k + 4, obstructed[k]))
241 obstructed[3] = false;
242 if (add_node(7, obstructed[3])) { // wrap light around nodes
243 ambient_occlusion -= 3;
244 for (u8 k = 0; k < 3; ++k)
245 add_node(k + 4, !obstructed[k]);
248 if (light_count == 0) {
249 light_day = light_night = 0;
251 light_day /= light_count;
252 light_night /= light_count;
255 // boost direct sunlight, if any
259 // Boost brightness around light sources
260 bool skip_ambient_occlusion_day = false;
261 if (decode_light(light_source_max) >= light_day) {
262 light_day = decode_light(light_source_max);
263 skip_ambient_occlusion_day = true;
266 bool skip_ambient_occlusion_night = false;
267 if(decode_light(light_source_max) >= light_night) {
268 light_night = decode_light(light_source_max);
269 skip_ambient_occlusion_night = true;
272 if (ambient_occlusion > 4) {
273 static thread_local const float ao_gamma = rangelim(
274 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
276 // Table of gamma space multiply factors.
277 static thread_local const float light_amount[3] = {
278 powf(0.75, 1.0 / ao_gamma),
279 powf(0.5, 1.0 / ao_gamma),
280 powf(0.25, 1.0 / ao_gamma)
283 //calculate table index for gamma space multiplier
284 ambient_occlusion -= 5;
286 if (!skip_ambient_occlusion_day)
287 light_day = rangelim(core::round32(
288 light_day * light_amount[ambient_occlusion]), 0, 255);
289 if (!skip_ambient_occlusion_night)
290 light_night = rangelim(core::round32(
291 light_night * light_amount[ambient_occlusion]), 0, 255);
294 return light_day | (light_night << 8);
298 Calculate smooth lighting at the given corner of p.
300 Node at p is solid, and thus the lighting is face-dependent.
302 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
304 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
308 Calculate smooth lighting at the given corner of p.
310 Node at p is not solid, and the lighting is not face-dependent.
312 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
314 const std::array<v3s16,8> dirs = {{
315 // Always shine light
322 v3s16(corner.X,corner.Y,0),
323 v3s16(corner.X,0,corner.Z),
324 v3s16(0,corner.Y,corner.Z),
325 v3s16(corner.X,corner.Y,corner.Z)
327 return getSmoothLightCombined(p, dirs, data);
330 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
331 f32 rg = daynight_ratio / 1000.0f - 0.04f;
332 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
338 void final_color_blend(video::SColor *result,
339 u16 light, u32 daynight_ratio)
341 video::SColorf dayLight;
342 get_sunlight_color(&dayLight, daynight_ratio);
343 final_color_blend(result,
344 encode_light(light, 0), dayLight);
347 void final_color_blend(video::SColor *result,
348 const video::SColor &data, const video::SColorf &dayLight)
350 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
352 video::SColorf c(data);
355 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
356 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
357 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
359 // Emphase blue a bit in darker places
360 // Each entry of this array represents a range of 8 blue levels
361 static const u8 emphase_blue_when_dark[32] = {
362 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
366 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
367 0, 255) / 8] / 255.0f;
369 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
370 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
371 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
375 Mesh generation helpers
379 vertex_dirs: v3s16[4]
381 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
384 If looked from outside the node towards the face, the corners are:
390 if (dir == v3s16(0, 0, 1)) {
391 // If looking towards z+, this is the face that is behind
392 // the center point, facing towards z+.
393 vertex_dirs[0] = v3s16(-1,-1, 1);
394 vertex_dirs[1] = v3s16( 1,-1, 1);
395 vertex_dirs[2] = v3s16( 1, 1, 1);
396 vertex_dirs[3] = v3s16(-1, 1, 1);
397 } else if (dir == v3s16(0, 0, -1)) {
399 vertex_dirs[0] = v3s16( 1,-1,-1);
400 vertex_dirs[1] = v3s16(-1,-1,-1);
401 vertex_dirs[2] = v3s16(-1, 1,-1);
402 vertex_dirs[3] = v3s16( 1, 1,-1);
403 } else if (dir == v3s16(1, 0, 0)) {
405 vertex_dirs[0] = v3s16( 1,-1, 1);
406 vertex_dirs[1] = v3s16( 1,-1,-1);
407 vertex_dirs[2] = v3s16( 1, 1,-1);
408 vertex_dirs[3] = v3s16( 1, 1, 1);
409 } else if (dir == v3s16(-1, 0, 0)) {
411 vertex_dirs[0] = v3s16(-1,-1,-1);
412 vertex_dirs[1] = v3s16(-1,-1, 1);
413 vertex_dirs[2] = v3s16(-1, 1, 1);
414 vertex_dirs[3] = v3s16(-1, 1,-1);
415 } else if (dir == v3s16(0, 1, 0)) {
416 // faces towards Y+ (assume Z- as "down" in texture)
417 vertex_dirs[0] = v3s16( 1, 1,-1);
418 vertex_dirs[1] = v3s16(-1, 1,-1);
419 vertex_dirs[2] = v3s16(-1, 1, 1);
420 vertex_dirs[3] = v3s16( 1, 1, 1);
421 } else if (dir == v3s16(0, -1, 0)) {
422 // faces towards Y- (assume Z+ as "down" in texture)
423 vertex_dirs[0] = v3s16( 1,-1, 1);
424 vertex_dirs[1] = v3s16(-1,-1, 1);
425 vertex_dirs[2] = v3s16(-1,-1,-1);
426 vertex_dirs[3] = v3s16( 1,-1,-1);
430 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
432 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
434 if (dir == v3s16(0,0,1)) {
437 } else if (dir == v3s16(0,0,-1)) {
440 } else if (dir == v3s16(1,0,0)) {
443 } else if (dir == v3s16(-1,0,0)) {
446 } else if (dir == v3s16(0,1,0)) {
449 } else if (dir == v3s16(0,-1,0)) {
458 video::S3DVertex vertices[4]; // Precalculated vertices
460 * The face is divided into two triangles. If this is true,
461 * vertices 0 and 2 are connected, othervise vertices 1 and 3
464 bool vertex_0_2_connected;
467 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
468 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
470 // Position is at the center of the cube.
479 v3s16 vertex_dirs[4];
480 getNodeVertexDirs(dir, vertex_dirs);
481 if (tile.world_aligned)
482 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
486 switch (tile.rotation) {
491 vertex_dirs[0] = vertex_dirs[3];
492 vertex_dirs[3] = vertex_dirs[2];
493 vertex_dirs[2] = vertex_dirs[1];
503 vertex_dirs[0] = vertex_dirs[2];
506 vertex_dirs[1] = vertex_dirs[3];
517 vertex_dirs[0] = vertex_dirs[1];
518 vertex_dirs[1] = vertex_dirs[2];
519 vertex_dirs[2] = vertex_dirs[3];
529 vertex_dirs[0] = vertex_dirs[3];
530 vertex_dirs[3] = vertex_dirs[2];
531 vertex_dirs[2] = vertex_dirs[1];
543 vertex_dirs[0] = vertex_dirs[1];
544 vertex_dirs[1] = vertex_dirs[2];
545 vertex_dirs[2] = vertex_dirs[3];
557 vertex_dirs[0] = vertex_dirs[3];
558 vertex_dirs[3] = vertex_dirs[2];
559 vertex_dirs[2] = vertex_dirs[1];
571 vertex_dirs[0] = vertex_dirs[1];
572 vertex_dirs[1] = vertex_dirs[2];
573 vertex_dirs[2] = vertex_dirs[3];
595 for (u16 i = 0; i < 4; i++) {
597 BS / 2 * vertex_dirs[i].X,
598 BS / 2 * vertex_dirs[i].Y,
599 BS / 2 * vertex_dirs[i].Z
603 for (v3f &vpos : vertex_pos) {
610 f32 abs_scale = 1.0f;
611 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
612 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
613 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
615 v3f normal(dir.X, dir.Y, dir.Z);
617 u16 li[4] = { li0, li1, li2, li3 };
621 for (u8 i = 0; i < 4; i++) {
623 night[i] = li[i] & 0xFF;
626 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
627 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
630 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
631 core::vector2d<f32>(x0, y0 + h),
632 core::vector2d<f32>(x0, y0),
633 core::vector2d<f32>(x0 + w * abs_scale, y0) };
635 // equivalent to dest.push_back(FastFace()) but faster
637 FastFace& face = *dest.rbegin();
639 for (u8 i = 0; i < 4; i++) {
640 video::SColor c = encode_light(li[i], tile.emissive_light);
641 if (!tile.emissive_light)
642 applyFacesShading(c, normal);
644 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
648 Revert triangles for nicer looking gradient if the
649 brightness of vertices 1 and 3 differ less than
650 the brightness of vertices 0 and 2.
652 face.vertex_0_2_connected = vertex_0_2_connected;
657 Nodes make a face if contents differ and solidness differs.
660 1: Face uses m1's content
661 2: Face uses m2's content
662 equivalent: Whether the blocks share the same face (eg. water and glass)
664 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
666 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
667 const NodeDefManager *ndef)
671 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
674 const ContentFeatures &f1 = ndef->get(m1);
675 const ContentFeatures &f2 = ndef->get(m2);
677 // Contents don't differ for different forms of same liquid
678 if (f1.sameLiquid(f2))
681 u8 c1 = f1.solidness;
682 u8 c2 = f2.solidness;
688 c1 = f1.visual_solidness;
690 c2 = f2.visual_solidness;
694 // If same solidness, liquid takes precense
708 Gets nth node tile (0 <= n <= 5).
710 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
712 const NodeDefManager *ndef = data->m_client->ndef();
713 const ContentFeatures &f = ndef->get(mn);
714 tile = f.tiles[tileindex];
715 bool has_crack = p == data->m_crack_pos_relative;
716 for (TileLayer &layer : tile.layers) {
717 if (layer.texture_id == 0)
719 if (!layer.has_color)
720 mn.getColor(f, &(layer.color));
721 // Apply temporary crack
723 layer.material_flags |= MATERIAL_FLAG_CRACK;
728 Gets node tile given a face direction.
730 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
732 const NodeDefManager *ndef = data->m_client->ndef();
734 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
735 // (0,0,1), (0,0,-1) or (0,0,0)
736 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
738 // Convert direction to single integer for table lookup
743 // 4 = invalid, treat as (0,0,0)
747 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
749 // Get rotation for things like chests
750 u8 facedir = mn.getFaceDir(ndef, true);
752 static const u16 dir_to_tile[24 * 16] =
754 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
755 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
756 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
757 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
758 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
760 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
761 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
762 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
763 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
765 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
766 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
767 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
768 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
770 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
771 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
772 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
773 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
775 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
776 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
777 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
778 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
780 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
781 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
782 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
783 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
786 u16 tile_index = facedir * 16 + dir_i;
787 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
788 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
791 static void getTileInfo(
795 const v3s16 &face_dir,
799 v3s16 &face_dir_corrected,
805 VoxelManipulator &vmanip = data->m_vmanip;
806 const NodeDefManager *ndef = data->m_client->ndef();
807 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
809 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
811 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
812 if (n0.getContent() == CONTENT_IGNORE) {
817 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
819 if (n1.getContent() == CONTENT_IGNORE) {
825 bool equivalent = false;
826 u8 mf = face_contents(n0.getContent(), n1.getContent(),
840 face_dir_corrected = face_dir;
843 p_corrected = p + face_dir;
844 face_dir_corrected = -face_dir;
847 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
848 const ContentFeatures &f = ndef->get(n);
850 tile.emissive_light = f.light_source;
852 // eg. water and glass
854 for (TileLayer &layer : tile.layers)
855 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
858 if (!data->m_smooth_lighting) {
859 lights[0] = lights[1] = lights[2] = lights[3] =
860 getFaceLight(n0, n1, face_dir, ndef);
862 v3s16 vertex_dirs[4];
863 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
865 v3s16 light_p = blockpos_nodes + p_corrected;
866 for (u16 i = 0; i < 4; i++)
867 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
873 translate_dir: unit vector with only one of x, y or z
874 face_dir: unit vector with only one of x, y or z
876 static void updateFastFaceRow(
878 const v3s16 &&startpos,
880 const v3f &&translate_dir_f,
881 const v3s16 &&face_dir,
882 std::vector<FastFace> &dest)
884 static thread_local const bool waving_liquids =
885 g_settings->getBool("enable_shaders") &&
886 g_settings->getBool("enable_waving_water");
890 u16 continuous_tiles_count = 1;
892 bool makes_face = false;
894 v3s16 face_dir_corrected;
895 u16 lights[4] = {0, 0, 0, 0};
898 getTileInfo(data, p, face_dir,
899 makes_face, p_corrected, face_dir_corrected,
900 lights, waving, tile);
902 // Unroll this variable which has a significant build cost
904 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
905 // If tiling can be done, this is set to false in the next step
906 bool next_is_different = true;
910 bool next_makes_face = false;
911 v3s16 next_p_corrected;
912 v3s16 next_face_dir_corrected;
913 u16 next_lights[4] = {0, 0, 0, 0};
915 // If at last position, there is nothing to compare to and
916 // the face must be drawn anyway
917 if (j != MAP_BLOCKSIZE - 1) {
918 p_next = p + translate_dir;
920 getTileInfo(data, p_next, face_dir,
921 next_makes_face, next_p_corrected,
922 next_face_dir_corrected, next_lights,
926 if (next_makes_face == makes_face
927 && next_p_corrected == p_corrected + translate_dir
928 && next_face_dir_corrected == face_dir_corrected
929 && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
930 // Don't apply fast faces to waving water.
931 && (waving != 3 || !waving_liquids)
932 && next_tile.isTileable(tile)) {
933 next_is_different = false;
934 continuous_tiles_count++;
937 if (next_is_different) {
939 Create a face if there should be one
942 // Floating point conversion of the position vector
943 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
944 // Center point of face (kind of)
945 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
949 if (translate_dir.X != 0)
950 scale.X = continuous_tiles_count;
951 if (translate_dir.Y != 0)
952 scale.Y = continuous_tiles_count;
953 if (translate_dir.Z != 0)
954 scale.Z = continuous_tiles_count;
956 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
957 pf, sp, face_dir_corrected, scale, dest);
958 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
961 continuous_tiles_count = 1;
964 makes_face = next_makes_face;
965 p_corrected = next_p_corrected;
966 face_dir_corrected = next_face_dir_corrected;
967 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
968 if (next_is_different)
974 static void updateAllFastFaceRows(MeshMakeData *data,
975 std::vector<FastFace> &dest)
978 Go through every y,z and get top(y+) faces in rows of x+
980 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
981 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
982 updateFastFaceRow(data,
984 v3s16(1, 0, 0), //dir
986 v3s16(0, 1, 0), //face dir
990 Go through every x,y and get right(x+) faces in rows of z+
992 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
993 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
994 updateFastFaceRow(data,
996 v3s16(0, 0, 1), //dir
998 v3s16(1, 0, 0), //face dir
1002 Go through every y,z and get back(z+) faces in rows of x+
1004 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1005 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1006 updateFastFaceRow(data,
1008 v3s16(1, 0, 0), //dir
1010 v3s16(0, 0, 1), //face dir
1014 static void applyTileColor(PreMeshBuffer &pmb)
1016 video::SColor tc = pmb.layer.color;
1017 if (tc == video::SColor(0xFFFFFFFF))
1019 for (video::S3DVertex &vertex : pmb.vertices) {
1020 video::SColor *c = &vertex.Color;
1021 c->set(c->getAlpha(),
1022 c->getRed() * tc.getRed() / 255,
1023 c->getGreen() * tc.getGreen() / 255,
1024 c->getBlue() * tc.getBlue() / 255);
1032 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1033 m_minimap_mapblock(NULL),
1034 m_tsrc(data->m_client->getTextureSource()),
1035 m_shdrsrc(data->m_client->getShaderSource()),
1036 m_animation_force_timer(0), // force initial animation
1038 m_last_daynight_ratio((u32) -1)
1040 for (auto &m : m_mesh)
1041 m = new scene::SMesh();
1042 m_enable_shaders = data->m_use_shaders;
1043 m_use_tangent_vertices = data->m_use_tangent_vertices;
1044 m_enable_vbo = g_settings->getBool("enable_vbo");
1046 if (g_settings->getBool("enable_minimap")) {
1047 m_minimap_mapblock = new MinimapMapblock;
1048 m_minimap_mapblock->getMinimapNodes(
1049 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1052 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1053 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1054 //TimeTaker timer1("MapBlockMesh()");
1056 std::vector<FastFace> fastfaces_new;
1057 fastfaces_new.reserve(512);
1060 We are including the faces of the trailing edges of the block.
1061 This means that when something changes, the caller must
1062 also update the meshes of the blocks at the leading edges.
1064 NOTE: This is the slowest part of this method.
1067 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1068 //TimeTaker timer2("updateAllFastFaceRows()");
1069 updateAllFastFaceRows(data, fastfaces_new);
1074 Convert FastFaces to MeshCollector
1077 MeshCollector collector;
1080 // avg 0ms (100ms spikes when loading textures the first time)
1081 // (NOTE: probably outdated)
1082 //TimeTaker timer2("MeshCollector building");
1084 for (const FastFace &f : fastfaces_new) {
1085 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1086 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1087 const u16 *indices_p =
1088 f.vertex_0_2_connected ? indices : indices_alternate;
1089 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1094 Add special graphics:
1102 MapblockMeshGenerator generator(data, &collector);
1103 generator.generate();
1107 Convert MeshCollector to SMesh
1110 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1111 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1113 PreMeshBuffer &p = collector.prebuffers[layer][i];
1117 // Generate animation data
1119 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1120 // Find the texture name plus ^[crack:N:
1121 std::ostringstream os(std::ios::binary);
1122 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1123 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1124 os << "o"; // use ^[cracko
1125 u8 tiles = p.layer.scale;
1127 os << ":" << (u32)tiles;
1128 os << ":" << (u32)p.layer.animation_frame_count << ":";
1129 m_crack_materials.insert(std::make_pair(
1130 std::pair<u8, u32>(layer, i), os.str()));
1131 // Replace tile texture with the cracked one
1132 p.layer.texture = m_tsrc->getTextureForMesh(
1134 &p.layer.texture_id);
1136 // - Texture animation
1137 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1138 // Add to MapBlockMesh in order to animate these tiles
1139 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1140 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1141 if (g_settings->getBool(
1142 "desynchronize_mapblock_texture_animation")) {
1143 // Get starting position from noise
1144 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1145 100000 * (2.0 + noise3d(
1146 data->m_blockpos.X, data->m_blockpos.Y,
1147 data->m_blockpos.Z, 0));
1149 // Play all synchronized
1150 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1152 // Replace tile texture with the first animation frame
1153 p.layer.texture = (*p.layer.frames)[0].texture;
1156 if (!m_enable_shaders) {
1157 // Extract colors for day-night animation
1158 // Dummy sunlight to handle non-sunlit areas
1159 video::SColorf sunlight;
1160 get_sunlight_color(&sunlight, 0);
1161 u32 vertex_count = p.vertices.size();
1162 for (u32 j = 0; j < vertex_count; j++) {
1163 video::SColor *vc = &p.vertices[j].Color;
1164 video::SColor copy = *vc;
1165 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1166 final_color_blend(vc, copy, sunlight); // Finalize color
1167 else // Record color to animate
1168 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1170 // The sunlight ratio has been stored,
1171 // delete alpha (for the final rendering).
1177 video::SMaterial material;
1178 material.setFlag(video::EMF_LIGHTING, false);
1179 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1180 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1181 material.setFlag(video::EMF_FOG_ENABLE, true);
1182 material.setTexture(0, p.layer.texture);
1184 if (m_enable_shaders) {
1185 material.MaterialType = m_shdrsrc->getShaderInfo(
1186 p.layer.shader_id).material;
1187 p.layer.applyMaterialOptionsWithShaders(material);
1188 if (p.layer.normal_texture)
1189 material.setTexture(1, p.layer.normal_texture);
1190 material.setTexture(2, p.layer.flags_texture);
1192 p.layer.applyMaterialOptions(material);
1195 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1197 // Create meshbuffer, add to mesh
1198 if (m_use_tangent_vertices) {
1199 scene::SMeshBufferTangents *buf =
1200 new scene::SMeshBufferTangents();
1201 buf->Material = material;
1202 buf->Vertices.reallocate(p.vertices.size());
1203 buf->Indices.reallocate(p.indices.size());
1204 for (const video::S3DVertex &v: p.vertices)
1205 buf->Vertices.push_back(video::S3DVertexTangents(v.Pos, v.Color, v.TCoords));
1206 for (u16 i: p.indices)
1207 buf->Indices.push_back(i);
1208 buf->recalculateBoundingBox();
1209 mesh->addMeshBuffer(buf);
1212 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1213 buf->Material = material;
1214 buf->append(&p.vertices[0], p.vertices.size(),
1215 &p.indices[0], p.indices.size());
1216 mesh->addMeshBuffer(buf);
1222 Do some stuff to the mesh
1224 m_camera_offset = camera_offset;
1225 translateMesh(m_mesh[layer],
1226 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1228 if (m_use_tangent_vertices) {
1229 scene::IMeshManipulator* meshmanip =
1230 RenderingEngine::get_scene_manager()->getMeshManipulator();
1231 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1234 if (m_mesh[layer]) {
1236 // Usually 1-700 faces and 1-7 materials
1237 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1238 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1239 << " materials (meshbuffers)" << std::endl;
1242 // Use VBO for mesh (this just would set this for ever buffer)
1244 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1248 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1250 // Check if animation is required for this mesh
1252 !m_crack_materials.empty() ||
1253 !m_daynight_diffs.empty() ||
1254 !m_animation_tiles.empty();
1257 MapBlockMesh::~MapBlockMesh()
1259 for (scene::IMesh *m : m_mesh) {
1260 if (m_enable_vbo && m)
1261 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1262 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1263 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1268 delete m_minimap_mapblock;
1271 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1274 if (!m_has_animation) {
1275 m_animation_force_timer = 100000;
1279 m_animation_force_timer = myrand_range(5, 100);
1282 if (crack != m_last_crack) {
1283 for (auto &crack_material : m_crack_materials) {
1284 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1285 getMeshBuffer(crack_material.first.second);
1286 std::string basename = crack_material.second;
1288 // Create new texture name from original
1289 std::ostringstream os;
1290 os << basename << crack;
1291 u32 new_texture_id = 0;
1292 video::ITexture *new_texture =
1293 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1294 buf->getMaterial().setTexture(0, new_texture);
1296 // If the current material is also animated,
1297 // update animation info
1298 auto anim_iter = m_animation_tiles.find(crack_material.first);
1299 if (anim_iter != m_animation_tiles.end()) {
1300 TileLayer &tile = anim_iter->second;
1301 tile.texture = new_texture;
1302 tile.texture_id = new_texture_id;
1303 // force animation update
1304 m_animation_frames[crack_material.first] = -1;
1308 m_last_crack = crack;
1311 // Texture animation
1312 for (auto &animation_tile : m_animation_tiles) {
1313 const TileLayer &tile = animation_tile.second;
1314 // Figure out current frame
1315 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1316 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1317 + frameoffset) % tile.animation_frame_count;
1318 // If frame doesn't change, skip
1319 if (frame == m_animation_frames[animation_tile.first])
1322 m_animation_frames[animation_tile.first] = frame;
1324 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1325 getMeshBuffer(animation_tile.first.second);
1327 const FrameSpec &animation_frame = (*tile.frames)[frame];
1328 buf->getMaterial().setTexture(0, animation_frame.texture);
1329 if (m_enable_shaders) {
1330 if (animation_frame.normal_texture)
1331 buf->getMaterial().setTexture(1,
1332 animation_frame.normal_texture);
1333 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1337 // Day-night transition
1338 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1339 // Force reload mesh to VBO
1341 for (scene::IMesh *m : m_mesh)
1343 video::SColorf day_color;
1344 get_sunlight_color(&day_color, daynight_ratio);
1346 for (auto &daynight_diff : m_daynight_diffs) {
1347 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1348 getMeshBuffer(daynight_diff.first.second);
1349 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1350 for (const auto &j : daynight_diff.second)
1351 final_color_blend(&(vertices[j.first].Color), j.second,
1354 m_last_daynight_ratio = daynight_ratio;
1360 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1362 if (camera_offset != m_camera_offset) {
1363 for (scene::IMesh *layer : m_mesh) {
1364 translateMesh(layer,
1365 intToFloat(m_camera_offset - camera_offset, BS));
1369 m_camera_offset = camera_offset;
1373 video::SColor encode_light(u16 light, u8 emissive_light)
1376 u32 day = (light & 0xff);
1377 u32 night = (light >> 8);
1378 // Add emissive light
1379 night += emissive_light * 2.5f;
1382 // Since we don't know if the day light is sunlight or
1383 // artificial light, assume it is artificial when the night
1384 // light bank is also lit.
1389 u32 sum = day + night;
1390 // Ratio of sunlight:
1393 r = day * 255 / sum;
1397 float b = (day + night) / 2;
1398 return video::SColor(r, b, b, b);