1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
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.
9
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.
14
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.
18 */
19
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>
33
34 /*
35 MeshMakeData
36 */
37
MeshMakeData(Client * client,bool use_shaders)38 MeshMakeData::MeshMakeData(Client *client, bool use_shaders):
39 m_client(client),
40 m_use_shaders(use_shaders)
41 {}
42
fillBlockDataBegin(const v3s16 & blockpos)43 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
44 {
45 m_blockpos = blockpos;
46
47 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
48
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);
53 }
54
fillBlockData(const v3s16 & block_offset,MapNode * data)55 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
56 {
57 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
58 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
59
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);
63 }
64
fill(MapBlock * block)65 void MeshMakeData::fill(MapBlock *block)
66 {
67 fillBlockDataBegin(block->getPos());
68
69 fillBlockData(v3s16(0,0,0), block->getData());
70
71 // Get map for reading neighbor blocks
72 Map *map = block->getParent();
73
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());
79 }
80 }
81
setCrack(int crack_level,v3s16 crack_pos)82 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
83 {
84 if (crack_level >= 0)
85 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
86 }
87
setSmoothLighting(bool smooth_lighting)88 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
89 {
90 m_smooth_lighting = smooth_lighting;
91 }
92
93 /*
94 Light and vertex color functions
95 */
96
97 /*
98 Calculate non-smooth lighting at interior of node.
99 Single light bank.
100 */
getInteriorLight(enum LightBank bank,MapNode n,s32 increment,const NodeDefManager * ndef)101 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
102 const NodeDefManager *ndef)
103 {
104 u8 light = n.getLight(bank, ndef);
105 if (light > 0)
106 light = rangelim(light + increment, 0, LIGHT_SUN);
107 return decode_light(light);
108 }
109
110 /*
111 Calculate non-smooth lighting at interior of node.
112 Both light banks.
113 */
getInteriorLight(MapNode n,s32 increment,const NodeDefManager * ndef)114 u16 getInteriorLight(MapNode n, s32 increment, const NodeDefManager *ndef)
115 {
116 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
117 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
118 return day | (night << 8);
119 }
120
121 /*
122 Calculate non-smooth lighting at face of node.
123 Single light bank.
124 */
getFaceLight(enum LightBank bank,MapNode n,MapNode n2,v3s16 face_dir,const NodeDefManager * ndef)125 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
126 v3s16 face_dir, const NodeDefManager *ndef)
127 {
128 u8 light;
129 u8 l1 = n.getLight(bank, ndef);
130 u8 l2 = n2.getLight(bank, ndef);
131 if(l1 > l2)
132 light = l1;
133 else
134 light = l2;
135
136 // Boost light level for light sources
137 u8 light_source = MYMAX(ndef->get(n).light_source,
138 ndef->get(n2).light_source);
139 if(light_source > light)
140 light = light_source;
141
142 return decode_light(light);
143 }
144
145 /*
146 Calculate non-smooth lighting at face of node.
147 Both light banks.
148 */
getFaceLight(MapNode n,MapNode n2,const v3s16 & face_dir,const NodeDefManager * ndef)149 u16 getFaceLight(MapNode n, MapNode n2, const v3s16 &face_dir,
150 const NodeDefManager *ndef)
151 {
152 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
153 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
154 return day | (night << 8);
155 }
156
157 /*
158 Calculate smooth lighting at the XYZ- corner of p.
159 Both light banks
160 */
getSmoothLightCombined(const v3s16 & p,const std::array<v3s16,8> & dirs,MeshMakeData * data)161 static u16 getSmoothLightCombined(const v3s16 &p,
162 const std::array<v3s16,8> &dirs, MeshMakeData *data)
163 {
164 const NodeDefManager *ndef = data->m_client->ndef();
165
166 u16 ambient_occlusion = 0;
167 u16 light_count = 0;
168 u8 light_source_max = 0;
169 u16 light_day = 0;
170 u16 light_night = 0;
171 bool direct_sunlight = false;
172
173 auto add_node = [&] (u8 i, bool obstructed = false) -> bool {
174 if (obstructed) {
175 ambient_occlusion++;
176 return false;
177 }
178 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
179 if (n.getContent() == CONTENT_IGNORE)
180 return true;
181 const ContentFeatures &f = ndef->get(n);
182 if (f.light_source > light_source_max)
183 light_source_max = f.light_source;
184 // Check f.solidness because fast-style leaves look better this way
185 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
186 u8 light_level_day = n.getLightNoChecks(LIGHTBANK_DAY, &f);
187 u8 light_level_night = n.getLightNoChecks(LIGHTBANK_NIGHT, &f);
188 if (light_level_day == LIGHT_SUN)
189 direct_sunlight = true;
190 light_day += decode_light(light_level_day);
191 light_night += decode_light(light_level_night);
192 light_count++;
193 } else {
194 ambient_occlusion++;
195 }
196 return f.light_propagates;
197 };
198
199 bool obstructed[4] = { true, true, true, true };
200 add_node(0);
201 bool opaque1 = !add_node(1);
202 bool opaque2 = !add_node(2);
203 bool opaque3 = !add_node(3);
204 obstructed[0] = opaque1 && opaque2;
205 obstructed[1] = opaque1 && opaque3;
206 obstructed[2] = opaque2 && opaque3;
207 for (u8 k = 0; k < 3; ++k)
208 if (add_node(k + 4, obstructed[k]))
209 obstructed[3] = false;
210 if (add_node(7, obstructed[3])) { // wrap light around nodes
211 ambient_occlusion -= 3;
212 for (u8 k = 0; k < 3; ++k)
213 add_node(k + 4, !obstructed[k]);
214 }
215
216 if (light_count == 0) {
217 light_day = light_night = 0;
218 } else {
219 light_day /= light_count;
220 light_night /= light_count;
221 }
222
223 // boost direct sunlight, if any
224 if (direct_sunlight)
225 light_day = 0xFF;
226
227 // Boost brightness around light sources
228 bool skip_ambient_occlusion_day = false;
229 if (decode_light(light_source_max) >= light_day) {
230 light_day = decode_light(light_source_max);
231 skip_ambient_occlusion_day = true;
232 }
233
234 bool skip_ambient_occlusion_night = false;
235 if(decode_light(light_source_max) >= light_night) {
236 light_night = decode_light(light_source_max);
237 skip_ambient_occlusion_night = true;
238 }
239
240 if (ambient_occlusion > 4) {
241 static thread_local const float ao_gamma = rangelim(
242 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
243
244 // Table of gamma space multiply factors.
245 static thread_local const float light_amount[3] = {
246 powf(0.75, 1.0 / ao_gamma),
247 powf(0.5, 1.0 / ao_gamma),
248 powf(0.25, 1.0 / ao_gamma)
249 };
250
251 //calculate table index for gamma space multiplier
252 ambient_occlusion -= 5;
253
254 if (!skip_ambient_occlusion_day)
255 light_day = rangelim(core::round32(
256 light_day * light_amount[ambient_occlusion]), 0, 255);
257 if (!skip_ambient_occlusion_night)
258 light_night = rangelim(core::round32(
259 light_night * light_amount[ambient_occlusion]), 0, 255);
260 }
261
262 return light_day | (light_night << 8);
263 }
264
265 /*
266 Calculate smooth lighting at the given corner of p.
267 Both light banks.
268 Node at p is solid, and thus the lighting is face-dependent.
269 */
getSmoothLightSolid(const v3s16 & p,const v3s16 & face_dir,const v3s16 & corner,MeshMakeData * data)270 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
271 {
272 return getSmoothLightTransparent(p + face_dir, corner - 2 * face_dir, data);
273 }
274
275 /*
276 Calculate smooth lighting at the given corner of p.
277 Both light banks.
278 Node at p is not solid, and the lighting is not face-dependent.
279 */
getSmoothLightTransparent(const v3s16 & p,const v3s16 & corner,MeshMakeData * data)280 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
281 {
282 const std::array<v3s16,8> dirs = {{
283 // Always shine light
284 v3s16(0,0,0),
285 v3s16(corner.X,0,0),
286 v3s16(0,corner.Y,0),
287 v3s16(0,0,corner.Z),
288
289 // Can be obstructed
290 v3s16(corner.X,corner.Y,0),
291 v3s16(corner.X,0,corner.Z),
292 v3s16(0,corner.Y,corner.Z),
293 v3s16(corner.X,corner.Y,corner.Z)
294 }};
295 return getSmoothLightCombined(p, dirs, data);
296 }
297
get_sunlight_color(video::SColorf * sunlight,u32 daynight_ratio)298 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
299 f32 rg = daynight_ratio / 1000.0f - 0.04f;
300 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
301 sunlight->r = rg;
302 sunlight->g = rg;
303 sunlight->b = b;
304 }
305
final_color_blend(video::SColor * result,u16 light,u32 daynight_ratio)306 void final_color_blend(video::SColor *result,
307 u16 light, u32 daynight_ratio)
308 {
309 video::SColorf dayLight;
310 get_sunlight_color(&dayLight, daynight_ratio);
311 final_color_blend(result,
312 encode_light(light, 0), dayLight);
313 }
314
final_color_blend(video::SColor * result,const video::SColor & data,const video::SColorf & dayLight)315 void final_color_blend(video::SColor *result,
316 const video::SColor &data, const video::SColorf &dayLight)
317 {
318 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
319
320 video::SColorf c(data);
321 f32 n = 1 - c.a;
322
323 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
324 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
325 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
326
327 // Emphase blue a bit in darker places
328 // Each entry of this array represents a range of 8 blue levels
329 static const u8 emphase_blue_when_dark[32] = {
330 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
331 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
332 };
333
334 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
335 0, 255) / 8] / 255.0f;
336
337 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
338 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
339 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
340 }
341
342 /*
343 Mesh generation helpers
344 */
345
346 // This table is moved outside getNodeVertexDirs to avoid the compiler using
347 // a mutex to initialize this table at runtime right in the hot path.
348 // For details search the internet for "cxa_guard_acquire".
349 static const v3s16 vertex_dirs_table[] = {
350 // ( 1, 0, 0)
351 v3s16( 1,-1, 1), v3s16( 1,-1,-1),
352 v3s16( 1, 1,-1), v3s16( 1, 1, 1),
353 // ( 0, 1, 0)
354 v3s16( 1, 1,-1), v3s16(-1, 1,-1),
355 v3s16(-1, 1, 1), v3s16( 1, 1, 1),
356 // ( 0, 0, 1)
357 v3s16(-1,-1, 1), v3s16( 1,-1, 1),
358 v3s16( 1, 1, 1), v3s16(-1, 1, 1),
359 // invalid
360 v3s16(), v3s16(), v3s16(), v3s16(),
361 // ( 0, 0,-1)
362 v3s16( 1,-1,-1), v3s16(-1,-1,-1),
363 v3s16(-1, 1,-1), v3s16( 1, 1,-1),
364 // ( 0,-1, 0)
365 v3s16( 1,-1, 1), v3s16(-1,-1, 1),
366 v3s16(-1,-1,-1), v3s16( 1,-1,-1),
367 // (-1, 0, 0)
368 v3s16(-1,-1,-1), v3s16(-1,-1, 1),
369 v3s16(-1, 1, 1), v3s16(-1, 1,-1)
370 };
371
372 /*
373 vertex_dirs: v3s16[4]
374 */
getNodeVertexDirs(const v3s16 & dir,v3s16 * vertex_dirs)375 static void getNodeVertexDirs(const v3s16 &dir, v3s16 *vertex_dirs)
376 {
377 /*
378 If looked from outside the node towards the face, the corners are:
379 0: bottom-right
380 1: bottom-left
381 2: top-left
382 3: top-right
383 */
384
385 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
386 // (0,0,1), (0,0,-1)
387 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z == 1);
388
389 // Convert direction to single integer for table lookup
390 u8 idx = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
391 idx = (idx - 1) * 4;
392
393 #if defined(__GNUC__) && !defined(__clang__)
394 #pragma GCC diagnostic push
395 #if __GNUC__ > 7
396 #pragma GCC diagnostic ignored "-Wclass-memaccess"
397 #endif
398 #endif
399 memcpy(vertex_dirs, &vertex_dirs_table[idx], 4 * sizeof(v3s16));
400 #if defined(__GNUC__) && !defined(__clang__)
401 #pragma GCC diagnostic pop
402 #endif
403 }
404
getNodeTextureCoords(v3f base,const v3f & scale,const v3s16 & dir,float * u,float * v)405 static void getNodeTextureCoords(v3f base, const v3f &scale, const v3s16 &dir, float *u, float *v)
406 {
407 if (dir.X > 0 || dir.Y != 0 || dir.Z < 0)
408 base -= scale;
409 if (dir == v3s16(0,0,1)) {
410 *u = -base.X - 1;
411 *v = -base.Y - 1;
412 } else if (dir == v3s16(0,0,-1)) {
413 *u = base.X + 1;
414 *v = -base.Y - 2;
415 } else if (dir == v3s16(1,0,0)) {
416 *u = base.Z + 1;
417 *v = -base.Y - 2;
418 } else if (dir == v3s16(-1,0,0)) {
419 *u = -base.Z - 1;
420 *v = -base.Y - 1;
421 } else if (dir == v3s16(0,1,0)) {
422 *u = base.X + 1;
423 *v = -base.Z - 2;
424 } else if (dir == v3s16(0,-1,0)) {
425 *u = base.X + 1;
426 *v = base.Z + 1;
427 }
428 }
429
430 struct FastFace
431 {
432 TileSpec tile;
433 video::S3DVertex vertices[4]; // Precalculated vertices
434 /*!
435 * The face is divided into two triangles. If this is true,
436 * vertices 0 and 2 are connected, othervise vertices 1 and 3
437 * are connected.
438 */
439 bool vertex_0_2_connected;
440 };
441
makeFastFace(const TileSpec & tile,u16 li0,u16 li1,u16 li2,u16 li3,const v3f & tp,const v3f & p,const v3s16 & dir,const v3f & scale,std::vector<FastFace> & dest)442 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
443 const v3f &tp, const v3f &p, const v3s16 &dir, const v3f &scale, std::vector<FastFace> &dest)
444 {
445 // Position is at the center of the cube.
446 v3f pos = p * BS;
447
448 float x0 = 0.0f;
449 float y0 = 0.0f;
450 float w = 1.0f;
451 float h = 1.0f;
452
453 v3f vertex_pos[4];
454 v3s16 vertex_dirs[4];
455 getNodeVertexDirs(dir, vertex_dirs);
456 if (tile.world_aligned)
457 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
458
459 v3s16 t;
460 u16 t1;
461 switch (tile.rotation) {
462 case 0:
463 break;
464 case 1: //R90
465 t = vertex_dirs[0];
466 vertex_dirs[0] = vertex_dirs[3];
467 vertex_dirs[3] = vertex_dirs[2];
468 vertex_dirs[2] = vertex_dirs[1];
469 vertex_dirs[1] = t;
470 t1 = li0;
471 li0 = li3;
472 li3 = li2;
473 li2 = li1;
474 li1 = t1;
475 break;
476 case 2: //R180
477 t = vertex_dirs[0];
478 vertex_dirs[0] = vertex_dirs[2];
479 vertex_dirs[2] = t;
480 t = vertex_dirs[1];
481 vertex_dirs[1] = vertex_dirs[3];
482 vertex_dirs[3] = t;
483 t1 = li0;
484 li0 = li2;
485 li2 = t1;
486 t1 = li1;
487 li1 = li3;
488 li3 = t1;
489 break;
490 case 3: //R270
491 t = vertex_dirs[0];
492 vertex_dirs[0] = vertex_dirs[1];
493 vertex_dirs[1] = vertex_dirs[2];
494 vertex_dirs[2] = vertex_dirs[3];
495 vertex_dirs[3] = t;
496 t1 = li0;
497 li0 = li1;
498 li1 = li2;
499 li2 = li3;
500 li3 = t1;
501 break;
502 case 4: //FXR90
503 t = vertex_dirs[0];
504 vertex_dirs[0] = vertex_dirs[3];
505 vertex_dirs[3] = vertex_dirs[2];
506 vertex_dirs[2] = vertex_dirs[1];
507 vertex_dirs[1] = t;
508 t1 = li0;
509 li0 = li3;
510 li3 = li2;
511 li2 = li1;
512 li1 = t1;
513 y0 += h;
514 h *= -1;
515 break;
516 case 5: //FXR270
517 t = vertex_dirs[0];
518 vertex_dirs[0] = vertex_dirs[1];
519 vertex_dirs[1] = vertex_dirs[2];
520 vertex_dirs[2] = vertex_dirs[3];
521 vertex_dirs[3] = t;
522 t1 = li0;
523 li0 = li1;
524 li1 = li2;
525 li2 = li3;
526 li3 = t1;
527 y0 += h;
528 h *= -1;
529 break;
530 case 6: //FYR90
531 t = vertex_dirs[0];
532 vertex_dirs[0] = vertex_dirs[3];
533 vertex_dirs[3] = vertex_dirs[2];
534 vertex_dirs[2] = vertex_dirs[1];
535 vertex_dirs[1] = t;
536 t1 = li0;
537 li0 = li3;
538 li3 = li2;
539 li2 = li1;
540 li1 = t1;
541 x0 += w;
542 w *= -1;
543 break;
544 case 7: //FYR270
545 t = vertex_dirs[0];
546 vertex_dirs[0] = vertex_dirs[1];
547 vertex_dirs[1] = vertex_dirs[2];
548 vertex_dirs[2] = vertex_dirs[3];
549 vertex_dirs[3] = t;
550 t1 = li0;
551 li0 = li1;
552 li1 = li2;
553 li2 = li3;
554 li3 = t1;
555 x0 += w;
556 w *= -1;
557 break;
558 case 8: //FX
559 y0 += h;
560 h *= -1;
561 break;
562 case 9: //FY
563 x0 += w;
564 w *= -1;
565 break;
566 default:
567 break;
568 }
569
570 for (u16 i = 0; i < 4; i++) {
571 vertex_pos[i] = v3f(
572 BS / 2 * vertex_dirs[i].X,
573 BS / 2 * vertex_dirs[i].Y,
574 BS / 2 * vertex_dirs[i].Z
575 );
576 }
577
578 for (v3f &vpos : vertex_pos) {
579 vpos.X *= scale.X;
580 vpos.Y *= scale.Y;
581 vpos.Z *= scale.Z;
582 vpos += pos;
583 }
584
585 f32 abs_scale = 1.0f;
586 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
587 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
588 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
589
590 v3f normal(dir.X, dir.Y, dir.Z);
591
592 u16 li[4] = { li0, li1, li2, li3 };
593 u16 day[4];
594 u16 night[4];
595
596 for (u8 i = 0; i < 4; i++) {
597 day[i] = li[i] >> 8;
598 night[i] = li[i] & 0xFF;
599 }
600
601 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
602 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
603
604 v2f32 f[4] = {
605 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
606 core::vector2d<f32>(x0, y0 + h),
607 core::vector2d<f32>(x0, y0),
608 core::vector2d<f32>(x0 + w * abs_scale, y0) };
609
610 // equivalent to dest.push_back(FastFace()) but faster
611 dest.emplace_back();
612 FastFace& face = *dest.rbegin();
613
614 for (u8 i = 0; i < 4; i++) {
615 video::SColor c = encode_light(li[i], tile.emissive_light);
616 if (!tile.emissive_light)
617 applyFacesShading(c, normal);
618
619 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
620 }
621
622 /*
623 Revert triangles for nicer looking gradient if the
624 brightness of vertices 1 and 3 differ less than
625 the brightness of vertices 0 and 2.
626 */
627 face.vertex_0_2_connected = vertex_0_2_connected;
628 face.tile = tile;
629 }
630
631 /*
632 Nodes make a face if contents differ and solidness differs.
633 Return value:
634 0: No face
635 1: Face uses m1's content
636 2: Face uses m2's content
637 equivalent: Whether the blocks share the same face (eg. water and glass)
638
639 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
640 */
face_contents(content_t m1,content_t m2,bool * equivalent,const NodeDefManager * ndef)641 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
642 const NodeDefManager *ndef)
643 {
644 *equivalent = false;
645
646 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
647 return 0;
648
649 const ContentFeatures &f1 = ndef->get(m1);
650 const ContentFeatures &f2 = ndef->get(m2);
651
652 // Contents don't differ for different forms of same liquid
653 if (f1.sameLiquid(f2))
654 return 0;
655
656 u8 c1 = f1.solidness;
657 u8 c2 = f2.solidness;
658
659 if (c1 == c2)
660 return 0;
661
662 if (c1 == 0)
663 c1 = f1.visual_solidness;
664 else if (c2 == 0)
665 c2 = f2.visual_solidness;
666
667 if (c1 == c2) {
668 *equivalent = true;
669 // If same solidness, liquid takes precense
670 if (f1.isLiquid())
671 return 1;
672 if (f2.isLiquid())
673 return 2;
674 }
675
676 if (c1 > c2)
677 return 1;
678
679 return 2;
680 }
681
682 /*
683 Gets nth node tile (0 <= n <= 5).
684 */
getNodeTileN(MapNode mn,const v3s16 & p,u8 tileindex,MeshMakeData * data,TileSpec & tile)685 void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
686 {
687 const NodeDefManager *ndef = data->m_client->ndef();
688 const ContentFeatures &f = ndef->get(mn);
689 tile = f.tiles[tileindex];
690 bool has_crack = p == data->m_crack_pos_relative;
691 for (TileLayer &layer : tile.layers) {
692 if (layer.texture_id == 0)
693 continue;
694 if (!layer.has_color)
695 mn.getColor(f, &(layer.color));
696 // Apply temporary crack
697 if (has_crack)
698 layer.material_flags |= MATERIAL_FLAG_CRACK;
699 }
700 }
701
702 /*
703 Gets node tile given a face direction.
704 */
getNodeTile(MapNode mn,const v3s16 & p,const v3s16 & dir,MeshMakeData * data,TileSpec & tile)705 void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
706 {
707 const NodeDefManager *ndef = data->m_client->ndef();
708
709 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
710 // (0,0,1), (0,0,-1) or (0,0,0)
711 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
712
713 // Convert direction to single integer for table lookup
714 // 0 = (0,0,0)
715 // 1 = (1,0,0)
716 // 2 = (0,1,0)
717 // 3 = (0,0,1)
718 // 4 = invalid, treat as (0,0,0)
719 // 5 = (0,0,-1)
720 // 6 = (0,-1,0)
721 // 7 = (-1,0,0)
722 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
723
724 // Get rotation for things like chests
725 u8 facedir = mn.getFaceDir(ndef, true);
726
727 static const u16 dir_to_tile[24 * 16] =
728 {
729 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
730 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
731 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
732 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
733 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
734
735 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
736 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
737 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
738 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
739
740 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
741 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
742 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
743 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
744
745 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
746 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
747 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
748 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
749
750 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
751 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
752 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
753 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
754
755 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
756 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
757 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
758 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
759
760 };
761 u16 tile_index = facedir * 16 + dir_i;
762 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
763 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
764 }
765
getTileInfo(MeshMakeData * data,const v3s16 & p,const v3s16 & face_dir,bool & makes_face,v3s16 & p_corrected,v3s16 & face_dir_corrected,u16 * lights,u8 & waving,TileSpec & tile)766 static void getTileInfo(
767 // Input:
768 MeshMakeData *data,
769 const v3s16 &p,
770 const v3s16 &face_dir,
771 // Output:
772 bool &makes_face,
773 v3s16 &p_corrected,
774 v3s16 &face_dir_corrected,
775 u16 *lights,
776 u8 &waving,
777 TileSpec &tile
778 )
779 {
780 VoxelManipulator &vmanip = data->m_vmanip;
781 const NodeDefManager *ndef = data->m_client->ndef();
782 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
783
784 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
785
786 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
787 if (n0.getContent() == CONTENT_IGNORE) {
788 makes_face = false;
789 return;
790 }
791
792 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
793
794 if (n1.getContent() == CONTENT_IGNORE) {
795 makes_face = false;
796 return;
797 }
798
799 // This is hackish
800 bool equivalent = false;
801 u8 mf = face_contents(n0.getContent(), n1.getContent(),
802 &equivalent, ndef);
803
804 if (mf == 0) {
805 makes_face = false;
806 return;
807 }
808
809 makes_face = true;
810
811 MapNode n = n0;
812
813 if (mf == 1) {
814 p_corrected = p;
815 face_dir_corrected = face_dir;
816 } else {
817 n = n1;
818 p_corrected = p + face_dir;
819 face_dir_corrected = -face_dir;
820 }
821
822 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
823 const ContentFeatures &f = ndef->get(n);
824 waving = f.waving;
825 tile.emissive_light = f.light_source;
826
827 // eg. water and glass
828 if (equivalent) {
829 for (TileLayer &layer : tile.layers)
830 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
831 }
832
833 if (!data->m_smooth_lighting) {
834 lights[0] = lights[1] = lights[2] = lights[3] =
835 getFaceLight(n0, n1, face_dir, ndef);
836 } else {
837 v3s16 vertex_dirs[4];
838 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
839
840 v3s16 light_p = blockpos_nodes + p_corrected;
841 for (u16 i = 0; i < 4; i++)
842 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
843 }
844 }
845
846 /*
847 startpos:
848 translate_dir: unit vector with only one of x, y or z
849 face_dir: unit vector with only one of x, y or z
850 */
updateFastFaceRow(MeshMakeData * data,const v3s16 && startpos,v3s16 translate_dir,const v3f && translate_dir_f,const v3s16 && face_dir,std::vector<FastFace> & dest)851 static void updateFastFaceRow(
852 MeshMakeData *data,
853 const v3s16 &&startpos,
854 v3s16 translate_dir,
855 const v3f &&translate_dir_f,
856 const v3s16 &&face_dir,
857 std::vector<FastFace> &dest)
858 {
859 static thread_local const bool waving_liquids =
860 g_settings->getBool("enable_shaders") &&
861 g_settings->getBool("enable_waving_water");
862
863 v3s16 p = startpos;
864
865 u16 continuous_tiles_count = 1;
866
867 bool makes_face = false;
868 v3s16 p_corrected;
869 v3s16 face_dir_corrected;
870 u16 lights[4] = {0, 0, 0, 0};
871 u8 waving = 0;
872 TileSpec tile;
873
874 // Get info of first tile
875 getTileInfo(data, p, face_dir,
876 makes_face, p_corrected, face_dir_corrected,
877 lights, waving, tile);
878
879 // Unroll this variable which has a significant build cost
880 TileSpec next_tile;
881 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
882 // If tiling can be done, this is set to false in the next step
883 bool next_is_different = true;
884
885 bool next_makes_face = false;
886 v3s16 next_p_corrected;
887 v3s16 next_face_dir_corrected;
888 u16 next_lights[4] = {0, 0, 0, 0};
889
890 // If at last position, there is nothing to compare to and
891 // the face must be drawn anyway
892 if (j != MAP_BLOCKSIZE - 1) {
893 p += translate_dir;
894
895 getTileInfo(data, p, face_dir,
896 next_makes_face, next_p_corrected,
897 next_face_dir_corrected, next_lights,
898 waving,
899 next_tile);
900
901 if (next_makes_face == makes_face
902 && next_p_corrected == p_corrected + translate_dir
903 && next_face_dir_corrected == face_dir_corrected
904 && memcmp(next_lights, lights, sizeof(lights)) == 0
905 // Don't apply fast faces to waving water.
906 && (waving != 3 || !waving_liquids)
907 && next_tile.isTileable(tile)) {
908 next_is_different = false;
909 continuous_tiles_count++;
910 }
911 }
912 if (next_is_different) {
913 /*
914 Create a face if there should be one
915 */
916 if (makes_face) {
917 // Floating point conversion of the position vector
918 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
919 // Center point of face (kind of)
920 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
921 * translate_dir_f;
922 v3f scale(1, 1, 1);
923
924 if (translate_dir.X != 0)
925 scale.X = continuous_tiles_count;
926 if (translate_dir.Y != 0)
927 scale.Y = continuous_tiles_count;
928 if (translate_dir.Z != 0)
929 scale.Z = continuous_tiles_count;
930
931 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
932 pf, sp, face_dir_corrected, scale, dest);
933 g_profiler->avg("Meshgen: Tiles per face [#]", continuous_tiles_count);
934 }
935
936 continuous_tiles_count = 1;
937 }
938
939 makes_face = next_makes_face;
940 p_corrected = next_p_corrected;
941 face_dir_corrected = next_face_dir_corrected;
942 memcpy(lights, next_lights, sizeof(lights));
943 if (next_is_different)
944 tile = std::move(next_tile); // faster than copy
945 }
946 }
947
updateAllFastFaceRows(MeshMakeData * data,std::vector<FastFace> & dest)948 static void updateAllFastFaceRows(MeshMakeData *data,
949 std::vector<FastFace> &dest)
950 {
951 /*
952 Go through every y,z and get top(y+) faces in rows of x+
953 */
954 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
955 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
956 updateFastFaceRow(data,
957 v3s16(0, y, z),
958 v3s16(1, 0, 0), //dir
959 v3f (1, 0, 0),
960 v3s16(0, 1, 0), //face dir
961 dest);
962
963 /*
964 Go through every x,y and get right(x+) faces in rows of z+
965 */
966 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
967 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
968 updateFastFaceRow(data,
969 v3s16(x, y, 0),
970 v3s16(0, 0, 1), //dir
971 v3f (0, 0, 1),
972 v3s16(1, 0, 0), //face dir
973 dest);
974
975 /*
976 Go through every y,z and get back(z+) faces in rows of x+
977 */
978 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
979 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
980 updateFastFaceRow(data,
981 v3s16(0, y, z),
982 v3s16(1, 0, 0), //dir
983 v3f (1, 0, 0),
984 v3s16(0, 0, 1), //face dir
985 dest);
986 }
987
applyTileColor(PreMeshBuffer & pmb)988 static void applyTileColor(PreMeshBuffer &pmb)
989 {
990 video::SColor tc = pmb.layer.color;
991 if (tc == video::SColor(0xFFFFFFFF))
992 return;
993 for (video::S3DVertex &vertex : pmb.vertices) {
994 video::SColor *c = &vertex.Color;
995 c->set(c->getAlpha(),
996 c->getRed() * tc.getRed() / 255,
997 c->getGreen() * tc.getGreen() / 255,
998 c->getBlue() * tc.getBlue() / 255);
999 }
1000 }
1001
1002 /*
1003 MapBlockMesh
1004 */
1005
MapBlockMesh(MeshMakeData * data,v3s16 camera_offset)1006 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1007 m_minimap_mapblock(NULL),
1008 m_tsrc(data->m_client->getTextureSource()),
1009 m_shdrsrc(data->m_client->getShaderSource()),
1010 m_animation_force_timer(0), // force initial animation
1011 m_last_crack(-1),
1012 m_last_daynight_ratio((u32) -1)
1013 {
1014 for (auto &m : m_mesh)
1015 m = new scene::SMesh();
1016 m_enable_shaders = data->m_use_shaders;
1017 m_enable_vbo = g_settings->getBool("enable_vbo");
1018
1019 if (data->m_client->getMinimap()) {
1020 m_minimap_mapblock = new MinimapMapblock;
1021 m_minimap_mapblock->getMinimapNodes(
1022 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1023 }
1024
1025 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1026 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1027 //TimeTaker timer1("MapBlockMesh()");
1028
1029 std::vector<FastFace> fastfaces_new;
1030 fastfaces_new.reserve(512);
1031
1032 /*
1033 We are including the faces of the trailing edges of the block.
1034 This means that when something changes, the caller must
1035 also update the meshes of the blocks at the leading edges.
1036
1037 NOTE: This is the slowest part of this method.
1038 */
1039 {
1040 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1041 //TimeTaker timer2("updateAllFastFaceRows()");
1042 updateAllFastFaceRows(data, fastfaces_new);
1043 }
1044 // End of slow part
1045
1046 /*
1047 Convert FastFaces to MeshCollector
1048 */
1049
1050 MeshCollector collector;
1051
1052 {
1053 // avg 0ms (100ms spikes when loading textures the first time)
1054 // (NOTE: probably outdated)
1055 //TimeTaker timer2("MeshCollector building");
1056
1057 for (const FastFace &f : fastfaces_new) {
1058 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1059 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1060 const u16 *indices_p =
1061 f.vertex_0_2_connected ? indices : indices_alternate;
1062 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1063 }
1064 }
1065
1066 /*
1067 Add special graphics:
1068 - torches
1069 - flowing water
1070 - fences
1071 - whatever
1072 */
1073
1074 {
1075 MapblockMeshGenerator generator(data, &collector);
1076 generator.generate();
1077 }
1078
1079 /*
1080 Convert MeshCollector to SMesh
1081 */
1082
1083 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1084 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1085 {
1086 PreMeshBuffer &p = collector.prebuffers[layer][i];
1087
1088 applyTileColor(p);
1089
1090 // Generate animation data
1091 // - Cracks
1092 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1093 // Find the texture name plus ^[crack:N:
1094 std::ostringstream os(std::ios::binary);
1095 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1096 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1097 os << "o"; // use ^[cracko
1098 u8 tiles = p.layer.scale;
1099 if (tiles > 1)
1100 os << ":" << (u32)tiles;
1101 os << ":" << (u32)p.layer.animation_frame_count << ":";
1102 m_crack_materials.insert(std::make_pair(
1103 std::pair<u8, u32>(layer, i), os.str()));
1104 // Replace tile texture with the cracked one
1105 p.layer.texture = m_tsrc->getTextureForMesh(
1106 os.str() + "0",
1107 &p.layer.texture_id);
1108 }
1109 // - Texture animation
1110 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1111 // Add to MapBlockMesh in order to animate these tiles
1112 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1113 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1114 if (g_settings->getBool(
1115 "desynchronize_mapblock_texture_animation")) {
1116 // Get starting position from noise
1117 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1118 100000 * (2.0 + noise3d(
1119 data->m_blockpos.X, data->m_blockpos.Y,
1120 data->m_blockpos.Z, 0));
1121 } else {
1122 // Play all synchronized
1123 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1124 }
1125 // Replace tile texture with the first animation frame
1126 p.layer.texture = (*p.layer.frames)[0].texture;
1127 }
1128
1129 if (!m_enable_shaders) {
1130 // Extract colors for day-night animation
1131 // Dummy sunlight to handle non-sunlit areas
1132 video::SColorf sunlight;
1133 get_sunlight_color(&sunlight, 0);
1134 u32 vertex_count = p.vertices.size();
1135 for (u32 j = 0; j < vertex_count; j++) {
1136 video::SColor *vc = &p.vertices[j].Color;
1137 video::SColor copy = *vc;
1138 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1139 final_color_blend(vc, copy, sunlight); // Finalize color
1140 else // Record color to animate
1141 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1142
1143 // The sunlight ratio has been stored,
1144 // delete alpha (for the final rendering).
1145 vc->setAlpha(255);
1146 }
1147 }
1148
1149 // Create material
1150 video::SMaterial material;
1151 material.setFlag(video::EMF_LIGHTING, false);
1152 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1153 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1154 material.setFlag(video::EMF_FOG_ENABLE, true);
1155 material.setTexture(0, p.layer.texture);
1156
1157 if (m_enable_shaders) {
1158 material.MaterialType = m_shdrsrc->getShaderInfo(
1159 p.layer.shader_id).material;
1160 p.layer.applyMaterialOptionsWithShaders(material);
1161 if (p.layer.normal_texture)
1162 material.setTexture(1, p.layer.normal_texture);
1163 material.setTexture(2, p.layer.flags_texture);
1164 } else {
1165 p.layer.applyMaterialOptions(material);
1166 }
1167
1168 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1169
1170 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1171 buf->Material = material;
1172 buf->append(&p.vertices[0], p.vertices.size(),
1173 &p.indices[0], p.indices.size());
1174 mesh->addMeshBuffer(buf);
1175 buf->drop();
1176 }
1177
1178 if (m_mesh[layer]) {
1179 // Use VBO for mesh (this just would set this for ever buffer)
1180 if (m_enable_vbo)
1181 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1182 }
1183 }
1184
1185 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1186
1187 // Check if animation is required for this mesh
1188 m_has_animation =
1189 !m_crack_materials.empty() ||
1190 !m_daynight_diffs.empty() ||
1191 !m_animation_tiles.empty();
1192 }
1193
~MapBlockMesh()1194 MapBlockMesh::~MapBlockMesh()
1195 {
1196 for (scene::IMesh *m : m_mesh) {
1197 if (m_enable_vbo) {
1198 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1199 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1200 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1201 }
1202 }
1203 m->drop();
1204 }
1205 delete m_minimap_mapblock;
1206 }
1207
animate(bool faraway,float time,int crack,u32 daynight_ratio)1208 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1209 u32 daynight_ratio)
1210 {
1211 if (!m_has_animation) {
1212 m_animation_force_timer = 100000;
1213 return false;
1214 }
1215
1216 m_animation_force_timer = myrand_range(5, 100);
1217
1218 // Cracks
1219 if (crack != m_last_crack) {
1220 for (auto &crack_material : m_crack_materials) {
1221 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1222 getMeshBuffer(crack_material.first.second);
1223 std::string basename = crack_material.second;
1224
1225 // Create new texture name from original
1226 std::ostringstream os;
1227 os << basename << crack;
1228 u32 new_texture_id = 0;
1229 video::ITexture *new_texture =
1230 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1231 buf->getMaterial().setTexture(0, new_texture);
1232
1233 // If the current material is also animated,
1234 // update animation info
1235 auto anim_iter = m_animation_tiles.find(crack_material.first);
1236 if (anim_iter != m_animation_tiles.end()) {
1237 TileLayer &tile = anim_iter->second;
1238 tile.texture = new_texture;
1239 tile.texture_id = new_texture_id;
1240 // force animation update
1241 m_animation_frames[crack_material.first] = -1;
1242 }
1243 }
1244
1245 m_last_crack = crack;
1246 }
1247
1248 // Texture animation
1249 for (auto &animation_tile : m_animation_tiles) {
1250 const TileLayer &tile = animation_tile.second;
1251 // Figure out current frame
1252 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1253 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1254 + frameoffset) % tile.animation_frame_count;
1255 // If frame doesn't change, skip
1256 if (frame == m_animation_frames[animation_tile.first])
1257 continue;
1258
1259 m_animation_frames[animation_tile.first] = frame;
1260
1261 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1262 getMeshBuffer(animation_tile.first.second);
1263
1264 const FrameSpec &animation_frame = (*tile.frames)[frame];
1265 buf->getMaterial().setTexture(0, animation_frame.texture);
1266 if (m_enable_shaders) {
1267 if (animation_frame.normal_texture)
1268 buf->getMaterial().setTexture(1,
1269 animation_frame.normal_texture);
1270 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1271 }
1272 }
1273
1274 // Day-night transition
1275 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1276 // Force reload mesh to VBO
1277 if (m_enable_vbo)
1278 for (scene::IMesh *m : m_mesh)
1279 m->setDirty();
1280 video::SColorf day_color;
1281 get_sunlight_color(&day_color, daynight_ratio);
1282
1283 for (auto &daynight_diff : m_daynight_diffs) {
1284 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1285 getMeshBuffer(daynight_diff.first.second);
1286 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1287 for (const auto &j : daynight_diff.second)
1288 final_color_blend(&(vertices[j.first].Color), j.second,
1289 day_color);
1290 }
1291 m_last_daynight_ratio = daynight_ratio;
1292 }
1293
1294 return true;
1295 }
1296
encode_light(u16 light,u8 emissive_light)1297 video::SColor encode_light(u16 light, u8 emissive_light)
1298 {
1299 // Get components
1300 u32 day = (light & 0xff);
1301 u32 night = (light >> 8);
1302 // Add emissive light
1303 night += emissive_light * 2.5f;
1304 if (night > 255)
1305 night = 255;
1306 // Since we don't know if the day light is sunlight or
1307 // artificial light, assume it is artificial when the night
1308 // light bank is also lit.
1309 if (day < night)
1310 day = 0;
1311 else
1312 day = day - night;
1313 u32 sum = day + night;
1314 // Ratio of sunlight:
1315 u32 r;
1316 if (sum > 0)
1317 r = day * 255 / sum;
1318 else
1319 r = 0;
1320 // Average light:
1321 float b = (day + night) / 2;
1322 return video::SColor(r, b, b, b);
1323 }
1324