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 INodeDefManager *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, INodeDefManager *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, INodeDefManager *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, INodeDefManager *ndef)
189 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
190 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
191 return day | (night << 8);
195 Calculate smooth lighting at the XYZ- corner of p.
198 static u16 getSmoothLightCombined(const v3s16 &p,
199 const std::array<v3s16,8> &dirs, MeshMakeData *data)
201 INodeDefManager *ndef = data->m_client->ndef();
203 u16 ambient_occlusion = 0;
205 u8 light_source_max = 0;
209 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
214 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
215 if (n.getContent() == CONTENT_IGNORE)
217 const ContentFeatures &f = ndef->get(n);
218 if (f.light_source > light_source_max)
219 light_source_max = f.light_source;
220 // Check f.solidness because fast-style leaves look better this way
221 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
222 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
223 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
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 brightness around light sources
256 bool skip_ambient_occlusion_day = false;
257 if (decode_light(light_source_max) >= light_day) {
258 light_day = decode_light(light_source_max);
259 skip_ambient_occlusion_day = true;
262 bool skip_ambient_occlusion_night = false;
263 if(decode_light(light_source_max) >= light_night) {
264 light_night = decode_light(light_source_max);
265 skip_ambient_occlusion_night = true;
268 if (ambient_occlusion > 4) {
269 static thread_local const float ao_gamma = rangelim(
270 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
272 // Table of gamma space multiply factors.
273 static thread_local const float light_amount[3] = {
274 powf(0.75, 1.0 / ao_gamma),
275 powf(0.5, 1.0 / ao_gamma),
276 powf(0.25, 1.0 / ao_gamma)
279 //calculate table index for gamma space multiplier
280 ambient_occlusion -= 5;
282 if (!skip_ambient_occlusion_day)
283 light_day = rangelim(core::round32(
284 light_day * light_amount[ambient_occlusion]), 0, 255);
285 if (!skip_ambient_occlusion_night)
286 light_night = rangelim(core::round32(
287 light_night * light_amount[ambient_occlusion]), 0, 255);
290 return light_day | (light_night << 8);
294 Calculate smooth lighting at the given corner of p.
296 Node at p is solid, and thus the lighting is face-dependent.
298 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
300 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
304 Calculate smooth lighting at the given corner of p.
306 Node at p is not solid, and the lighting is not face-dependent.
308 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
310 const std::array<v3s16,8> dirs = {{
311 // Always shine light
318 v3s16(corner.X,corner.Y,0),
319 v3s16(corner.X,0,corner.Z),
320 v3s16(0,corner.Y,corner.Z),
321 v3s16(corner.X,corner.Y,corner.Z)
323 return getSmoothLightCombined(p, dirs, data);
326 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
327 f32 rg = daynight_ratio / 1000.0f - 0.04f;
328 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
334 void final_color_blend(video::SColor *result,
335 u16 light, u32 daynight_ratio)
337 video::SColorf dayLight;
338 get_sunlight_color(&dayLight, daynight_ratio);
339 final_color_blend(result,
340 encode_light(light, 0), dayLight);
343 void final_color_blend(video::SColor *result,
344 const video::SColor &data, const video::SColorf &dayLight)
346 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
348 video::SColorf c(data);
351 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
352 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
353 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
355 // Emphase blue a bit in darker places
356 // Each entry of this array represents a range of 8 blue levels
357 static const u8 emphase_blue_when_dark[32] = {
358 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
359 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
362 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
363 0, 255) / 8] / 255.0f;
365 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
366 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
367 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
371 Mesh generation helpers
375 vertex_dirs: v3s16[4]
377 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
380 If looked from outside the node towards the face, the corners are:
386 if (dir == v3s16(0, 0, 1)) {
387 // If looking towards z+, this is the face that is behind
388 // the center point, facing towards z+.
389 vertex_dirs[0] = v3s16(-1,-1, 1);
390 vertex_dirs[1] = v3s16( 1,-1, 1);
391 vertex_dirs[2] = v3s16( 1, 1, 1);
392 vertex_dirs[3] = v3s16(-1, 1, 1);
393 } else if (dir == v3s16(0, 0, -1)) {
395 vertex_dirs[0] = v3s16( 1,-1,-1);
396 vertex_dirs[1] = v3s16(-1,-1,-1);
397 vertex_dirs[2] = v3s16(-1, 1,-1);
398 vertex_dirs[3] = v3s16( 1, 1,-1);
399 } else if (dir == v3s16(1, 0, 0)) {
401 vertex_dirs[0] = v3s16( 1,-1, 1);
402 vertex_dirs[1] = v3s16( 1,-1,-1);
403 vertex_dirs[2] = v3s16( 1, 1,-1);
404 vertex_dirs[3] = v3s16( 1, 1, 1);
405 } else if (dir == v3s16(-1, 0, 0)) {
407 vertex_dirs[0] = v3s16(-1,-1,-1);
408 vertex_dirs[1] = v3s16(-1,-1, 1);
409 vertex_dirs[2] = v3s16(-1, 1, 1);
410 vertex_dirs[3] = v3s16(-1, 1,-1);
411 } else if (dir == v3s16(0, 1, 0)) {
412 // faces towards Y+ (assume Z- as "down" in texture)
413 vertex_dirs[0] = v3s16( 1, 1,-1);
414 vertex_dirs[1] = v3s16(-1, 1,-1);
415 vertex_dirs[2] = v3s16(-1, 1, 1);
416 vertex_dirs[3] = v3s16( 1, 1, 1);
417 } else if (dir == v3s16(0, -1, 0)) {
418 // faces towards Y- (assume Z+ as "down" in texture)
419 vertex_dirs[0] = v3s16( 1,-1, 1);
420 vertex_dirs[1] = v3s16(-1,-1, 1);
421 vertex_dirs[2] = v3s16(-1,-1,-1);
422 vertex_dirs[3] = v3s16( 1,-1,-1);
426 static void getNodeTextureCoords(v3f base, const v3f &scale, v3s16 dir, float *u, float *v)
428 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
430 if (dir == v3s16(0,0,1)) {
433 } else if (dir == v3s16(0,0,-1)) {
436 } else if (dir == v3s16(1,0,0)) {
439 } else if (dir == v3s16(-1,0,0)) {
442 } else if (dir == v3s16(0,1,0)) {
445 } else if (dir == v3s16(0,-1,0)) {
454 video::S3DVertex vertices[4]; // Precalculated vertices
456 * The face is divided into two triangles. If this is true,
457 * vertices 0 and 2 are connected, othervise vertices 1 and 3
460 bool vertex_0_2_connected;
465 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
466 v3f tp, v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
468 // Position is at the center of the cube.
477 v3s16 vertex_dirs[4];
478 getNodeVertexDirs(dir, vertex_dirs);
479 if (tile.world_aligned)
480 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
484 switch (tile.rotation) {
489 vertex_dirs[0] = vertex_dirs[3];
490 vertex_dirs[3] = vertex_dirs[2];
491 vertex_dirs[2] = vertex_dirs[1];
501 vertex_dirs[0] = vertex_dirs[2];
504 vertex_dirs[1] = vertex_dirs[3];
515 vertex_dirs[0] = vertex_dirs[1];
516 vertex_dirs[1] = vertex_dirs[2];
517 vertex_dirs[2] = vertex_dirs[3];
527 vertex_dirs[0] = vertex_dirs[3];
528 vertex_dirs[3] = vertex_dirs[2];
529 vertex_dirs[2] = vertex_dirs[1];
541 vertex_dirs[0] = vertex_dirs[1];
542 vertex_dirs[1] = vertex_dirs[2];
543 vertex_dirs[2] = vertex_dirs[3];
555 vertex_dirs[0] = vertex_dirs[3];
556 vertex_dirs[3] = vertex_dirs[2];
557 vertex_dirs[2] = vertex_dirs[1];
569 vertex_dirs[0] = vertex_dirs[1];
570 vertex_dirs[1] = vertex_dirs[2];
571 vertex_dirs[2] = vertex_dirs[3];
593 for (u16 i = 0; i < 4; i++) {
595 BS / 2 * vertex_dirs[i].X,
596 BS / 2 * vertex_dirs[i].Y,
597 BS / 2 * vertex_dirs[i].Z
601 for (v3f &vpos : vertex_pos) {
608 f32 abs_scale = 1.0f;
609 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
610 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
611 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
613 v3f normal(dir.X, dir.Y, dir.Z);
615 u16 li[4] = { li0, li1, li2, li3 };
619 for (u8 i = 0; i < 4; i++) {
621 night[i] = li[i] & 0xFF;
624 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
625 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
628 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
629 core::vector2d<f32>(x0, y0 + h),
630 core::vector2d<f32>(x0, y0),
631 core::vector2d<f32>(x0 + w * abs_scale, y0) };
633 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
634 const TileLayer *layer = &tile.layers[layernum];
635 if (layer->texture_id == 0)
638 // equivalent to dest.push_back(FastFace()) but faster
640 FastFace& face = *dest.rbegin();
642 for (u8 i = 0; i < 4; i++) {
643 video::SColor c = encode_light(li[i], tile.emissive_light);
644 if (!tile.emissive_light)
645 applyFacesShading(c, normal);
647 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
651 Revert triangles for nicer looking gradient if the
652 brightness of vertices 1 and 3 differ less than
653 the brightness of vertices 0 and 2.
655 face.vertex_0_2_connected = vertex_0_2_connected;
658 face.layernum = layernum;
660 face.world_aligned = tile.world_aligned;
665 Nodes make a face if contents differ and solidness differs.
668 1: Face uses m1's content
669 2: Face uses m2's content
670 equivalent: Whether the blocks share the same face (eg. water and glass)
672 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
674 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
675 INodeDefManager *ndef)
679 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
682 const ContentFeatures &f1 = ndef->get(m1);
683 const ContentFeatures &f2 = ndef->get(m2);
685 // Contents don't differ for different forms of same liquid
686 if (f1.sameLiquid(f2))
689 u8 c1 = f1.solidness;
690 u8 c2 = f2.solidness;
696 c1 = f1.visual_solidness;
698 c2 = f2.visual_solidness;
702 // If same solidness, liquid takes precense
716 Gets nth node tile (0 <= n <= 5).
718 void getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
720 INodeDefManager *ndef = data->m_client->ndef();
721 const ContentFeatures &f = ndef->get(mn);
722 tile = f.tiles[tileindex];
723 bool has_crack = p == data->m_crack_pos_relative;
724 for (TileLayer &layer : tile.layers) {
725 if (layer.texture_id == 0)
727 if (!layer.has_color)
728 mn.getColor(f, &(layer.color));
729 // Apply temporary crack
731 layer.material_flags |= MATERIAL_FLAG_CRACK;
736 Gets node tile given a face direction.
738 void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &tile)
740 INodeDefManager *ndef = data->m_client->ndef();
742 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
743 // (0,0,1), (0,0,-1) or (0,0,0)
744 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
746 // Convert direction to single integer for table lookup
751 // 4 = invalid, treat as (0,0,0)
755 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
757 // Get rotation for things like chests
758 u8 facedir = mn.getFaceDir(ndef);
760 static const u16 dir_to_tile[24 * 16] =
762 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
763 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
764 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
765 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
766 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
768 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
769 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
770 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
771 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
773 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
774 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
775 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
776 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
778 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
779 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
780 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
781 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
783 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
784 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
785 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
786 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
788 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
789 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
790 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
791 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
794 u16 tile_index = facedir * 16 + dir_i;
795 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
796 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
799 static void getTileInfo(
803 const v3s16 &face_dir,
807 v3s16 &face_dir_corrected,
812 VoxelManipulator &vmanip = data->m_vmanip;
813 INodeDefManager *ndef = data->m_client->ndef();
814 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
816 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
818 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
819 if (n0.getContent() == CONTENT_IGNORE) {
824 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
826 if (n1.getContent() == CONTENT_IGNORE) {
832 bool equivalent = false;
833 u8 mf = face_contents(n0.getContent(), n1.getContent(),
847 face_dir_corrected = face_dir;
850 p_corrected = p + face_dir;
851 face_dir_corrected = -face_dir;
854 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
855 const ContentFeatures &f = ndef->get(n);
856 tile.emissive_light = f.light_source;
858 // eg. water and glass
860 for (TileLayer &layer : tile.layers)
861 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
864 if (!data->m_smooth_lighting) {
865 lights[0] = lights[1] = lights[2] = lights[3] =
866 getFaceLight(n0, n1, face_dir, ndef);
868 v3s16 vertex_dirs[4];
869 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
871 v3s16 light_p = blockpos_nodes + p_corrected;
872 for (u16 i = 0; i < 4; i++)
873 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
879 translate_dir: unit vector with only one of x, y or z
880 face_dir: unit vector with only one of x, y or z
882 static void updateFastFaceRow(
884 const v3s16 &&startpos,
886 const v3f &&translate_dir_f,
887 const v3s16 &&face_dir,
888 std::vector<FastFace> &dest)
892 u16 continuous_tiles_count = 1;
894 bool makes_face = false;
896 v3s16 face_dir_corrected;
897 u16 lights[4] = {0, 0, 0, 0};
899 getTileInfo(data, p, face_dir,
900 makes_face, p_corrected, face_dir_corrected,
903 // Unroll this variable which has a significant build cost
905 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
906 // If tiling can be done, this is set to false in the next step
907 bool next_is_different = true;
911 bool next_makes_face = false;
912 v3s16 next_p_corrected;
913 v3s16 next_face_dir_corrected;
914 u16 next_lights[4] = {0, 0, 0, 0};
916 // If at last position, there is nothing to compare to and
917 // the face must be drawn anyway
918 if (j != MAP_BLOCKSIZE - 1) {
919 p_next = p + translate_dir;
921 getTileInfo(data, p_next, face_dir,
922 next_makes_face, next_p_corrected,
923 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 && next_tile.isTileable(tile)) {
931 next_is_different = false;
932 continuous_tiles_count++;
935 if (next_is_different) {
937 Create a face if there should be one
940 // Floating point conversion of the position vector
941 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
942 // Center point of face (kind of)
943 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
947 if (translate_dir.X != 0)
948 scale.X = continuous_tiles_count;
949 if (translate_dir.Y != 0)
950 scale.Y = continuous_tiles_count;
951 if (translate_dir.Z != 0)
952 scale.Z = continuous_tiles_count;
954 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
955 pf, sp, face_dir_corrected, scale, dest);
957 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
958 for (int i = 1; i < continuous_tiles_count; i++)
959 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
962 continuous_tiles_count = 1;
965 makes_face = next_makes_face;
966 p_corrected = next_p_corrected;
967 face_dir_corrected = next_face_dir_corrected;
968 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
969 if (next_is_different)
975 static void updateAllFastFaceRows(MeshMakeData *data,
976 std::vector<FastFace> &dest)
979 Go through every y,z and get top(y+) faces in rows of x+
981 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
982 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
983 updateFastFaceRow(data,
985 v3s16(1, 0, 0), //dir
987 v3s16(0, 1, 0), //face dir
991 Go through every x,y and get right(x+) faces in rows of z+
993 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
994 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
995 updateFastFaceRow(data,
997 v3s16(0, 0, 1), //dir
999 v3s16(1, 0, 0), //face dir
1003 Go through every y,z and get back(z+) faces in rows of x+
1005 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1006 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1007 updateFastFaceRow(data,
1009 v3s16(1, 0, 0), //dir
1011 v3s16(0, 0, 1), //face dir
1019 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1020 m_minimap_mapblock(NULL),
1021 m_tsrc(data->m_client->getTextureSource()),
1022 m_shdrsrc(data->m_client->getShaderSource()),
1023 m_animation_force_timer(0), // force initial animation
1025 m_last_daynight_ratio((u32) -1)
1027 for (auto &m : m_mesh)
1028 m = new scene::SMesh();
1029 m_enable_shaders = data->m_use_shaders;
1030 m_use_tangent_vertices = data->m_use_tangent_vertices;
1031 m_enable_vbo = g_settings->getBool("enable_vbo");
1033 if (g_settings->getBool("enable_minimap")) {
1034 m_minimap_mapblock = new MinimapMapblock;
1035 m_minimap_mapblock->getMinimapNodes(
1036 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1039 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1040 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1041 //TimeTaker timer1("MapBlockMesh()");
1043 std::vector<FastFace> fastfaces_new;
1044 fastfaces_new.reserve(512);
1047 We are including the faces of the trailing edges of the block.
1048 This means that when something changes, the caller must
1049 also update the meshes of the blocks at the leading edges.
1051 NOTE: This is the slowest part of this method.
1054 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1055 //TimeTaker timer2("updateAllFastFaceRows()");
1056 updateAllFastFaceRows(data, fastfaces_new);
1061 Convert FastFaces to MeshCollector
1064 MeshCollector collector(m_use_tangent_vertices);
1067 // avg 0ms (100ms spikes when loading textures the first time)
1068 // (NOTE: probably outdated)
1069 //TimeTaker timer2("MeshCollector building");
1071 for (const FastFace &f : fastfaces_new) {
1072 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1073 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1075 if (!f.layer.texture)
1078 const u16 *indices_p =
1079 f.vertex_0_2_connected ? indices : indices_alternate;
1081 collector.append(f.layer, f.vertices, 4, indices_p, 6,
1082 f.layernum, f.world_aligned);
1087 Add special graphics:
1095 MapblockMeshGenerator generator(data, &collector);
1096 generator.generate();
1099 collector.applyTileColors();
1102 Convert MeshCollector to SMesh
1105 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1106 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1108 PreMeshBuffer &p = collector.prebuffers[layer][i];
1110 // Generate animation data
1112 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1113 // Find the texture name plus ^[crack:N:
1114 std::ostringstream os(std::ios::binary);
1115 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1116 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1117 os << "o"; // use ^[cracko
1118 u8 tiles = p.layer.scale;
1120 os << ":" << (u32)tiles;
1121 os << ":" << (u32)p.layer.animation_frame_count << ":";
1122 m_crack_materials.insert(std::make_pair(
1123 std::pair<u8, u32>(layer, i), os.str()));
1124 // Replace tile texture with the cracked one
1125 p.layer.texture = m_tsrc->getTextureForMesh(
1127 &p.layer.texture_id);
1129 // - Texture animation
1130 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1131 // Add to MapBlockMesh in order to animate these tiles
1132 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1133 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1134 if (g_settings->getBool(
1135 "desynchronize_mapblock_texture_animation")) {
1136 // Get starting position from noise
1137 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1138 100000 * (2.0 + noise3d(
1139 data->m_blockpos.X, data->m_blockpos.Y,
1140 data->m_blockpos.Z, 0));
1142 // Play all synchronized
1143 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1145 // Replace tile texture with the first animation frame
1146 p.layer.texture = (*p.layer.frames)[0].texture;
1149 if (!m_enable_shaders) {
1150 // Extract colors for day-night animation
1151 // Dummy sunlight to handle non-sunlit areas
1152 video::SColorf sunlight;
1153 get_sunlight_color(&sunlight, 0);
1154 u32 vertex_count = m_use_tangent_vertices ?
1155 p.tangent_vertices.size() : p.vertices.size();
1156 for (u32 j = 0; j < vertex_count; j++) {
1158 if (m_use_tangent_vertices) {
1159 vc = &p.tangent_vertices[j].Color;
1161 vc = &p.vertices[j].Color;
1163 video::SColor copy(*vc);
1164 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1165 final_color_blend(vc, copy, sunlight); // Finalize color
1166 else // Record color to animate
1167 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1169 // The sunlight ratio has been stored,
1170 // delete alpha (for the final rendering).
1176 video::SMaterial material;
1177 material.setFlag(video::EMF_LIGHTING, false);
1178 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1179 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1180 material.setFlag(video::EMF_FOG_ENABLE, true);
1181 material.setTexture(0, p.layer.texture);
1183 if (m_enable_shaders) {
1184 material.MaterialType = m_shdrsrc->getShaderInfo(
1185 p.layer.shader_id).material;
1186 p.layer.applyMaterialOptionsWithShaders(material);
1187 if (p.layer.normal_texture)
1188 material.setTexture(1, p.layer.normal_texture);
1189 material.setTexture(2, p.layer.flags_texture);
1191 p.layer.applyMaterialOptions(material);
1194 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1196 // Create meshbuffer, add to mesh
1197 if (m_use_tangent_vertices) {
1198 scene::SMeshBufferTangents *buf =
1199 new scene::SMeshBufferTangents();
1201 buf->Material = material;
1203 mesh->addMeshBuffer(buf);
1206 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1207 &p.indices[0], p.indices.size());
1209 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1211 buf->Material = material;
1213 mesh->addMeshBuffer(buf);
1216 buf->append(&p.vertices[0], p.vertices.size(),
1217 &p.indices[0], p.indices.size());
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;
1377 void MeshCollector::append(const TileSpec &tile,
1378 const video::S3DVertex *vertices, u32 numVertices,
1379 const u16 *indices, u32 numIndices)
1381 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1382 const TileLayer *layer = &tile.layers[layernum];
1383 if (layer->texture_id == 0)
1385 append(*layer, vertices, numVertices, indices, numIndices,
1386 layernum, tile.world_aligned);
1390 void MeshCollector::append(const TileLayer &layer,
1391 const video::S3DVertex *vertices, u32 numVertices,
1392 const u16 *indices, u32 numIndices, u8 layernum,
1395 if (numIndices > 65535) {
1396 dstream << "FIXME: MeshCollector::append() called with numIndices="
1397 << numIndices << " (limit 65535)" << std::endl;
1400 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1402 PreMeshBuffer *p = NULL;
1403 for (PreMeshBuffer &pp : *buffers) {
1404 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1413 buffers->push_back(pp);
1414 p = &(*buffers)[buffers->size() - 1];
1419 scale = 1.0 / layer.scale;
1422 if (m_use_tangent_vertices) {
1423 vertex_count = p->tangent_vertices.size();
1424 for (u32 i = 0; i < numVertices; i++) {
1426 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1427 vertices[i].Color, scale * vertices[i].TCoords);
1428 p->tangent_vertices.push_back(vert);
1431 vertex_count = p->vertices.size();
1432 for (u32 i = 0; i < numVertices; i++) {
1433 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1434 vertices[i].Color, scale * vertices[i].TCoords);
1436 p->vertices.push_back(vert);
1440 for (u32 i = 0; i < numIndices; i++) {
1441 u32 j = indices[i] + vertex_count;
1442 p->indices.push_back(j);
1447 MeshCollector - for meshnodes and converted drawtypes.
1450 void MeshCollector::append(const TileSpec &tile,
1451 const video::S3DVertex *vertices, u32 numVertices,
1452 const u16 *indices, u32 numIndices,
1453 v3f pos, video::SColor c, u8 light_source)
1455 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1456 const TileLayer *layer = &tile.layers[layernum];
1457 if (layer->texture_id == 0)
1459 append(*layer, vertices, numVertices, indices, numIndices, pos,
1460 c, light_source, layernum, tile.world_aligned);
1464 void MeshCollector::append(const TileLayer &layer,
1465 const video::S3DVertex *vertices, u32 numVertices,
1466 const u16 *indices, u32 numIndices,
1467 v3f pos, video::SColor c, u8 light_source, u8 layernum,
1470 if (numIndices > 65535) {
1471 dstream << "FIXME: MeshCollector::append() called with numIndices="
1472 << numIndices << " (limit 65535)" << std::endl;
1475 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1477 PreMeshBuffer *p = NULL;
1478 for (PreMeshBuffer &pp : *buffers) {
1479 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1488 buffers->push_back(pp);
1489 p = &(*buffers)[buffers->size() - 1];
1494 scale = 1.0 / layer.scale;
1496 video::SColor original_c = c;
1498 if (m_use_tangent_vertices) {
1499 vertex_count = p->tangent_vertices.size();
1500 for (u32 i = 0; i < numVertices; i++) {
1501 if (!light_source) {
1503 applyFacesShading(c, vertices[i].Normal);
1505 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1506 vertices[i].Normal, c, scale * vertices[i].TCoords);
1507 p->tangent_vertices.push_back(vert);
1510 vertex_count = p->vertices.size();
1511 for (u32 i = 0; i < numVertices; i++) {
1512 if (!light_source) {
1514 applyFacesShading(c, vertices[i].Normal);
1516 video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
1517 scale * vertices[i].TCoords);
1518 p->vertices.push_back(vert);
1522 for (u32 i = 0; i < numIndices; i++) {
1523 u32 j = indices[i] + vertex_count;
1524 p->indices.push_back(j);
1528 void MeshCollector::applyTileColors()
1530 if (m_use_tangent_vertices)
1531 for (auto &prebuffer : prebuffers) {
1532 for (PreMeshBuffer &pmb : prebuffer) {
1533 video::SColor tc = pmb.layer.color;
1534 if (tc == video::SColor(0xFFFFFFFF))
1536 for (video::S3DVertexTangents &tangent_vertex : pmb.tangent_vertices) {
1537 video::SColor *c = &tangent_vertex.Color;
1538 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1539 c->getGreen() * tc.getGreen() / 255,
1540 c->getBlue() * tc.getBlue() / 255);
1545 for (auto &prebuffer : prebuffers) {
1546 for (PreMeshBuffer &pmb : prebuffer) {
1547 video::SColor tc = pmb.layer.color;
1548 if (tc == video::SColor(0xFFFFFFFF))
1550 for (video::S3DVertex &vertex : pmb.vertices) {
1551 video::SColor *c = &vertex.Color;
1552 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1553 c->getGreen() * tc.getGreen() / 255,
1554 c->getBlue() * tc.getBlue() / 255);
1560 video::SColor encode_light(u16 light, u8 emissive_light)
1563 u32 day = (light & 0xff);
1564 u32 night = (light >> 8);
1565 // Add emissive light
1566 night += emissive_light * 2.5f;
1569 // Since we don't know if the day light is sunlight or
1570 // artificial light, assume it is artificial when the night
1571 // light bank is also lit.
1576 u32 sum = day + night;
1577 // Ratio of sunlight:
1580 r = day * 255 / sum;
1584 float b = (day + night) / 2;
1585 return video::SColor(r, b, b, b);