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);
134 light = rangelim(light + increment, 0, LIGHT_SUN);
135 return decode_light(light);
139 Calculate non-smooth lighting at interior of node.
142 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
144 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
145 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
146 return day | (night << 8);
150 Calculate non-smooth lighting at face of node.
153 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
154 v3s16 face_dir, const NodeDefManager *ndef)
157 u8 l1 = n.getLight(bank, ndef);
158 u8 l2 = n2.getLight(bank, ndef);
164 // Boost light level for light sources
165 u8 light_source = MYMAX(ndef->get(n).light_source,
166 ndef->get(n2).light_source);
167 if(light_source > light)
168 light = light_source;
170 return decode_light(light);
174 Calculate non-smooth lighting at face of node.
177 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir,
178 const NodeDefManager *ndef)
180 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
181 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
182 return day | (night << 8);
186 Calculate smooth lighting at the XYZ- corner of p.
189 static u16 getSmoothLightCombined(const v3s16 &p,
190 const std::array<v3s16,8> &dirs, MeshMakeData *data)
192 const NodeDefManager *ndef = data->m_client->ndef();
194 u16 ambient_occlusion = 0;
196 u8 light_source_max = 0;
200 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
205 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
206 if (n.getContent() == CONTENT_IGNORE)
208 const ContentFeatures &f = ndef->get(n);
209 if (f.light_source > light_source_max)
210 light_source_max = f.light_source;
211 // Check f.solidness because fast-style leaves look better this way
212 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
213 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
214 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
219 return f.light_propagates;
222 std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
224 bool opaque1 = !add_node(1);
225 bool opaque2 = !add_node(2);
226 bool opaque3 = !add_node(3);
227 obstructed[0] = opaque1 && opaque2;
228 obstructed[1] = opaque1 && opaque3;
229 obstructed[2] = opaque2 && opaque3;
230 for (u8 k = 0; k < 3; ++k)
231 if (add_node(k + 4, obstructed[k]))
232 obstructed[3] = false;
233 if (add_node(7, obstructed[3])) { // wrap light around nodes
234 ambient_occlusion -= 3;
235 for (u8 k = 0; k < 3; ++k)
236 add_node(k + 4, !obstructed[k]);
239 if (light_count == 0) {
240 light_day = light_night = 0;
242 light_day /= light_count;
243 light_night /= light_count;
246 // Boost brightness around light sources
247 bool skip_ambient_occlusion_day = false;
248 if (decode_light(light_source_max) >= light_day) {
249 light_day = decode_light(light_source_max);
250 skip_ambient_occlusion_day = true;
253 bool skip_ambient_occlusion_night = false;
254 if(decode_light(light_source_max) >= light_night) {
255 light_night = decode_light(light_source_max);
256 skip_ambient_occlusion_night = true;
259 if (ambient_occlusion > 4) {
260 static thread_local const float ao_gamma = rangelim(
261 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
263 // Table of gamma space multiply factors.
264 static thread_local const float light_amount[3] = {
265 powf(0.75, 1.0 / ao_gamma),
266 powf(0.5, 1.0 / ao_gamma),
267 powf(0.25, 1.0 / ao_gamma)
270 //calculate table index for gamma space multiplier
271 ambient_occlusion -= 5;
273 if (!skip_ambient_occlusion_day)
274 light_day = rangelim(core::round32(
275 light_day * light_amount[ambient_occlusion]), 0, 255);
276 if (!skip_ambient_occlusion_night)
277 light_night = rangelim(core::round32(
278 light_night * light_amount[ambient_occlusion]), 0, 255);
281 return light_day | (light_night << 8);
285 Calculate smooth lighting at the given corner of p.
287 Node at p is solid, and thus the lighting is face-dependent.
289 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
291 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
295 Calculate smooth lighting at the given corner of p.
297 Node at p is not solid, and the lighting is not face-dependent.
299 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
301 const std::array<v3s16,8> dirs = {{
302 // Always shine light
309 v3s16(corner.X,corner.Y,0),
310 v3s16(corner.X,0,corner.Z),
311 v3s16(0,corner.Y,corner.Z),
312 v3s16(corner.X,corner.Y,corner.Z)
314 return getSmoothLightCombined(p, dirs, data);
317 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
318 f32 rg = daynight_ratio / 1000.0f - 0.04f;
319 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
325 void final_color_blend(video::SColor *result,
326 u16 light, u32 daynight_ratio)
328 video::SColorf dayLight;
329 get_sunlight_color(&dayLight, daynight_ratio);
330 final_color_blend(result,
331 encode_light(light, 0), dayLight);
334 void final_color_blend(video::SColor *result,
335 const video::SColor &data, const video::SColorf &dayLight)
337 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
339 video::SColorf c(data);
342 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
343 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
344 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
346 // Emphase blue a bit in darker places
347 // Each entry of this array represents a range of 8 blue levels
348 static const u8 emphase_blue_when_dark[32] = {
349 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
350 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
353 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
354 0, 255) / 8] / 255.0f;
356 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
357 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
358 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
362 Mesh generation helpers
366 vertex_dirs: v3s16[4]
368 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
371 If looked from outside the node towards the face, the corners are:
377 if (dir == v3s16(0, 0, 1)) {
378 // If looking towards z+, this is the face that is behind
379 // the center point, facing towards z+.
380 vertex_dirs[0] = v3s16(-1,-1, 1);
381 vertex_dirs[1] = v3s16( 1,-1, 1);
382 vertex_dirs[2] = v3s16( 1, 1, 1);
383 vertex_dirs[3] = v3s16(-1, 1, 1);
384 } else if (dir == v3s16(0, 0, -1)) {
386 vertex_dirs[0] = v3s16( 1,-1,-1);
387 vertex_dirs[1] = v3s16(-1,-1,-1);
388 vertex_dirs[2] = v3s16(-1, 1,-1);
389 vertex_dirs[3] = v3s16( 1, 1,-1);
390 } else if (dir == v3s16(1, 0, 0)) {
392 vertex_dirs[0] = v3s16( 1,-1, 1);
393 vertex_dirs[1] = v3s16( 1,-1,-1);
394 vertex_dirs[2] = v3s16( 1, 1,-1);
395 vertex_dirs[3] = v3s16( 1, 1, 1);
396 } else if (dir == v3s16(-1, 0, 0)) {
398 vertex_dirs[0] = v3s16(-1,-1,-1);
399 vertex_dirs[1] = v3s16(-1,-1, 1);
400 vertex_dirs[2] = v3s16(-1, 1, 1);
401 vertex_dirs[3] = v3s16(-1, 1,-1);
402 } else if (dir == v3s16(0, 1, 0)) {
403 // faces towards Y+ (assume Z- as "down" in texture)
404 vertex_dirs[0] = v3s16( 1, 1,-1);
405 vertex_dirs[1] = v3s16(-1, 1,-1);
406 vertex_dirs[2] = v3s16(-1, 1, 1);
407 vertex_dirs[3] = v3s16( 1, 1, 1);
408 } else if (dir == v3s16(0, -1, 0)) {
409 // faces towards Y- (assume Z+ as "down" in texture)
410 vertex_dirs[0] = v3s16( 1,-1, 1);
411 vertex_dirs[1] = v3s16(-1,-1, 1);
412 vertex_dirs[2] = v3s16(-1,-1,-1);
413 vertex_dirs[3] = v3s16( 1,-1,-1);
417 static void getNodeTextureCoords(v3f base, const v3f &scale, v3s16 dir, float *u, float *v)
419 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
421 if (dir == v3s16(0,0,1)) {
424 } else if (dir == v3s16(0,0,-1)) {
427 } else if (dir == v3s16(1,0,0)) {
430 } else if (dir == v3s16(-1,0,0)) {
433 } else if (dir == v3s16(0,1,0)) {
436 } else if (dir == v3s16(0,-1,0)) {
445 video::S3DVertex vertices[4]; // Precalculated vertices
447 * The face is divided into two triangles. If this is true,
448 * vertices 0 and 2 are connected, othervise vertices 1 and 3
451 bool vertex_0_2_connected;
456 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
457 v3f tp, v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
459 // Position is at the center of the cube.
468 v3s16 vertex_dirs[4];
469 getNodeVertexDirs(dir, vertex_dirs);
470 if (tile.world_aligned)
471 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
475 switch (tile.rotation) {
480 vertex_dirs[0] = vertex_dirs[3];
481 vertex_dirs[3] = vertex_dirs[2];
482 vertex_dirs[2] = vertex_dirs[1];
492 vertex_dirs[0] = vertex_dirs[2];
495 vertex_dirs[1] = vertex_dirs[3];
506 vertex_dirs[0] = vertex_dirs[1];
507 vertex_dirs[1] = vertex_dirs[2];
508 vertex_dirs[2] = vertex_dirs[3];
518 vertex_dirs[0] = vertex_dirs[3];
519 vertex_dirs[3] = vertex_dirs[2];
520 vertex_dirs[2] = vertex_dirs[1];
532 vertex_dirs[0] = vertex_dirs[1];
533 vertex_dirs[1] = vertex_dirs[2];
534 vertex_dirs[2] = vertex_dirs[3];
546 vertex_dirs[0] = vertex_dirs[3];
547 vertex_dirs[3] = vertex_dirs[2];
548 vertex_dirs[2] = vertex_dirs[1];
560 vertex_dirs[0] = vertex_dirs[1];
561 vertex_dirs[1] = vertex_dirs[2];
562 vertex_dirs[2] = vertex_dirs[3];
584 for (u16 i = 0; i < 4; i++) {
586 BS / 2 * vertex_dirs[i].X,
587 BS / 2 * vertex_dirs[i].Y,
588 BS / 2 * vertex_dirs[i].Z
592 for (v3f &vpos : vertex_pos) {
599 f32 abs_scale = 1.0f;
600 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
601 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
602 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
604 v3f normal(dir.X, dir.Y, dir.Z);
606 u16 li[4] = { li0, li1, li2, li3 };
610 for (u8 i = 0; i < 4; i++) {
612 night[i] = li[i] & 0xFF;
615 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
616 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
619 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
620 core::vector2d<f32>(x0, y0 + h),
621 core::vector2d<f32>(x0, y0),
622 core::vector2d<f32>(x0 + w * abs_scale, y0) };
624 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
625 const TileLayer *layer = &tile.layers[layernum];
626 if (layer->texture_id == 0)
629 // equivalent to dest.push_back(FastFace()) but faster
631 FastFace& face = *dest.rbegin();
633 for (u8 i = 0; i < 4; i++) {
634 video::SColor c = encode_light(li[i], tile.emissive_light);
635 if (!tile.emissive_light)
636 applyFacesShading(c, normal);
638 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
642 Revert triangles for nicer looking gradient if the
643 brightness of vertices 1 and 3 differ less than
644 the brightness of vertices 0 and 2.
646 face.vertex_0_2_connected = vertex_0_2_connected;
649 face.layernum = layernum;
651 face.world_aligned = tile.world_aligned;
656 Nodes make a face if contents differ and solidness differs.
659 1: Face uses m1's content
660 2: Face uses m2's content
661 equivalent: Whether the blocks share the same face (eg. water and glass)
663 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
665 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
666 const NodeDefManager *ndef)
670 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
673 const ContentFeatures &f1 = ndef->get(m1);
674 const ContentFeatures &f2 = ndef->get(m2);
676 // Contents don't differ for different forms of same liquid
677 if (f1.sameLiquid(f2))
680 u8 c1 = f1.solidness;
681 u8 c2 = f2.solidness;
687 c1 = f1.visual_solidness;
689 c2 = f2.visual_solidness;
693 // If same solidness, liquid takes precense
707 Gets nth node tile (0 <= n <= 5).
709 void getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
711 const NodeDefManager *ndef = data->m_client->ndef();
712 const ContentFeatures &f = ndef->get(mn);
713 tile = f.tiles[tileindex];
714 bool has_crack = p == data->m_crack_pos_relative;
715 for (TileLayer &layer : tile.layers) {
716 if (layer.texture_id == 0)
718 if (!layer.has_color)
719 mn.getColor(f, &(layer.color));
720 // Apply temporary crack
722 layer.material_flags |= MATERIAL_FLAG_CRACK;
727 Gets node tile given a face direction.
729 void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &tile)
731 const NodeDefManager *ndef = data->m_client->ndef();
733 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
734 // (0,0,1), (0,0,-1) or (0,0,0)
735 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
737 // Convert direction to single integer for table lookup
742 // 4 = invalid, treat as (0,0,0)
746 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
748 // Get rotation for things like chests
749 u8 facedir = mn.getFaceDir(ndef);
751 static const u16 dir_to_tile[24 * 16] =
753 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
754 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
755 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
756 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
757 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
759 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
760 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
761 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
762 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
764 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
765 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
766 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
767 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
769 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
770 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
771 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
772 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
774 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
775 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
776 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
777 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
779 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
780 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
781 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
782 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
785 u16 tile_index = facedir * 16 + dir_i;
786 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
787 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
790 static void getTileInfo(
794 const v3s16 &face_dir,
798 v3s16 &face_dir_corrected,
803 VoxelManipulator &vmanip = data->m_vmanip;
804 const NodeDefManager *ndef = data->m_client->ndef();
805 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
807 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
809 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
810 if (n0.getContent() == CONTENT_IGNORE) {
815 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
817 if (n1.getContent() == CONTENT_IGNORE) {
823 bool equivalent = false;
824 u8 mf = face_contents(n0.getContent(), n1.getContent(),
838 face_dir_corrected = face_dir;
841 p_corrected = p + face_dir;
842 face_dir_corrected = -face_dir;
845 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
846 const ContentFeatures &f = ndef->get(n);
847 tile.emissive_light = f.light_source;
849 // eg. water and glass
851 for (TileLayer &layer : tile.layers)
852 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
855 if (!data->m_smooth_lighting) {
856 lights[0] = lights[1] = lights[2] = lights[3] =
857 getFaceLight(n0, n1, face_dir, ndef);
859 v3s16 vertex_dirs[4];
860 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
862 v3s16 light_p = blockpos_nodes + p_corrected;
863 for (u16 i = 0; i < 4; i++)
864 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
870 translate_dir: unit vector with only one of x, y or z
871 face_dir: unit vector with only one of x, y or z
873 static void updateFastFaceRow(
875 const v3s16 &&startpos,
877 const v3f &&translate_dir_f,
878 const v3s16 &&face_dir,
879 std::vector<FastFace> &dest)
883 u16 continuous_tiles_count = 1;
885 bool makes_face = false;
887 v3s16 face_dir_corrected;
888 u16 lights[4] = {0, 0, 0, 0};
890 getTileInfo(data, p, face_dir,
891 makes_face, p_corrected, face_dir_corrected,
894 // Unroll this variable which has a significant build cost
896 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
897 // If tiling can be done, this is set to false in the next step
898 bool next_is_different = true;
902 bool next_makes_face = false;
903 v3s16 next_p_corrected;
904 v3s16 next_face_dir_corrected;
905 u16 next_lights[4] = {0, 0, 0, 0};
907 // If at last position, there is nothing to compare to and
908 // the face must be drawn anyway
909 if (j != MAP_BLOCKSIZE - 1) {
910 p_next = p + translate_dir;
912 getTileInfo(data, p_next, face_dir,
913 next_makes_face, next_p_corrected,
914 next_face_dir_corrected, next_lights,
917 if (next_makes_face == makes_face
918 && next_p_corrected == p_corrected + translate_dir
919 && next_face_dir_corrected == face_dir_corrected
920 && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
921 && next_tile.isTileable(tile)) {
922 next_is_different = false;
923 continuous_tiles_count++;
926 if (next_is_different) {
928 Create a face if there should be one
931 // Floating point conversion of the position vector
932 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
933 // Center point of face (kind of)
934 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
938 if (translate_dir.X != 0)
939 scale.X = continuous_tiles_count;
940 if (translate_dir.Y != 0)
941 scale.Y = continuous_tiles_count;
942 if (translate_dir.Z != 0)
943 scale.Z = continuous_tiles_count;
945 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
946 pf, sp, face_dir_corrected, scale, dest);
948 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
949 for (int i = 1; i < continuous_tiles_count; i++)
950 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
953 continuous_tiles_count = 1;
956 makes_face = next_makes_face;
957 p_corrected = next_p_corrected;
958 face_dir_corrected = next_face_dir_corrected;
959 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
960 if (next_is_different)
966 static void updateAllFastFaceRows(MeshMakeData *data,
967 std::vector<FastFace> &dest)
970 Go through every y,z and get top(y+) faces in rows of x+
972 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
973 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
974 updateFastFaceRow(data,
976 v3s16(1, 0, 0), //dir
978 v3s16(0, 1, 0), //face dir
982 Go through every x,y and get right(x+) faces in rows of z+
984 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
985 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
986 updateFastFaceRow(data,
988 v3s16(0, 0, 1), //dir
990 v3s16(1, 0, 0), //face dir
994 Go through every y,z and get back(z+) faces in rows of x+
996 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
997 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
998 updateFastFaceRow(data,
1000 v3s16(1, 0, 0), //dir
1002 v3s16(0, 0, 1), //face dir
1010 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1011 m_minimap_mapblock(NULL),
1012 m_tsrc(data->m_client->getTextureSource()),
1013 m_shdrsrc(data->m_client->getShaderSource()),
1014 m_animation_force_timer(0), // force initial animation
1016 m_last_daynight_ratio((u32) -1)
1018 for (auto &m : m_mesh)
1019 m = new scene::SMesh();
1020 m_enable_shaders = data->m_use_shaders;
1021 m_use_tangent_vertices = data->m_use_tangent_vertices;
1022 m_enable_vbo = g_settings->getBool("enable_vbo");
1024 if (g_settings->getBool("enable_minimap")) {
1025 m_minimap_mapblock = new MinimapMapblock;
1026 m_minimap_mapblock->getMinimapNodes(
1027 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1030 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1031 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1032 //TimeTaker timer1("MapBlockMesh()");
1034 std::vector<FastFace> fastfaces_new;
1035 fastfaces_new.reserve(512);
1038 We are including the faces of the trailing edges of the block.
1039 This means that when something changes, the caller must
1040 also update the meshes of the blocks at the leading edges.
1042 NOTE: This is the slowest part of this method.
1045 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1046 //TimeTaker timer2("updateAllFastFaceRows()");
1047 updateAllFastFaceRows(data, fastfaces_new);
1052 Convert FastFaces to MeshCollector
1055 MeshCollector collector(m_use_tangent_vertices);
1058 // avg 0ms (100ms spikes when loading textures the first time)
1059 // (NOTE: probably outdated)
1060 //TimeTaker timer2("MeshCollector building");
1062 for (const FastFace &f : fastfaces_new) {
1063 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1064 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1066 if (!f.layer.texture)
1069 const u16 *indices_p =
1070 f.vertex_0_2_connected ? indices : indices_alternate;
1072 collector.append(f.layer, f.vertices, 4, indices_p, 6,
1073 f.layernum, f.world_aligned);
1078 Add special graphics:
1086 MapblockMeshGenerator generator(data, &collector);
1087 generator.generate();
1090 collector.applyTileColors();
1093 Convert MeshCollector to SMesh
1096 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1097 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1099 PreMeshBuffer &p = collector.prebuffers[layer][i];
1101 // Generate animation data
1103 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1104 // Find the texture name plus ^[crack:N:
1105 std::ostringstream os(std::ios::binary);
1106 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1107 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1108 os << "o"; // use ^[cracko
1109 u8 tiles = p.layer.scale;
1111 os << ":" << (u32)tiles;
1112 os << ":" << (u32)p.layer.animation_frame_count << ":";
1113 m_crack_materials.insert(std::make_pair(
1114 std::pair<u8, u32>(layer, i), os.str()));
1115 // Replace tile texture with the cracked one
1116 p.layer.texture = m_tsrc->getTextureForMesh(
1118 &p.layer.texture_id);
1120 // - Texture animation
1121 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1122 // Add to MapBlockMesh in order to animate these tiles
1123 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1124 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1125 if (g_settings->getBool(
1126 "desynchronize_mapblock_texture_animation")) {
1127 // Get starting position from noise
1128 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1129 100000 * (2.0 + noise3d(
1130 data->m_blockpos.X, data->m_blockpos.Y,
1131 data->m_blockpos.Z, 0));
1133 // Play all synchronized
1134 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1136 // Replace tile texture with the first animation frame
1137 p.layer.texture = (*p.layer.frames)[0].texture;
1140 if (!m_enable_shaders) {
1141 // Extract colors for day-night animation
1142 // Dummy sunlight to handle non-sunlit areas
1143 video::SColorf sunlight;
1144 get_sunlight_color(&sunlight, 0);
1145 u32 vertex_count = m_use_tangent_vertices ?
1146 p.tangent_vertices.size() : p.vertices.size();
1147 for (u32 j = 0; j < vertex_count; j++) {
1149 if (m_use_tangent_vertices) {
1150 vc = &p.tangent_vertices[j].Color;
1152 vc = &p.vertices[j].Color;
1154 video::SColor copy(*vc);
1155 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1156 final_color_blend(vc, copy, sunlight); // Finalize color
1157 else // Record color to animate
1158 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1160 // The sunlight ratio has been stored,
1161 // delete alpha (for the final rendering).
1167 video::SMaterial material;
1168 material.setFlag(video::EMF_LIGHTING, false);
1169 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1170 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1171 material.setFlag(video::EMF_FOG_ENABLE, true);
1172 material.setTexture(0, p.layer.texture);
1174 if (m_enable_shaders) {
1175 material.MaterialType = m_shdrsrc->getShaderInfo(
1176 p.layer.shader_id).material;
1177 p.layer.applyMaterialOptionsWithShaders(material);
1178 if (p.layer.normal_texture)
1179 material.setTexture(1, p.layer.normal_texture);
1180 material.setTexture(2, p.layer.flags_texture);
1182 p.layer.applyMaterialOptions(material);
1185 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1187 // Create meshbuffer, add to mesh
1188 if (m_use_tangent_vertices) {
1189 scene::SMeshBufferTangents *buf =
1190 new scene::SMeshBufferTangents();
1192 buf->Material = material;
1194 mesh->addMeshBuffer(buf);
1197 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1198 &p.indices[0], p.indices.size());
1200 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1202 buf->Material = material;
1204 mesh->addMeshBuffer(buf);
1207 buf->append(&p.vertices[0], p.vertices.size(),
1208 &p.indices[0], p.indices.size());
1213 Do some stuff to the mesh
1215 m_camera_offset = camera_offset;
1216 translateMesh(m_mesh[layer],
1217 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1219 if (m_use_tangent_vertices) {
1220 scene::IMeshManipulator* meshmanip =
1221 RenderingEngine::get_scene_manager()->getMeshManipulator();
1222 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1225 if (m_mesh[layer]) {
1227 // Usually 1-700 faces and 1-7 materials
1228 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1229 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1230 << " materials (meshbuffers)" << std::endl;
1233 // Use VBO for mesh (this just would set this for ever buffer)
1235 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1239 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1241 // Check if animation is required for this mesh
1243 !m_crack_materials.empty() ||
1244 !m_daynight_diffs.empty() ||
1245 !m_animation_tiles.empty();
1248 MapBlockMesh::~MapBlockMesh()
1250 for (scene::IMesh *m : m_mesh) {
1251 if (m_enable_vbo && m)
1252 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1253 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1254 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1259 delete m_minimap_mapblock;
1262 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1265 if (!m_has_animation) {
1266 m_animation_force_timer = 100000;
1270 m_animation_force_timer = myrand_range(5, 100);
1273 if (crack != m_last_crack) {
1274 for (auto &crack_material : m_crack_materials) {
1275 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1276 getMeshBuffer(crack_material.first.second);
1277 std::string basename = crack_material.second;
1279 // Create new texture name from original
1280 std::ostringstream os;
1281 os << basename << crack;
1282 u32 new_texture_id = 0;
1283 video::ITexture *new_texture =
1284 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1285 buf->getMaterial().setTexture(0, new_texture);
1287 // If the current material is also animated,
1288 // update animation info
1289 auto anim_iter = m_animation_tiles.find(crack_material.first);
1290 if (anim_iter != m_animation_tiles.end()) {
1291 TileLayer &tile = anim_iter->second;
1292 tile.texture = new_texture;
1293 tile.texture_id = new_texture_id;
1294 // force animation update
1295 m_animation_frames[crack_material.first] = -1;
1299 m_last_crack = crack;
1302 // Texture animation
1303 for (auto &animation_tile : m_animation_tiles) {
1304 const TileLayer &tile = animation_tile.second;
1305 // Figure out current frame
1306 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1307 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1308 + frameoffset) % tile.animation_frame_count;
1309 // If frame doesn't change, skip
1310 if (frame == m_animation_frames[animation_tile.first])
1313 m_animation_frames[animation_tile.first] = frame;
1315 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1316 getMeshBuffer(animation_tile.first.second);
1318 const FrameSpec &animation_frame = (*tile.frames)[frame];
1319 buf->getMaterial().setTexture(0, animation_frame.texture);
1320 if (m_enable_shaders) {
1321 if (animation_frame.normal_texture)
1322 buf->getMaterial().setTexture(1,
1323 animation_frame.normal_texture);
1324 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1328 // Day-night transition
1329 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1330 // Force reload mesh to VBO
1332 for (scene::IMesh *m : m_mesh)
1334 video::SColorf day_color;
1335 get_sunlight_color(&day_color, daynight_ratio);
1337 for (auto &daynight_diff : m_daynight_diffs) {
1338 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1339 getMeshBuffer(daynight_diff.first.second);
1340 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1341 for (const auto &j : daynight_diff.second)
1342 final_color_blend(&(vertices[j.first].Color), j.second,
1345 m_last_daynight_ratio = daynight_ratio;
1351 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1353 if (camera_offset != m_camera_offset) {
1354 for (scene::IMesh *layer : m_mesh) {
1355 translateMesh(layer,
1356 intToFloat(m_camera_offset - camera_offset, BS));
1360 m_camera_offset = camera_offset;
1368 void MeshCollector::append(const TileSpec &tile,
1369 const video::S3DVertex *vertices, u32 numVertices,
1370 const u16 *indices, u32 numIndices)
1372 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1373 const TileLayer *layer = &tile.layers[layernum];
1374 if (layer->texture_id == 0)
1376 append(*layer, vertices, numVertices, indices, numIndices,
1377 layernum, tile.world_aligned);
1381 void MeshCollector::append(const TileLayer &layer,
1382 const video::S3DVertex *vertices, u32 numVertices,
1383 const u16 *indices, u32 numIndices, u8 layernum,
1386 if (numIndices > 65535) {
1387 dstream << "FIXME: MeshCollector::append() called with numIndices="
1388 << numIndices << " (limit 65535)" << std::endl;
1391 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1393 PreMeshBuffer *p = NULL;
1394 for (PreMeshBuffer &pp : *buffers) {
1395 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1404 buffers->push_back(pp);
1405 p = &(*buffers)[buffers->size() - 1];
1410 scale = 1.0 / layer.scale;
1413 if (m_use_tangent_vertices) {
1414 vertex_count = p->tangent_vertices.size();
1415 for (u32 i = 0; i < numVertices; i++) {
1417 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1418 vertices[i].Color, scale * vertices[i].TCoords);
1419 p->tangent_vertices.push_back(vert);
1422 vertex_count = p->vertices.size();
1423 for (u32 i = 0; i < numVertices; i++) {
1424 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1425 vertices[i].Color, scale * vertices[i].TCoords);
1427 p->vertices.push_back(vert);
1431 for (u32 i = 0; i < numIndices; i++) {
1432 u32 j = indices[i] + vertex_count;
1433 p->indices.push_back(j);
1438 MeshCollector - for meshnodes and converted drawtypes.
1441 void MeshCollector::append(const TileSpec &tile,
1442 const video::S3DVertex *vertices, u32 numVertices,
1443 const u16 *indices, u32 numIndices,
1444 v3f pos, video::SColor c, u8 light_source)
1446 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1447 const TileLayer *layer = &tile.layers[layernum];
1448 if (layer->texture_id == 0)
1450 append(*layer, vertices, numVertices, indices, numIndices, pos,
1451 c, light_source, layernum, tile.world_aligned);
1455 void MeshCollector::append(const TileLayer &layer,
1456 const video::S3DVertex *vertices, u32 numVertices,
1457 const u16 *indices, u32 numIndices,
1458 v3f pos, video::SColor c, u8 light_source, u8 layernum,
1461 if (numIndices > 65535) {
1462 dstream << "FIXME: MeshCollector::append() called with numIndices="
1463 << numIndices << " (limit 65535)" << std::endl;
1466 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1468 PreMeshBuffer *p = NULL;
1469 for (PreMeshBuffer &pp : *buffers) {
1470 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1479 buffers->push_back(pp);
1480 p = &(*buffers)[buffers->size() - 1];
1485 scale = 1.0 / layer.scale;
1487 video::SColor original_c = c;
1489 if (m_use_tangent_vertices) {
1490 vertex_count = p->tangent_vertices.size();
1491 for (u32 i = 0; i < numVertices; i++) {
1492 if (!light_source) {
1494 applyFacesShading(c, vertices[i].Normal);
1496 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1497 vertices[i].Normal, c, scale * vertices[i].TCoords);
1498 p->tangent_vertices.push_back(vert);
1501 vertex_count = p->vertices.size();
1502 for (u32 i = 0; i < numVertices; i++) {
1503 if (!light_source) {
1505 applyFacesShading(c, vertices[i].Normal);
1507 video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
1508 scale * vertices[i].TCoords);
1509 p->vertices.push_back(vert);
1513 for (u32 i = 0; i < numIndices; i++) {
1514 u32 j = indices[i] + vertex_count;
1515 p->indices.push_back(j);
1519 void MeshCollector::applyTileColors()
1521 if (m_use_tangent_vertices)
1522 for (auto &prebuffer : prebuffers) {
1523 for (PreMeshBuffer &pmb : prebuffer) {
1524 video::SColor tc = pmb.layer.color;
1525 if (tc == video::SColor(0xFFFFFFFF))
1527 for (video::S3DVertexTangents &tangent_vertex : pmb.tangent_vertices) {
1528 video::SColor *c = &tangent_vertex.Color;
1529 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1530 c->getGreen() * tc.getGreen() / 255,
1531 c->getBlue() * tc.getBlue() / 255);
1536 for (auto &prebuffer : prebuffers) {
1537 for (PreMeshBuffer &pmb : prebuffer) {
1538 video::SColor tc = pmb.layer.color;
1539 if (tc == video::SColor(0xFFFFFFFF))
1541 for (video::S3DVertex &vertex : pmb.vertices) {
1542 video::SColor *c = &vertex.Color;
1543 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1544 c->getGreen() * tc.getGreen() / 255,
1545 c->getBlue() * tc.getBlue() / 255);
1551 video::SColor encode_light(u16 light, u8 emissive_light)
1554 u32 day = (light & 0xff);
1555 u32 night = (light >> 8);
1556 // Add emissive light
1557 night += emissive_light * 2.5f;
1560 // Since we don't know if the day light is sunlight or
1561 // artificial light, assume it is artificial when the night
1562 // light bank is also lit.
1567 u32 sum = day + night;
1568 // Ratio of sunlight:
1571 r = day * 255 / sum;
1575 float b = (day + night) / 2;
1576 return video::SColor(r, b, b, b);