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/renderingengine.h"
37 MeshMakeData::MeshMakeData(Client *client, bool use_shaders,
38 bool use_tangent_vertices):
40 m_use_shaders(use_shaders),
41 m_use_tangent_vertices(use_tangent_vertices)
44 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
46 m_blockpos = blockpos;
48 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
51 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
52 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
53 m_vmanip.addArea(voxel_area);
56 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
58 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
59 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
61 v3s16 bp = m_blockpos + block_offset;
62 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
63 m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
66 void MeshMakeData::fill(MapBlock *block)
68 fillBlockDataBegin(block->getPos());
70 fillBlockData(v3s16(0,0,0), block->getData());
72 // Get map for reading neighbor blocks
73 Map *map = block->getParent();
75 for (const v3s16 &dir : g_26dirs) {
76 v3s16 bp = m_blockpos + dir;
77 MapBlock *b = map->getBlockNoCreateNoEx(bp);
79 fillBlockData(dir, b->getData());
83 void MeshMakeData::fillSingleNode(MapNode *node)
85 m_blockpos = v3s16(0,0,0);
87 v3s16 blockpos_nodes = v3s16(0,0,0);
88 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
89 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
90 s32 volume = area.getVolume();
91 s32 our_node_index = area.index(1,1,1);
93 // Allocate this block + neighbors
95 m_vmanip.addArea(area);
98 MapNode *data = new MapNode[volume];
99 for(s32 i = 0; i < volume; i++)
101 if (i == our_node_index)
104 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
106 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
110 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
112 if (crack_level >= 0)
113 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
116 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
118 m_smooth_lighting = smooth_lighting;
122 Light and vertex color functions
126 Calculate non-smooth lighting at interior of node.
129 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
130 const NodeDefManager *ndef)
132 u8 light = n.getLight(bank, ndef);
136 light = undiminish_light(light);
141 light = diminish_light(light);
145 return decode_light(light);
149 Calculate non-smooth lighting at interior of node.
152 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
154 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
155 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
156 return day | (night << 8);
160 Calculate non-smooth lighting at face of node.
163 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
164 v3s16 face_dir, const NodeDefManager *ndef)
167 u8 l1 = n.getLight(bank, ndef);
168 u8 l2 = n2.getLight(bank, ndef);
174 // Boost light level for light sources
175 u8 light_source = MYMAX(ndef->get(n).light_source,
176 ndef->get(n2).light_source);
177 if(light_source > light)
178 light = light_source;
180 return decode_light(light);
184 Calculate non-smooth lighting at face of node.
187 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir,
188 const NodeDefManager *ndef)
190 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
191 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
192 return day | (night << 8);
196 Calculate smooth lighting at the XYZ- corner of p.
199 static u16 getSmoothLightCombined(const v3s16 &p,
200 const std::array<v3s16,8> &dirs, MeshMakeData *data)
202 const NodeDefManager *ndef = data->m_client->ndef();
204 u16 ambient_occlusion = 0;
206 u8 light_source_max = 0;
210 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
215 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
216 if (n.getContent() == CONTENT_IGNORE)
218 const ContentFeatures &f = ndef->get(n);
219 if (f.light_source > light_source_max)
220 light_source_max = f.light_source;
221 // Check f.solidness because fast-style leaves look better this way
222 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
223 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
224 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
229 return f.light_propagates;
232 std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
234 bool opaque1 = !add_node(1);
235 bool opaque2 = !add_node(2);
236 bool opaque3 = !add_node(3);
237 obstructed[0] = opaque1 && opaque2;
238 obstructed[1] = opaque1 && opaque3;
239 obstructed[2] = opaque2 && opaque3;
240 for (u8 k = 0; k < 3; ++k)
241 if (add_node(k + 4, obstructed[k]))
242 obstructed[3] = false;
243 if (add_node(7, obstructed[3])) { // wrap light around nodes
244 ambient_occlusion -= 3;
245 for (u8 k = 0; k < 3; ++k)
246 add_node(k + 4, !obstructed[k]);
249 if (light_count == 0) {
250 light_day = light_night = 0;
252 light_day /= light_count;
253 light_night /= light_count;
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(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, 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;
466 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
467 v3f tp, v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
469 // Position is at the center of the cube.
478 v3s16 vertex_dirs[4];
479 getNodeVertexDirs(dir, vertex_dirs);
480 if (tile.world_aligned)
481 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
485 switch (tile.rotation) {
490 vertex_dirs[0] = vertex_dirs[3];
491 vertex_dirs[3] = vertex_dirs[2];
492 vertex_dirs[2] = vertex_dirs[1];
502 vertex_dirs[0] = vertex_dirs[2];
505 vertex_dirs[1] = vertex_dirs[3];
516 vertex_dirs[0] = vertex_dirs[1];
517 vertex_dirs[1] = vertex_dirs[2];
518 vertex_dirs[2] = vertex_dirs[3];
528 vertex_dirs[0] = vertex_dirs[3];
529 vertex_dirs[3] = vertex_dirs[2];
530 vertex_dirs[2] = vertex_dirs[1];
542 vertex_dirs[0] = vertex_dirs[1];
543 vertex_dirs[1] = vertex_dirs[2];
544 vertex_dirs[2] = vertex_dirs[3];
556 vertex_dirs[0] = vertex_dirs[3];
557 vertex_dirs[3] = vertex_dirs[2];
558 vertex_dirs[2] = vertex_dirs[1];
570 vertex_dirs[0] = vertex_dirs[1];
571 vertex_dirs[1] = vertex_dirs[2];
572 vertex_dirs[2] = vertex_dirs[3];
594 for (u16 i = 0; i < 4; i++) {
596 BS / 2 * vertex_dirs[i].X,
597 BS / 2 * vertex_dirs[i].Y,
598 BS / 2 * vertex_dirs[i].Z
602 for (v3f &vpos : vertex_pos) {
609 f32 abs_scale = 1.0f;
610 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
611 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
612 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
614 v3f normal(dir.X, dir.Y, dir.Z);
616 u16 li[4] = { li0, li1, li2, li3 };
620 for (u8 i = 0; i < 4; i++) {
622 night[i] = li[i] & 0xFF;
625 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
626 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
629 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
630 core::vector2d<f32>(x0, y0 + h),
631 core::vector2d<f32>(x0, y0),
632 core::vector2d<f32>(x0 + w * abs_scale, y0) };
634 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
635 const TileLayer *layer = &tile.layers[layernum];
636 if (layer->texture_id == 0)
639 // equivalent to dest.push_back(FastFace()) but faster
641 FastFace& face = *dest.rbegin();
643 for (u8 i = 0; i < 4; i++) {
644 video::SColor c = encode_light(li[i], tile.emissive_light);
645 if (!tile.emissive_light)
646 applyFacesShading(c, normal);
648 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
652 Revert triangles for nicer looking gradient if the
653 brightness of vertices 1 and 3 differ less than
654 the brightness of vertices 0 and 2.
656 face.vertex_0_2_connected = vertex_0_2_connected;
659 face.layernum = layernum;
661 face.world_aligned = tile.world_aligned;
666 Nodes make a face if contents differ and solidness differs.
669 1: Face uses m1's content
670 2: Face uses m2's content
671 equivalent: Whether the blocks share the same face (eg. water and glass)
673 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
675 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
676 const NodeDefManager *ndef)
680 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
683 const ContentFeatures &f1 = ndef->get(m1);
684 const ContentFeatures &f2 = ndef->get(m2);
686 // Contents don't differ for different forms of same liquid
687 if (f1.sameLiquid(f2))
690 u8 c1 = f1.solidness;
691 u8 c2 = f2.solidness;
697 c1 = f1.visual_solidness;
699 c2 = f2.visual_solidness;
703 // If same solidness, liquid takes precense
717 Gets nth node tile (0 <= n <= 5).
719 void getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
721 const NodeDefManager *ndef = data->m_client->ndef();
722 const ContentFeatures &f = ndef->get(mn);
723 tile = f.tiles[tileindex];
724 bool has_crack = p == data->m_crack_pos_relative;
725 for (TileLayer &layer : tile.layers) {
726 if (layer.texture_id == 0)
728 if (!layer.has_color)
729 mn.getColor(f, &(layer.color));
730 // Apply temporary crack
732 layer.material_flags |= MATERIAL_FLAG_CRACK;
737 Gets node tile given a face direction.
739 void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &tile)
741 const NodeDefManager *ndef = data->m_client->ndef();
743 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
744 // (0,0,1), (0,0,-1) or (0,0,0)
745 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
747 // Convert direction to single integer for table lookup
752 // 4 = invalid, treat as (0,0,0)
756 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
758 // Get rotation for things like chests
759 u8 facedir = mn.getFaceDir(ndef);
761 static const u16 dir_to_tile[24 * 16] =
763 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
764 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
765 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
766 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
767 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
769 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
770 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
771 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
772 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
774 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
775 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
776 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
777 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
779 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
780 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
781 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
782 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
784 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
785 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
786 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
787 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
789 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
790 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
791 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
792 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
795 u16 tile_index = facedir * 16 + dir_i;
796 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
797 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
800 static void getTileInfo(
804 const v3s16 &face_dir,
808 v3s16 &face_dir_corrected,
813 VoxelManipulator &vmanip = data->m_vmanip;
814 const NodeDefManager *ndef = data->m_client->ndef();
815 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
817 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
819 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
820 if (n0.getContent() == CONTENT_IGNORE) {
825 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
827 if (n1.getContent() == CONTENT_IGNORE) {
833 bool equivalent = false;
834 u8 mf = face_contents(n0.getContent(), n1.getContent(),
848 face_dir_corrected = face_dir;
851 p_corrected = p + face_dir;
852 face_dir_corrected = -face_dir;
855 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
856 const ContentFeatures &f = ndef->get(n);
857 tile.emissive_light = f.light_source;
859 // eg. water and glass
861 for (TileLayer &layer : tile.layers)
862 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
865 if (!data->m_smooth_lighting) {
866 lights[0] = lights[1] = lights[2] = lights[3] =
867 getFaceLight(n0, n1, face_dir, ndef);
869 v3s16 vertex_dirs[4];
870 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
872 v3s16 light_p = blockpos_nodes + p_corrected;
873 for (u16 i = 0; i < 4; i++)
874 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
880 translate_dir: unit vector with only one of x, y or z
881 face_dir: unit vector with only one of x, y or z
883 static void updateFastFaceRow(
885 const v3s16 &&startpos,
887 const v3f &&translate_dir_f,
888 const v3s16 &&face_dir,
889 std::vector<FastFace> &dest)
893 u16 continuous_tiles_count = 1;
895 bool makes_face = false;
897 v3s16 face_dir_corrected;
898 u16 lights[4] = {0, 0, 0, 0};
900 getTileInfo(data, p, face_dir,
901 makes_face, p_corrected, face_dir_corrected,
904 // Unroll this variable which has a significant build cost
906 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
907 // If tiling can be done, this is set to false in the next step
908 bool next_is_different = true;
912 bool next_makes_face = false;
913 v3s16 next_p_corrected;
914 v3s16 next_face_dir_corrected;
915 u16 next_lights[4] = {0, 0, 0, 0};
917 // If at last position, there is nothing to compare to and
918 // the face must be drawn anyway
919 if (j != MAP_BLOCKSIZE - 1) {
920 p_next = p + translate_dir;
922 getTileInfo(data, p_next, face_dir,
923 next_makes_face, next_p_corrected,
924 next_face_dir_corrected, next_lights,
927 if (next_makes_face == makes_face
928 && next_p_corrected == p_corrected + translate_dir
929 && next_face_dir_corrected == face_dir_corrected
930 && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
931 && next_tile.isTileable(tile)) {
932 next_is_different = false;
933 continuous_tiles_count++;
936 if (next_is_different) {
938 Create a face if there should be one
941 // Floating point conversion of the position vector
942 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
943 // Center point of face (kind of)
944 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
948 if (translate_dir.X != 0)
949 scale.X = continuous_tiles_count;
950 if (translate_dir.Y != 0)
951 scale.Y = continuous_tiles_count;
952 if (translate_dir.Z != 0)
953 scale.Z = continuous_tiles_count;
955 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
956 pf, sp, face_dir_corrected, scale, dest);
958 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
959 for (int i = 1; i < continuous_tiles_count; i++)
960 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
963 continuous_tiles_count = 1;
966 makes_face = next_makes_face;
967 p_corrected = next_p_corrected;
968 face_dir_corrected = next_face_dir_corrected;
969 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
970 if (next_is_different)
976 static void updateAllFastFaceRows(MeshMakeData *data,
977 std::vector<FastFace> &dest)
980 Go through every y,z and get top(y+) faces in rows of x+
982 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
983 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
984 updateFastFaceRow(data,
986 v3s16(1, 0, 0), //dir
988 v3s16(0, 1, 0), //face dir
992 Go through every x,y and get right(x+) faces in rows of z+
994 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
995 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
996 updateFastFaceRow(data,
998 v3s16(0, 0, 1), //dir
1000 v3s16(1, 0, 0), //face dir
1004 Go through every y,z and get back(z+) faces in rows of x+
1006 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1007 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1008 updateFastFaceRow(data,
1010 v3s16(1, 0, 0), //dir
1012 v3s16(0, 0, 1), //face dir
1020 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1021 m_minimap_mapblock(NULL),
1022 m_tsrc(data->m_client->getTextureSource()),
1023 m_shdrsrc(data->m_client->getShaderSource()),
1024 m_animation_force_timer(0), // force initial animation
1026 m_last_daynight_ratio((u32) -1)
1028 for (auto &m : m_mesh)
1029 m = new scene::SMesh();
1030 m_enable_shaders = data->m_use_shaders;
1031 m_use_tangent_vertices = data->m_use_tangent_vertices;
1032 m_enable_vbo = g_settings->getBool("enable_vbo");
1034 if (g_settings->getBool("enable_minimap")) {
1035 m_minimap_mapblock = new MinimapMapblock;
1036 m_minimap_mapblock->getMinimapNodes(
1037 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1040 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1041 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1042 //TimeTaker timer1("MapBlockMesh()");
1044 std::vector<FastFace> fastfaces_new;
1045 fastfaces_new.reserve(512);
1048 We are including the faces of the trailing edges of the block.
1049 This means that when something changes, the caller must
1050 also update the meshes of the blocks at the leading edges.
1052 NOTE: This is the slowest part of this method.
1055 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1056 //TimeTaker timer2("updateAllFastFaceRows()");
1057 updateAllFastFaceRows(data, fastfaces_new);
1062 Convert FastFaces to MeshCollector
1065 MeshCollector collector(m_use_tangent_vertices);
1068 // avg 0ms (100ms spikes when loading textures the first time)
1069 // (NOTE: probably outdated)
1070 //TimeTaker timer2("MeshCollector building");
1072 for (const FastFace &f : fastfaces_new) {
1073 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1074 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1076 if (!f.layer.texture)
1079 const u16 *indices_p =
1080 f.vertex_0_2_connected ? indices : indices_alternate;
1082 collector.append(f.layer, f.vertices, 4, indices_p, 6,
1083 f.layernum, f.world_aligned);
1088 Add special graphics:
1096 MapblockMeshGenerator generator(data, &collector);
1097 generator.generate();
1100 collector.applyTileColors();
1103 Convert MeshCollector to SMesh
1106 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1107 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1109 PreMeshBuffer &p = collector.prebuffers[layer][i];
1111 // Generate animation data
1113 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1114 // Find the texture name plus ^[crack:N:
1115 std::ostringstream os(std::ios::binary);
1116 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1117 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1118 os << "o"; // use ^[cracko
1119 u8 tiles = p.layer.scale;
1121 os << ":" << (u32)tiles;
1122 os << ":" << (u32)p.layer.animation_frame_count << ":";
1123 m_crack_materials.insert(std::make_pair(
1124 std::pair<u8, u32>(layer, i), os.str()));
1125 // Replace tile texture with the cracked one
1126 p.layer.texture = m_tsrc->getTextureForMesh(
1128 &p.layer.texture_id);
1130 // - Texture animation
1131 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1132 // Add to MapBlockMesh in order to animate these tiles
1133 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1134 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1135 if (g_settings->getBool(
1136 "desynchronize_mapblock_texture_animation")) {
1137 // Get starting position from noise
1138 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1139 100000 * (2.0 + noise3d(
1140 data->m_blockpos.X, data->m_blockpos.Y,
1141 data->m_blockpos.Z, 0));
1143 // Play all synchronized
1144 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1146 // Replace tile texture with the first animation frame
1147 p.layer.texture = (*p.layer.frames)[0].texture;
1150 if (!m_enable_shaders) {
1151 // Extract colors for day-night animation
1152 // Dummy sunlight to handle non-sunlit areas
1153 video::SColorf sunlight;
1154 get_sunlight_color(&sunlight, 0);
1155 u32 vertex_count = m_use_tangent_vertices ?
1156 p.tangent_vertices.size() : p.vertices.size();
1157 for (u32 j = 0; j < vertex_count; j++) {
1159 if (m_use_tangent_vertices) {
1160 vc = &p.tangent_vertices[j].Color;
1162 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();
1202 buf->Material = material;
1204 mesh->addMeshBuffer(buf);
1207 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1208 &p.indices[0], p.indices.size());
1210 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1212 buf->Material = material;
1214 mesh->addMeshBuffer(buf);
1217 buf->append(&p.vertices[0], p.vertices.size(),
1218 &p.indices[0], p.indices.size());
1223 Do some stuff to the mesh
1225 m_camera_offset = camera_offset;
1226 translateMesh(m_mesh[layer],
1227 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1229 if (m_use_tangent_vertices) {
1230 scene::IMeshManipulator* meshmanip =
1231 RenderingEngine::get_scene_manager()->getMeshManipulator();
1232 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1235 if (m_mesh[layer]) {
1237 // Usually 1-700 faces and 1-7 materials
1238 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1239 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1240 << " materials (meshbuffers)" << std::endl;
1243 // Use VBO for mesh (this just would set this for ever buffer)
1245 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1249 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1251 // Check if animation is required for this mesh
1253 !m_crack_materials.empty() ||
1254 !m_daynight_diffs.empty() ||
1255 !m_animation_tiles.empty();
1258 MapBlockMesh::~MapBlockMesh()
1260 for (scene::IMesh *m : m_mesh) {
1261 if (m_enable_vbo && m)
1262 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1263 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1264 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1269 delete m_minimap_mapblock;
1272 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1275 if (!m_has_animation) {
1276 m_animation_force_timer = 100000;
1280 m_animation_force_timer = myrand_range(5, 100);
1283 if (crack != m_last_crack) {
1284 for (auto &crack_material : m_crack_materials) {
1285 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1286 getMeshBuffer(crack_material.first.second);
1287 std::string basename = crack_material.second;
1289 // Create new texture name from original
1290 std::ostringstream os;
1291 os << basename << crack;
1292 u32 new_texture_id = 0;
1293 video::ITexture *new_texture =
1294 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1295 buf->getMaterial().setTexture(0, new_texture);
1297 // If the current material is also animated,
1298 // update animation info
1299 auto anim_iter = m_animation_tiles.find(crack_material.first);
1300 if (anim_iter != m_animation_tiles.end()) {
1301 TileLayer &tile = anim_iter->second;
1302 tile.texture = new_texture;
1303 tile.texture_id = new_texture_id;
1304 // force animation update
1305 m_animation_frames[crack_material.first] = -1;
1309 m_last_crack = crack;
1312 // Texture animation
1313 for (auto &animation_tile : m_animation_tiles) {
1314 const TileLayer &tile = animation_tile.second;
1315 // Figure out current frame
1316 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1317 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1318 + frameoffset) % tile.animation_frame_count;
1319 // If frame doesn't change, skip
1320 if (frame == m_animation_frames[animation_tile.first])
1323 m_animation_frames[animation_tile.first] = frame;
1325 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1326 getMeshBuffer(animation_tile.first.second);
1328 const FrameSpec &animation_frame = (*tile.frames)[frame];
1329 buf->getMaterial().setTexture(0, animation_frame.texture);
1330 if (m_enable_shaders) {
1331 if (animation_frame.normal_texture)
1332 buf->getMaterial().setTexture(1,
1333 animation_frame.normal_texture);
1334 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1338 // Day-night transition
1339 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1340 // Force reload mesh to VBO
1342 for (scene::IMesh *m : m_mesh)
1344 video::SColorf day_color;
1345 get_sunlight_color(&day_color, daynight_ratio);
1347 for (auto &daynight_diff : m_daynight_diffs) {
1348 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1349 getMeshBuffer(daynight_diff.first.second);
1350 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1351 for (const auto &j : daynight_diff.second)
1352 final_color_blend(&(vertices[j.first].Color), j.second,
1355 m_last_daynight_ratio = daynight_ratio;
1361 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1363 if (camera_offset != m_camera_offset) {
1364 for (scene::IMesh *layer : m_mesh) {
1365 translateMesh(layer,
1366 intToFloat(m_camera_offset - camera_offset, BS));
1370 m_camera_offset = camera_offset;
1378 void MeshCollector::append(const TileSpec &tile,
1379 const video::S3DVertex *vertices, u32 numVertices,
1380 const u16 *indices, u32 numIndices)
1382 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1383 const TileLayer *layer = &tile.layers[layernum];
1384 if (layer->texture_id == 0)
1386 append(*layer, vertices, numVertices, indices, numIndices,
1387 layernum, tile.world_aligned);
1391 void MeshCollector::append(const TileLayer &layer,
1392 const video::S3DVertex *vertices, u32 numVertices,
1393 const u16 *indices, u32 numIndices, u8 layernum,
1396 if (numIndices > 65535) {
1397 dstream << "FIXME: MeshCollector::append() called with numIndices="
1398 << numIndices << " (limit 65535)" << std::endl;
1401 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1403 PreMeshBuffer *p = NULL;
1404 for (PreMeshBuffer &pp : *buffers) {
1405 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1414 buffers->push_back(pp);
1415 p = &(*buffers)[buffers->size() - 1];
1420 scale = 1.0 / layer.scale;
1423 if (m_use_tangent_vertices) {
1424 vertex_count = p->tangent_vertices.size();
1425 for (u32 i = 0; i < numVertices; i++) {
1427 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1428 vertices[i].Color, scale * vertices[i].TCoords);
1429 p->tangent_vertices.push_back(vert);
1432 vertex_count = p->vertices.size();
1433 for (u32 i = 0; i < numVertices; i++) {
1434 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1435 vertices[i].Color, scale * vertices[i].TCoords);
1437 p->vertices.push_back(vert);
1441 for (u32 i = 0; i < numIndices; i++) {
1442 u32 j = indices[i] + vertex_count;
1443 p->indices.push_back(j);
1448 MeshCollector - for meshnodes and converted drawtypes.
1451 void MeshCollector::append(const TileSpec &tile,
1452 const video::S3DVertex *vertices, u32 numVertices,
1453 const u16 *indices, u32 numIndices,
1454 v3f pos, video::SColor c, u8 light_source)
1456 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1457 const TileLayer *layer = &tile.layers[layernum];
1458 if (layer->texture_id == 0)
1460 append(*layer, vertices, numVertices, indices, numIndices, pos,
1461 c, light_source, layernum, tile.world_aligned);
1465 void MeshCollector::append(const TileLayer &layer,
1466 const video::S3DVertex *vertices, u32 numVertices,
1467 const u16 *indices, u32 numIndices,
1468 v3f pos, video::SColor c, u8 light_source, u8 layernum,
1471 if (numIndices > 65535) {
1472 dstream << "FIXME: MeshCollector::append() called with numIndices="
1473 << numIndices << " (limit 65535)" << std::endl;
1476 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1478 PreMeshBuffer *p = NULL;
1479 for (PreMeshBuffer &pp : *buffers) {
1480 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1489 buffers->push_back(pp);
1490 p = &(*buffers)[buffers->size() - 1];
1495 scale = 1.0 / layer.scale;
1497 video::SColor original_c = c;
1499 if (m_use_tangent_vertices) {
1500 vertex_count = p->tangent_vertices.size();
1501 for (u32 i = 0; i < numVertices; i++) {
1502 if (!light_source) {
1504 applyFacesShading(c, vertices[i].Normal);
1506 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1507 vertices[i].Normal, c, scale * vertices[i].TCoords);
1508 p->tangent_vertices.push_back(vert);
1511 vertex_count = p->vertices.size();
1512 for (u32 i = 0; i < numVertices; i++) {
1513 if (!light_source) {
1515 applyFacesShading(c, vertices[i].Normal);
1517 video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
1518 scale * vertices[i].TCoords);
1519 p->vertices.push_back(vert);
1523 for (u32 i = 0; i < numIndices; i++) {
1524 u32 j = indices[i] + vertex_count;
1525 p->indices.push_back(j);
1529 void MeshCollector::applyTileColors()
1531 if (m_use_tangent_vertices)
1532 for (auto &prebuffer : prebuffers) {
1533 for (PreMeshBuffer &pmb : prebuffer) {
1534 video::SColor tc = pmb.layer.color;
1535 if (tc == video::SColor(0xFFFFFFFF))
1537 for (video::S3DVertexTangents &tangent_vertex : pmb.tangent_vertices) {
1538 video::SColor *c = &tangent_vertex.Color;
1539 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1540 c->getGreen() * tc.getGreen() / 255,
1541 c->getBlue() * tc.getBlue() / 255);
1546 for (auto &prebuffer : prebuffers) {
1547 for (PreMeshBuffer &pmb : prebuffer) {
1548 video::SColor tc = pmb.layer.color;
1549 if (tc == video::SColor(0xFFFFFFFF))
1551 for (video::S3DVertex &vertex : pmb.vertices) {
1552 video::SColor *c = &vertex.Color;
1553 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1554 c->getGreen() * tc.getGreen() / 255,
1555 c->getBlue() * tc.getBlue() / 255);
1561 video::SColor encode_light(u16 light, u8 emissive_light)
1564 u32 day = (light & 0xff);
1565 u32 night = (light >> 8);
1566 // Add emissive light
1567 night += emissive_light * 2.5f;
1570 // Since we don't know if the day light is sunlight or
1571 // artificial light, assume it is artificial when the night
1572 // light bank is also lit.
1577 u32 sum = day + night;
1578 // Ratio of sunlight:
1581 r = day * 255 / sum;
1585 float b = (day + night) / 2;
1586 return video::SColor(r, b, b, b);