lua api: add register_on_item_activate
[waspsaliva.git] / src / client / mapblock_mesh.cpp
blob7b342fc338e4a91ab6a9b46684231285468a8ae2
1 /*
2 Minetest
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"
21 #include "client.h"
22 #include "mapblock.h"
23 #include "map.h"
24 #include "profiler.h"
25 #include "shader.h"
26 #include "mesh.h"
27 #include "minimap.h"
28 #include "content_mapblock.h"
29 #include "util/directiontables.h"
30 #include "client/meshgen/collector.h"
31 #include "client/renderingengine.h"
32 #include <array>
35 MeshMakeData
38 MeshMakeData::MeshMakeData(Client *client, bool use_shaders):
39 m_client(client),
40 m_use_shaders(use_shaders)
43 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
45 m_blockpos = blockpos;
47 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
49 m_vmanip.clear();
50 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
51 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
52 m_vmanip.addArea(voxel_area);
55 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
57 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
58 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
60 v3s16 bp = m_blockpos + block_offset;
61 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
62 m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
65 void MeshMakeData::fill(MapBlock *block)
67 fillBlockDataBegin(block->getPos());
69 fillBlockData(v3s16(0,0,0), block->getData());
71 // Get map for reading neighbor blocks
72 Map *map = block->getParent();
74 for (const v3s16 &dir : g_26dirs) {
75 v3s16 bp = m_blockpos + dir;
76 MapBlock *b = map->getBlockNoCreateNoEx(bp);
77 if(b)
78 fillBlockData(dir, b->getData());
82 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
84 if (crack_level >= 0)
85 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
88 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
90 m_smooth_lighting = smooth_lighting && ! g_settings->getBool("fullbright");
94 Light and vertex color functions
98 Calculate non-smooth lighting at interior of node.
99 Single light bank.
101 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
102 const NodeDefManager *ndef)
104 u8 light = n.getLight(bank, ndef);
105 if (light > 0)
106 light = rangelim(light + increment, 0, LIGHT_SUN);
107 if(g_settings->getBool("fullbright"))
108 return 255;
109 return decode_light(light);
113 Calculate non-smooth lighting at interior of node.
114 Both light banks.
116 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
118 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
119 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
120 return day | (night << 8);
124 Calculate non-smooth lighting at face of node.
125 Single light bank.
127 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
128 v3s16 face_dir, const NodeDefManager *ndef)
130 u8 light;
131 u8 l1 = n.getLight(bank, ndef);
132 u8 l2 = n2.getLight(bank, ndef);
133 if(l1 > l2)
134 light = l1;
135 else
136 light = l2;
138 // Boost light level for light sources
139 u8 light_source = MYMAX(ndef->get(n).light_source,
140 ndef->get(n2).light_source);
141 if(light_source > light)
142 light = light_source;
143 if(g_settings->getBool("fullbright"))
144 return 255;
145 return decode_light(light);
149 Calculate non-smooth lighting at face of node.
150 Both light banks.
152 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
153 const NodeDefManager *ndef)
155 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
156 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
157 return day | (night << 8);
161 Calculate smooth lighting at the XYZ- corner of p.
162 Both light banks
164 static u16 getSmoothLightCombined(const v3s16 &p,
165 const std::array<v3s16,8> &dirs, MeshMakeData *data)
167 const NodeDefManager *ndef = data->m_client->ndef();
169 u16 ambient_occlusion = 0;
170 u16 light_count = 0;
171 u8 light_source_max = 0;
172 u16 light_day = 0;
173 u16 light_night = 0;
174 bool direct_sunlight = false;
176 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
177 if (obstructed) {
178 ambient_occlusion++;
179 return false;
181 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
182 if (n.getContent() == CONTENT_IGNORE)
183 return true;
184 const ContentFeatures &f = ndef->get(n);
185 if (f.light_source > light_source_max)
186 light_source_max = f.light_source;
187 // Check f.solidness because fast-style leaves look better this way
188 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
189 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
190 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
191 if (light_level_day == LIGHT_SUN)
192 direct_sunlight = true;
193 light_day += decode_light(light_level_day);
194 light_night += decode_light(light_level_night);
195 light_count++;
196 } else {
197 ambient_occlusion++;
199 return f.light_propagates;
202 bool obstructed[4] = { true, true, true, true };
203 add_node(0);
204 bool opaque1 = !add_node(1);
205 bool opaque2 = !add_node(2);
206 bool opaque3 = !add_node(3);
207 obstructed[0] = opaque1 && opaque2;
208 obstructed[1] = opaque1 && opaque3;
209 obstructed[2] = opaque2 && opaque3;
210 for (u8 k = 0; k < 3; ++k)
211 if (add_node(k + 4, obstructed[k]))
212 obstructed[3] = false;
213 if (add_node(7, obstructed[3])) { // wrap light around nodes
214 ambient_occlusion -= 3;
215 for (u8 k = 0; k < 3; ++k)
216 add_node(k + 4, !obstructed[k]);
219 if (light_count == 0) {
220 light_day = light_night = 0;
221 } else {
222 light_day /= light_count;
223 light_night /= light_count;
226 // boost direct sunlight, if any
227 if (direct_sunlight)
228 light_day = 0xFF;
230 // Boost brightness around light sources
231 bool skip_ambient_occlusion_day = false;
232 if (decode_light(light_source_max) >= light_day) {
233 light_day = decode_light(light_source_max);
234 skip_ambient_occlusion_day = true;
237 bool skip_ambient_occlusion_night = false;
238 if(decode_light(light_source_max) >= light_night) {
239 light_night = decode_light(light_source_max);
240 skip_ambient_occlusion_night = true;
243 if (ambient_occlusion > 4) {
244 static thread_local const float ao_gamma = rangelim(
245 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
247 // Table of gamma space multiply factors.
248 static thread_local const float light_amount[3] = {
249 powf(0.75, 1.0 / ao_gamma),
250 powf(0.5, 1.0 / ao_gamma),
251 powf(0.25, 1.0 / ao_gamma)
254 //calculate table index for gamma space multiplier
255 ambient_occlusion -= 5;
257 if (!skip_ambient_occlusion_day)
258 light_day = rangelim(core::round32(
259 light_day * light_amount[ambient_occlusion]), 0, 255);
260 if (!skip_ambient_occlusion_night)
261 light_night = rangelim(core::round32(
262 light_night * light_amount[ambient_occlusion]), 0, 255);
265 return light_day | (light_night << 8);
269 Calculate smooth lighting at the given corner of p.
270 Both light banks.
271 Node at p is solid, and thus the lighting is face-dependent.
273 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
275 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
279 Calculate smooth lighting at the given corner of p.
280 Both light banks.
281 Node at p is not solid, and the lighting is not face-dependent.
283 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
285 const std::array<v3s16,8> dirs = {{
286 // Always shine light
287 v3s16(0,0,0),
288 v3s16(corner.X,0,0),
289 v3s16(0,corner.Y,0),
290 v3s16(0,0,corner.Z),
292 // Can be obstructed
293 v3s16(corner.X,corner.Y,0),
294 v3s16(corner.X,0,corner.Z),
295 v3s16(0,corner.Y,corner.Z),
296 v3s16(corner.X,corner.Y,corner.Z)
298 return getSmoothLightCombined(p, dirs, data);
301 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
302 f32 rg = daynight_ratio / 1000.0f - 0.04f;
303 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
304 sunlight->r = rg;
305 sunlight->g = rg;
306 sunlight->b = b;
309 void final_color_blend(video::SColor *result,
310 u16 light, u32 daynight_ratio)
312 video::SColorf dayLight;
313 get_sunlight_color(&dayLight, daynight_ratio);
314 final_color_blend(result,
315 encode_light(light, 0), dayLight);
318 void final_color_blend(video::SColor *result,
319 const video::SColor &data, const video::SColorf &dayLight)
321 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
323 video::SColorf c(data);
324 f32 n = 1 - c.a;
326 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
327 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
328 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
330 // Emphase blue a bit in darker places
331 // Each entry of this array represents a range of 8 blue levels
332 static const u8 emphase_blue_when_dark[32] = {
333 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
334 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
337 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
338 0, 255) / 8] / 255.0f;
340 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
341 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
342 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
346 Mesh generation helpers
349 // This table is moved outside getNodeVertexDirs to avoid the compiler using
350 // a mutex to initialize this table at runtime right in the hot path.
351 // For details search the internet for "cxa_guard_acquire".
352 static const v3s16 vertex_dirs_table[] = {
353 // ( 1, 0, 0)
354 v3s16( 1,-1, 1), v3s16( 1,-1,-1),
355 v3s16( 1, 1,-1), v3s16( 1, 1, 1),
356 // ( 0, 1, 0)
357 v3s16( 1, 1,-1), v3s16(-1, 1,-1),
358 v3s16(-1, 1, 1), v3s16( 1, 1, 1),
359 // ( 0, 0, 1)
360 v3s16(-1,-1, 1), v3s16( 1,-1, 1),
361 v3s16( 1, 1, 1), v3s16(-1, 1, 1),
362 // invalid
363 v3s16(), v3s16(), v3s16(), v3s16(),
364 // ( 0, 0,-1)
365 v3s16( 1,-1,-1), v3s16(-1,-1,-1),
366 v3s16(-1, 1,-1), v3s16( 1, 1,-1),
367 // ( 0,-1, 0)
368 v3s16( 1,-1, 1), v3s16(-1,-1, 1),
369 v3s16(-1,-1,-1), v3s16( 1,-1,-1),
370 // (-1, 0, 0)
371 v3s16(-1,-1,-1), v3s16(-1,-1, 1),
372 v3s16(-1, 1, 1), v3s16(-1, 1,-1)
376 vertex_dirs: v3s16[4]
378 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
381 If looked from outside the node towards the face, the corners are:
382 0: bottom-right
383 1: bottom-left
384 2: top-left
385 3: top-right
388 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
389 // (0,0,1), (0,0,-1)
390 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
392 // Convert direction to single integer for table lookup
393 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
394 idx = (idx - 1) * 4;
396 #if defined(__GNUC__) && !defined(__clang__)
397 #pragma GCC diagnostic push
398 #if __GNUC__ > 7
399 #pragma GCC diagnostic ignored "-Wclass-memaccess"
400 #endif
401 #endif
402 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
403 #if defined(__GNUC__) && !defined(__clang__)
404 #pragma GCC diagnostic pop
405 #endif
408 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
410 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
411 base -= scale;
412 if (dir == v3s16(0,0,1)) {
413 *u = -base.X - 1;
414 *v = -base.Y - 1;
415 } else if (dir == v3s16(0,0,-1)) {
416 *u = base.X + 1;
417 *v = -base.Y - 2;
418 } else if (dir == v3s16(1,0,0)) {
419 *u = base.Z + 1;
420 *v = -base.Y - 2;
421 } else if (dir == v3s16(-1,0,0)) {
422 *u = -base.Z - 1;
423 *v = -base.Y - 1;
424 } else if (dir == v3s16(0,1,0)) {
425 *u = base.X + 1;
426 *v = -base.Z - 2;
427 } else if (dir == v3s16(0,-1,0)) {
428 *u = base.X;
429 *v = base.Z;
433 struct FastFace
435 TileSpec tile;
436 video::S3DVertex vertices[4]; // Precalculated vertices
438 * The face is divided into two triangles. If this is true,
439 * vertices 0 and 2 are connected, othervise vertices 1 and 3
440 * are connected.
442 bool vertex_0_2_connected;
445 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
446 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
448 // Position is at the center of the cube.
449 v3f pos = p * BS;
451 float x0 = 0.0f;
452 float y0 = 0.0f;
453 float w = 1.0f;
454 float h = 1.0f;
456 v3f vertex_pos[4];
457 v3s16 vertex_dirs[4];
458 getNodeVertexDirs(dir, vertex_dirs);
459 if (tile.world_aligned)
460 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
462 v3s16 t;
463 u16 t1;
464 switch (tile.rotation) {
465 case 0:
466 break;
467 case 1: //R90
468 t = vertex_dirs[0];
469 vertex_dirs[0] = vertex_dirs[3];
470 vertex_dirs[3] = vertex_dirs[2];
471 vertex_dirs[2] = vertex_dirs[1];
472 vertex_dirs[1] = t;
473 t1 = li0;
474 li0 = li3;
475 li3 = li2;
476 li2 = li1;
477 li1 = t1;
478 break;
479 case 2: //R180
480 t = vertex_dirs[0];
481 vertex_dirs[0] = vertex_dirs[2];
482 vertex_dirs[2] = t;
483 t = vertex_dirs[1];
484 vertex_dirs[1] = vertex_dirs[3];
485 vertex_dirs[3] = t;
486 t1 = li0;
487 li0 = li2;
488 li2 = t1;
489 t1 = li1;
490 li1 = li3;
491 li3 = t1;
492 break;
493 case 3: //R270
494 t = vertex_dirs[0];
495 vertex_dirs[0] = vertex_dirs[1];
496 vertex_dirs[1] = vertex_dirs[2];
497 vertex_dirs[2] = vertex_dirs[3];
498 vertex_dirs[3] = t;
499 t1 = li0;
500 li0 = li1;
501 li1 = li2;
502 li2 = li3;
503 li3 = t1;
504 break;
505 case 4: //FXR90
506 t = vertex_dirs[0];
507 vertex_dirs[0] = vertex_dirs[3];
508 vertex_dirs[3] = vertex_dirs[2];
509 vertex_dirs[2] = vertex_dirs[1];
510 vertex_dirs[1] = t;
511 t1 = li0;
512 li0 = li3;
513 li3 = li2;
514 li2 = li1;
515 li1 = t1;
516 y0 += h;
517 h *= -1;
518 break;
519 case 5: //FXR270
520 t = vertex_dirs[0];
521 vertex_dirs[0] = vertex_dirs[1];
522 vertex_dirs[1] = vertex_dirs[2];
523 vertex_dirs[2] = vertex_dirs[3];
524 vertex_dirs[3] = t;
525 t1 = li0;
526 li0 = li1;
527 li1 = li2;
528 li2 = li3;
529 li3 = t1;
530 y0 += h;
531 h *= -1;
532 break;
533 case 6: //FYR90
534 t = vertex_dirs[0];
535 vertex_dirs[0] = vertex_dirs[3];
536 vertex_dirs[3] = vertex_dirs[2];
537 vertex_dirs[2] = vertex_dirs[1];
538 vertex_dirs[1] = t;
539 t1 = li0;
540 li0 = li3;
541 li3 = li2;
542 li2 = li1;
543 li1 = t1;
544 x0 += w;
545 w *= -1;
546 break;
547 case 7: //FYR270
548 t = vertex_dirs[0];
549 vertex_dirs[0] = vertex_dirs[1];
550 vertex_dirs[1] = vertex_dirs[2];
551 vertex_dirs[2] = vertex_dirs[3];
552 vertex_dirs[3] = t;
553 t1 = li0;
554 li0 = li1;
555 li1 = li2;
556 li2 = li3;
557 li3 = t1;
558 x0 += w;
559 w *= -1;
560 break;
561 case 8: //FX
562 y0 += h;
563 h *= -1;
564 break;
565 case 9: //FY
566 x0 += w;
567 w *= -1;
568 break;
569 default:
570 break;
573 for (u16 i = 0; i < 4; i++) {
574 vertex_pos[i] = v3f(
575 BS / 2 * vertex_dirs[i].X,
576 BS / 2 * vertex_dirs[i].Y,
577 BS / 2 * vertex_dirs[i].Z
581 for (v3f &vpos : vertex_pos) {
582 vpos.X *= scale.X;
583 vpos.Y *= scale.Y;
584 vpos.Z *= scale.Z;
585 vpos += pos;
588 f32 abs_scale = 1.0f;
589 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
590 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
591 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
593 v3f normal(dir.X, dir.Y, dir.Z);
595 u16 li[4] = { li0, li1, li2, li3 };
596 u16 day[4];
597 u16 night[4];
599 for (u8 i = 0; i < 4; i++) {
600 day[i] = li[i] >> 8;
601 night[i] = li[i] & 0xFF;
604 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
605 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
607 v2f32 f[4] = {
608 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
609 core::vector2d<f32>(x0, y0 + h),
610 core::vector2d<f32>(x0, y0),
611 core::vector2d<f32>(x0 + w * abs_scale, y0) };
613 // equivalent to dest.push_back(FastFace()) but faster
614 dest.emplace_back();
615 FastFace& face = *dest.rbegin();
617 for (u8 i = 0; i < 4; i++) {
618 video::SColor c = encode_light(li[i], tile.emissive_light);
619 if (!tile.emissive_light)
620 applyFacesShading(c, normal);
622 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
626 Revert triangles for nicer looking gradient if the
627 brightness of vertices 1 and 3 differ less than
628 the brightness of vertices 0 and 2.
630 face.vertex_0_2_connected = vertex_0_2_connected;
631 face.tile = tile;
635 Nodes make a face if contents differ and solidness differs.
636 Return value:
637 0: No face
638 1: Face uses m1's content
639 2: Face uses m2's content
640 equivalent: Whether the blocks share the same face (eg. water and glass)
642 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
644 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
645 const NodeDefManager *ndef)
647 *equivalent = false;
649 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
650 return 0;
652 const ContentFeatures &f1 = ndef->get(m1);
653 const ContentFeatures &f2 = ndef->get(m2);
655 // Contents don't differ for different forms of same liquid
656 if (f1.sameLiquid(f2))
657 return 0;
659 u8 c1 = f1.solidness;
660 u8 c2 = f2.solidness;
663 if (c1 == c2)
664 return 0;
666 if (c1 == 0)
667 c1 = f1.visual_solidness;
668 else if (c2 == 0)
669 c2 = f2.visual_solidness;
672 if (c1 == c2) {
673 *equivalent = true;
674 // If same solidness, liquid takes precense
675 if (f1.isLiquid())
676 return 1;
677 if (f2.isLiquid())
678 return 2;
681 if (c1 > c2)
682 return 1;
684 return 2;
688 Gets nth node tile (0 <= n <= 5).
690 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
692 const NodeDefManager *ndef = data->m_client->ndef();
693 const ContentFeatures &f = ndef->get(mn);
694 tile = f.tiles[tileindex];
695 bool has_crack = p == data->m_crack_pos_relative;
696 for (TileLayer &layer : tile.layers) {
697 if (layer.texture_id == 0)
698 continue;
699 if (!layer.has_color)
700 mn.getColor(f, &(layer.color));
701 // Apply temporary crack
702 if (has_crack)
703 layer.material_flags |= MATERIAL_FLAG_CRACK;
708 Gets node tile given a face direction.
710 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
712 const NodeDefManager *ndef = data->m_client->ndef();
714 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
715 // (0,0,1), (0,0,-1) or (0,0,0)
716 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
718 // Convert direction to single integer for table lookup
719 // 0 = (0,0,0)
720 // 1 = (1,0,0)
721 // 2 = (0,1,0)
722 // 3 = (0,0,1)
723 // 4 = invalid, treat as (0,0,0)
724 // 5 = (0,0,-1)
725 // 6 = (0,-1,0)
726 // 7 = (-1,0,0)
727 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
729 // Get rotation for things like chests
730 u8 facedir = mn.getFaceDir(ndef, true);
732 static const u16 dir_to_tile[24 * 16] =
734 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
735 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
736 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
737 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
738 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
740 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
741 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
742 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
743 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
745 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
746 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
747 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
748 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
750 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
751 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
752 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
753 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
755 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
756 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
757 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
758 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
760 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
761 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
762 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
763 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
766 u16 tile_index = facedir * 16 + dir_i;
767 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
768 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
771 std::set<content_t> splitToContentT(std::string str, const NodeDefManager *ndef)
773 str += "\n";
774 std::set<content_t> dat;
775 std::string buf;
776 for (char c : str) {
777 if (c == ',' || c == '\n') {
778 if (! buf.empty()) {
779 dat.insert(ndef->getId(buf));
781 buf.clear();
782 } else if (c != ' ') {
783 buf += c;
786 return dat;
789 static void getTileInfo(
790 // Input:
791 MeshMakeData *data,
792 const v3s16 &p,
793 const v3s16 &face_dir,
794 // Output:
795 bool &makes_face,
796 v3s16 &p_corrected,
797 v3s16 &face_dir_corrected,
798 u16 *lights,
799 u8 &waving,
800 TileSpec &tile,
801 // lol more Input
802 bool xray,
803 std::set<content_t> xraySet)
805 VoxelManipulator &vmanip = data->m_vmanip;
806 const NodeDefManager *ndef = data->m_client->ndef();
807 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
809 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
811 content_t c0 = n0.getContent();
812 if (xray && xraySet.find(c0) != xraySet.end())
813 c0 = CONTENT_AIR;
814 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
815 if (c0 == CONTENT_IGNORE) {
816 makes_face = false;
817 return;
820 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
822 content_t c1 = n1.getContent();
823 if (xray && xraySet.find(c1) != xraySet.end())
824 c1 = CONTENT_AIR;
826 if (c1 == CONTENT_IGNORE) {
827 makes_face = false;
828 return;
831 // This is hackish
832 bool equivalent = false;
833 u8 mf = face_contents(c0, c1,
834 &equivalent, ndef);
836 if (mf == 0) {
837 makes_face = false;
838 return;
841 makes_face = true;
843 MapNode n = n0;
845 if (mf == 1) {
846 p_corrected = p;
847 face_dir_corrected = face_dir;
848 } else {
849 n = n1;
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 waving = f.waving;
857 tile.emissive_light = f.light_source;
859 // eg. water and glass
860 if (equivalent) {
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);
868 } else {
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);
879 startpos:
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(
884 MeshMakeData *data,
885 const v3s16 &&startpos,
886 v3s16 translate_dir,
887 const v3f &&translate_dir_f,
888 const v3s16 &&face_dir,
889 std::vector<FastFace> &dest,
890 bool xray,
891 std::set<content_t> xraySet)
893 static thread_local const bool waving_liquids =
894 g_settings->getBool("enable_shaders") &&
895 g_settings->getBool("enable_waving_water");
897 v3s16 p = startpos;
899 u16 continuous_tiles_count = 1;
901 bool makes_face = false;
902 v3s16 p_corrected;
903 v3s16 face_dir_corrected;
904 u16 lights[4] = {0, 0, 0, 0};
905 u8 waving = 0;
906 TileSpec tile;
908 // Get info of first tile
909 getTileInfo(data, p, face_dir,
910 makes_face, p_corrected, face_dir_corrected,
911 lights, waving, tile, xray, xraySet);
913 // Unroll this variable which has a significant build cost
914 TileSpec next_tile;
915 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
916 // If tiling can be done, this is set to false in the next step
917 bool next_is_different = true;
919 bool next_makes_face = false;
920 v3s16 next_p_corrected;
921 v3s16 next_face_dir_corrected;
922 u16 next_lights[4] = {0, 0, 0, 0};
924 // If at last position, there is nothing to compare to and
925 // the face must be drawn anyway
926 if (j != MAP_BLOCKSIZE - 1) {
927 p += translate_dir;
929 getTileInfo(data, p, face_dir,
930 next_makes_face, next_p_corrected,
931 next_face_dir_corrected, next_lights,
932 waving,
933 next_tile,
934 xray,
935 xraySet);
937 if (next_makes_face == makes_face
938 && next_p_corrected == p_corrected + translate_dir
939 && next_face_dir_corrected == face_dir_corrected
940 && memcmp(next_lights, lights, sizeof(lights)) == 0
941 // Don't apply fast faces to waving water.
942 && (waving != 3 || !waving_liquids)
943 && next_tile.isTileable(tile)) {
944 next_is_different = false;
945 continuous_tiles_count++;
948 if (next_is_different) {
950 Create a face if there should be one
952 if (makes_face) {
953 // Floating point conversion of the position vector
954 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
955 // Center point of face (kind of)
956 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
957 * translate_dir_f;
958 v3f scale(1, 1, 1);
960 if (translate_dir.X != 0)
961 scale.X = continuous_tiles_count;
962 if (translate_dir.Y != 0)
963 scale.Y = continuous_tiles_count;
964 if (translate_dir.Z != 0)
965 scale.Z = continuous_tiles_count;
967 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
968 pf, sp, face_dir_corrected, scale, dest);
969 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
972 continuous_tiles_count = 1;
975 makes_face = next_makes_face;
976 p_corrected = next_p_corrected;
977 face_dir_corrected = next_face_dir_corrected;
978 memcpy(lights, next_lights, sizeof(lights));
979 if (next_is_different)
980 tile = std::move(next_tile); // faster than copy
984 static void updateAllFastFaceRows(MeshMakeData *data,
985 std::vector<FastFace> &dest, bool xray, std::set<content_t> xraySet)
988 Go through every y,z and get top(y+) faces in rows of x+
990 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
991 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
992 updateFastFaceRow(data,
993 v3s16(0, y, z),
994 v3s16(1, 0, 0), //dir
995 v3f (1, 0, 0),
996 v3s16(0, 1, 0), //face dir
997 dest,
998 xray,
999 xraySet);
1002 Go through every x,y and get right(x+) faces in rows of z+
1004 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
1005 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1006 updateFastFaceRow(data,
1007 v3s16(x, y, 0),
1008 v3s16(0, 0, 1), //dir
1009 v3f (0, 0, 1),
1010 v3s16(1, 0, 0), //face dir
1011 dest,
1012 xray,
1013 xraySet);
1016 Go through every y,z and get back(z+) faces in rows of x+
1018 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1019 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1020 updateFastFaceRow(data,
1021 v3s16(0, y, z),
1022 v3s16(1, 0, 0), //dir
1023 v3f (1, 0, 0),
1024 v3s16(0, 0, 1), //face dir
1025 dest,
1026 xray,
1027 xraySet);
1030 static void applyTileColor(PreMeshBuffer &pmb)
1032 video::SColor tc = pmb.layer.color;
1033 if (tc == video::SColor(0xFFFFFFFF))
1034 return;
1035 for (video::S3DVertex &vertex : pmb.vertices) {
1036 video::SColor *c = &vertex.Color;
1037 c->set(c->getAlpha(),
1038 c->getRed() * tc.getRed() / 255,
1039 c->getGreen() * tc.getGreen() / 255,
1040 c->getBlue() * tc.getBlue() / 255);
1045 MapBlockMesh
1048 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1049 m_minimap_mapblock(NULL),
1050 m_tsrc(data->m_client->getTextureSource()),
1051 m_shdrsrc(data->m_client->getShaderSource()),
1052 m_animation_force_timer(0), // force initial animation
1053 m_last_crack(-1),
1054 m_last_daynight_ratio((u32) -1)
1056 for (auto &m : m_mesh)
1057 m = new scene::SMesh();
1058 m_enable_shaders = data->m_use_shaders;
1059 m_enable_vbo = g_settings->getBool("enable_vbo");
1061 if (data->m_client->getMinimap()) {
1062 m_minimap_mapblock = new MinimapMapblock;
1063 m_minimap_mapblock->getMinimapNodes(
1064 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1067 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1068 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1069 //TimeTaker timer1("MapBlockMesh()");
1071 std::vector<FastFace> fastfaces_new;
1072 fastfaces_new.reserve(512);
1074 X-Ray
1076 bool xray = g_settings->getBool("xray");
1077 std::set<content_t> xraySet, nodeESPSet;
1078 if (xray)
1079 xraySet = splitToContentT(g_settings->get("xray_nodes"), data->m_client->ndef());
1081 nodeESPSet = splitToContentT(g_settings->get("node_esp_nodes"), data->m_client->ndef());
1084 We are including the faces of the trailing edges of the block.
1085 This means that when something changes, the caller must
1086 also update the meshes of the blocks at the leading edges.
1088 NOTE: This is the slowest part of this method.
1091 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1092 //TimeTaker timer2("updateAllFastFaceRows()");
1093 updateAllFastFaceRows(data, fastfaces_new, xray, xraySet);
1095 // End of slow part
1098 NodeESP
1101 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
1102 for (s16 x = 0; x < MAP_BLOCKSIZE; x++) {
1103 for (s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1104 for (s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1105 v3s16 pos = v3s16(x, y, z) + blockpos_nodes;
1106 const MapNode &node = data->m_vmanip.getNodeRefUnsafeCheckFlags(pos);
1107 if (nodeESPSet.find(node.getContent()) != nodeESPSet.end())
1108 esp_nodes.insert(pos);
1115 Convert FastFaces to MeshCollector
1118 MeshCollector collector;
1121 // avg 0ms (100ms spikes when loading textures the first time)
1122 // (NOTE: probably outdated)
1123 //TimeTaker timer2("MeshCollector building");
1125 for (const FastFace &f : fastfaces_new) {
1126 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1127 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1128 const u16 *indices_p =
1129 f.vertex_0_2_connected ? indices : indices_alternate;
1130 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1135 Add special graphics:
1136 - torches
1137 - flowing water
1138 - fences
1139 - whatever
1143 MapblockMeshGenerator generator(data, &collector);
1144 generator.generate();
1148 Convert MeshCollector to SMesh
1151 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1152 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1154 PreMeshBuffer &p = collector.prebuffers[layer][i];
1156 applyTileColor(p);
1158 // Generate animation data
1159 // - Cracks
1160 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1161 // Find the texture name plus ^[crack:N:
1162 std::ostringstream os(std::ios::binary);
1163 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1164 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1165 os << "o"; // use ^[cracko
1166 u8 tiles = p.layer.scale;
1167 if (tiles > 1)
1168 os << ":" << (u32)tiles;
1169 os << ":" << (u32)p.layer.animation_frame_count << ":";
1170 m_crack_materials.insert(std::make_pair(
1171 std::pair<u8, u32>(layer, i), os.str()));
1172 // Replace tile texture with the cracked one
1173 p.layer.texture = m_tsrc->getTextureForMesh(
1174 os.str() + "0",
1175 &p.layer.texture_id);
1177 // - Texture animation
1178 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1179 // Add to MapBlockMesh in order to animate these tiles
1180 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1181 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1182 if (g_settings->getBool(
1183 "desynchronize_mapblock_texture_animation")) {
1184 // Get starting position from noise
1185 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1186 100000 * (2.0 + noise3d(
1187 data->m_blockpos.X, data->m_blockpos.Y,
1188 data->m_blockpos.Z, 0));
1189 } else {
1190 // Play all synchronized
1191 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1193 // Replace tile texture with the first animation frame
1194 p.layer.texture = (*p.layer.frames)[0].texture;
1197 if (!m_enable_shaders) {
1198 // Extract colors for day-night animation
1199 // Dummy sunlight to handle non-sunlit areas
1200 video::SColorf sunlight;
1201 get_sunlight_color(&sunlight, 0);
1202 u32 vertex_count = p.vertices.size();
1203 for (u32 j = 0; j < vertex_count; j++) {
1204 video::SColor *vc = &p.vertices[j].Color;
1205 video::SColor copy = *vc;
1206 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1207 final_color_blend(vc, copy, sunlight); // Finalize color
1208 else // Record color to animate
1209 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1211 // The sunlight ratio has been stored,
1212 // delete alpha (for the final rendering).
1213 vc->setAlpha(255);
1217 // Create material
1218 video::SMaterial material;
1219 material.setFlag(video::EMF_LIGHTING, false);
1220 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1221 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1222 material.setFlag(video::EMF_FOG_ENABLE, true);
1223 material.setTexture(0, p.layer.texture);
1225 if (m_enable_shaders) {
1226 material.MaterialType = m_shdrsrc->getShaderInfo(
1227 p.layer.shader_id).material;
1228 p.layer.applyMaterialOptionsWithShaders(material);
1229 if (p.layer.normal_texture)
1230 material.setTexture(1, p.layer.normal_texture);
1231 material.setTexture(2, p.layer.flags_texture);
1232 } else {
1233 p.layer.applyMaterialOptions(material);
1236 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1238 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1239 buf->Material = material;
1240 buf->append(&p.vertices[0], p.vertices.size(),
1241 &p.indices[0], p.indices.size());
1242 mesh->addMeshBuffer(buf);
1243 buf->drop();
1247 Do some stuff to the mesh
1249 m_camera_offset = camera_offset;
1250 translateMesh(m_mesh[layer],
1251 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1253 if (m_mesh[layer]) {
1254 #if 0
1255 // Usually 1-700 faces and 1-7 materials
1256 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1257 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1258 << " materials (meshbuffers)" << std::endl;
1259 #endif
1261 // Use VBO for mesh (this just would set this for ever buffer)
1262 if (m_enable_vbo)
1263 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1267 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1269 // Check if animation is required for this mesh
1270 m_has_animation =
1271 !m_crack_materials.empty() ||
1272 !m_daynight_diffs.empty() ||
1273 !m_animation_tiles.empty();
1276 MapBlockMesh::~MapBlockMesh()
1278 for (scene::IMesh *m : m_mesh) {
1279 if (m_enable_vbo && m)
1280 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1281 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1282 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1284 m->drop();
1285 m = NULL;
1287 delete m_minimap_mapblock;
1290 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1291 u32 daynight_ratio)
1293 if (!m_has_animation) {
1294 m_animation_force_timer = 100000;
1295 return false;
1298 m_animation_force_timer = myrand_range(5, 100);
1300 // Cracks
1301 if (crack != m_last_crack) {
1302 for (auto &crack_material : m_crack_materials) {
1303 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1304 getMeshBuffer(crack_material.first.second);
1305 std::string basename = crack_material.second;
1307 // Create new texture name from original
1308 std::ostringstream os;
1309 os << basename << crack;
1310 u32 new_texture_id = 0;
1311 video::ITexture *new_texture =
1312 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1313 buf->getMaterial().setTexture(0, new_texture);
1315 // If the current material is also animated,
1316 // update animation info
1317 auto anim_iter = m_animation_tiles.find(crack_material.first);
1318 if (anim_iter != m_animation_tiles.end()) {
1319 TileLayer &tile = anim_iter->second;
1320 tile.texture = new_texture;
1321 tile.texture_id = new_texture_id;
1322 // force animation update
1323 m_animation_frames[crack_material.first] = -1;
1327 m_last_crack = crack;
1330 // Texture animation
1331 for (auto &animation_tile : m_animation_tiles) {
1332 const TileLayer &tile = animation_tile.second;
1333 // Figure out current frame
1334 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1335 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1336 + frameoffset) % tile.animation_frame_count;
1337 // If frame doesn't change, skip
1338 if (frame == m_animation_frames[animation_tile.first])
1339 continue;
1341 m_animation_frames[animation_tile.first] = frame;
1343 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1344 getMeshBuffer(animation_tile.first.second);
1346 const FrameSpec &animation_frame = (*tile.frames)[frame];
1347 buf->getMaterial().setTexture(0, animation_frame.texture);
1348 if (m_enable_shaders) {
1349 if (animation_frame.normal_texture)
1350 buf->getMaterial().setTexture(1,
1351 animation_frame.normal_texture);
1352 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1356 // Day-night transition
1357 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1358 // Force reload mesh to VBO
1359 if (m_enable_vbo)
1360 for (scene::IMesh *m : m_mesh)
1361 m->setDirty();
1362 video::SColorf day_color;
1363 get_sunlight_color(&day_color, daynight_ratio);
1365 for (auto &daynight_diff : m_daynight_diffs) {
1366 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1367 getMeshBuffer(daynight_diff.first.second);
1368 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1369 for (const auto &j : daynight_diff.second)
1370 final_color_blend(&(vertices[j.first].Color), j.second,
1371 day_color);
1373 m_last_daynight_ratio = daynight_ratio;
1376 return true;
1379 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1381 if (camera_offset != m_camera_offset) {
1382 for (scene::IMesh *layer : m_mesh) {
1383 translateMesh(layer,
1384 intToFloat(m_camera_offset - camera_offset, BS));
1385 if (m_enable_vbo)
1386 layer->setDirty();
1388 m_camera_offset = camera_offset;
1392 video::SColor encode_light(u16 light, u8 emissive_light)
1394 // Get components
1395 u32 day = (light & 0xff);
1396 u32 night = (light >> 8);
1397 // Add emissive light
1398 night += emissive_light * 2.5f;
1399 if (night > 255)
1400 night = 255;
1401 // Since we don't know if the day light is sunlight or
1402 // artificial light, assume it is artificial when the night
1403 // light bank is also lit.
1404 if (day < night)
1405 day = 0;
1406 else
1407 day = day - night;
1408 u32 sum = day + night;
1409 // Ratio of sunlight:
1410 u32 r;
1411 if (sum > 0)
1412 r = day * 255 / sum;
1413 else
1414 r = 0;
1415 // Average light:
1416 float b = (day + night) / 2;
1417 return video::SColor(r, b, b, b);