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, bool node_solid)
201 INodeDefManager *ndef = data->m_client->ndef();
203 u16 ambient_occlusion = 0;
205 u8 light_source_max = 0;
209 auto add_node = [&] (int i) -> const ContentFeatures& {
210 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p + dirs[i]);
211 const ContentFeatures &f = ndef->get(n);
212 if (f.light_source > light_source_max)
213 light_source_max = f.light_source;
214 // Check f.solidness because fast-style leaves look better this way
215 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
216 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
217 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
226 ambient_occlusion = 3;
227 bool corner_obstructed = true;
228 for (int i = 0; i < 2; ++i) {
229 if (add_node(i).light_propagates)
230 corner_obstructed = false;
234 if (corner_obstructed)
239 std::array<bool, 4> obstructed = {{ 1, 1, 1, 1 }};
241 bool opaque1 = !add_node(1).light_propagates;
242 bool opaque2 = !add_node(2).light_propagates;
243 bool opaque3 = !add_node(3).light_propagates;
244 obstructed[0] = opaque1 && opaque2;
245 obstructed[1] = opaque1 && opaque3;
246 obstructed[2] = opaque2 && opaque3;
247 for (int k = 0; k < 4; ++k) {
250 else if (add_node(k + 4).light_propagates)
251 obstructed[3] = false;
255 if (light_count == 0) {
256 light_day = light_night = 0;
258 light_day /= light_count;
259 light_night /= light_count;
262 // Boost brightness around light sources
263 bool skip_ambient_occlusion_day = false;
264 if (decode_light(light_source_max) >= light_day) {
265 light_day = decode_light(light_source_max);
266 skip_ambient_occlusion_day = true;
269 bool skip_ambient_occlusion_night = false;
270 if(decode_light(light_source_max) >= light_night) {
271 light_night = decode_light(light_source_max);
272 skip_ambient_occlusion_night = true;
275 if (ambient_occlusion > 4) {
276 static thread_local const float ao_gamma = rangelim(
277 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
279 // Table of gamma space multiply factors.
280 static const float light_amount[3] = {
281 powf(0.75, 1.0 / ao_gamma),
282 powf(0.5, 1.0 / ao_gamma),
283 powf(0.25, 1.0 / ao_gamma)
286 //calculate table index for gamma space multiplier
287 ambient_occlusion -= 5;
289 if (!skip_ambient_occlusion_day)
290 light_day = rangelim(core::round32(
291 light_day * light_amount[ambient_occlusion]), 0, 255);
292 if (!skip_ambient_occlusion_night)
293 light_night = rangelim(core::round32(
294 light_night * light_amount[ambient_occlusion]), 0, 255);
297 return light_day | (light_night << 8);
301 Calculate smooth lighting at the given corner of p.
303 Node at p is solid, and thus the lighting is face-dependent.
305 u16 getSmoothLightSolid(const v3s16 &p, const v3s16 &face_dir, const v3s16 &corner, MeshMakeData *data)
307 v3s16 neighbor_offset1, neighbor_offset2;
310 * face_dir, neighbor_offset1 and neighbor_offset2 define an
311 * orthonormal basis which is used to define the offsets of the 8
312 * surrounding nodes and to differentiate the "distance" (by going only
313 * along directly neighboring nodes) relative to the node at p.
314 * Apart from the node at p, only the 4 nodes which contain face_dir
315 * can contribute light.
317 if (face_dir.X != 0) {
318 neighbor_offset1 = v3s16(0, corner.Y, 0);
319 neighbor_offset2 = v3s16(0, 0, corner.Z);
320 } else if (face_dir.Y != 0) {
321 neighbor_offset1 = v3s16(0, 0, corner.Z);
322 neighbor_offset2 = v3s16(corner.X, 0, 0);
323 } else if (face_dir.Z != 0) {
324 neighbor_offset1 = v3s16(corner.X,0,0);
325 neighbor_offset2 = v3s16(0,corner.Y,0);
328 const std::array<v3s16,8> dirs = {{
329 // Always shine light
330 neighbor_offset1 + face_dir,
331 neighbor_offset2 + face_dir,
336 neighbor_offset1 + neighbor_offset2 + face_dir,
338 // Do not shine light, only for ambient occlusion
341 neighbor_offset1 + neighbor_offset2
343 return getSmoothLightCombined(p, dirs, data, true);
347 Calculate smooth lighting at the given corner of p.
349 Node at p is not solid, and the lighting is not face-dependent.
351 u16 getSmoothLightTransparent(const v3s16 &p, const v3s16 &corner, MeshMakeData *data)
353 const std::array<v3s16,8> dirs = {{
354 // Always shine light
361 v3s16(corner.X,corner.Y,0),
362 v3s16(corner.X,0,corner.Z),
363 v3s16(0,corner.Y,corner.Z),
364 v3s16(corner.X,corner.Y,corner.Z)
366 return getSmoothLightCombined(p, dirs, data, false);
369 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
370 f32 rg = daynight_ratio / 1000.0f - 0.04f;
371 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
377 void final_color_blend(video::SColor *result,
378 u16 light, u32 daynight_ratio)
380 video::SColorf dayLight;
381 get_sunlight_color(&dayLight, daynight_ratio);
382 final_color_blend(result,
383 encode_light(light, 0), dayLight);
386 void final_color_blend(video::SColor *result,
387 const video::SColor &data, const video::SColorf &dayLight)
389 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
391 video::SColorf c(data);
394 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
395 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
396 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
398 // Emphase blue a bit in darker places
399 // Each entry of this array represents a range of 8 blue levels
400 static const u8 emphase_blue_when_dark[32] = {
401 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
402 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
405 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
406 0, 255) / 8] / 255.0f;
408 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
409 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
410 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
414 Mesh generation helpers
418 vertex_dirs: v3s16[4]
420 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
423 If looked from outside the node towards the face, the corners are:
429 if (dir == v3s16(0, 0, 1)) {
430 // If looking towards z+, this is the face that is behind
431 // the center point, facing towards z+.
432 vertex_dirs[0] = v3s16(-1,-1, 1);
433 vertex_dirs[1] = v3s16( 1,-1, 1);
434 vertex_dirs[2] = v3s16( 1, 1, 1);
435 vertex_dirs[3] = v3s16(-1, 1, 1);
436 } else if (dir == v3s16(0, 0, -1)) {
438 vertex_dirs[0] = v3s16( 1,-1,-1);
439 vertex_dirs[1] = v3s16(-1,-1,-1);
440 vertex_dirs[2] = v3s16(-1, 1,-1);
441 vertex_dirs[3] = v3s16( 1, 1,-1);
442 } else if (dir == v3s16(1, 0, 0)) {
444 vertex_dirs[0] = v3s16( 1,-1, 1);
445 vertex_dirs[1] = v3s16( 1,-1,-1);
446 vertex_dirs[2] = v3s16( 1, 1,-1);
447 vertex_dirs[3] = v3s16( 1, 1, 1);
448 } else if (dir == v3s16(-1, 0, 0)) {
450 vertex_dirs[0] = v3s16(-1,-1,-1);
451 vertex_dirs[1] = v3s16(-1,-1, 1);
452 vertex_dirs[2] = v3s16(-1, 1, 1);
453 vertex_dirs[3] = v3s16(-1, 1,-1);
454 } else if (dir == v3s16(0, 1, 0)) {
455 // faces towards Y+ (assume Z- as "down" in texture)
456 vertex_dirs[0] = v3s16( 1, 1,-1);
457 vertex_dirs[1] = v3s16(-1, 1,-1);
458 vertex_dirs[2] = v3s16(-1, 1, 1);
459 vertex_dirs[3] = v3s16( 1, 1, 1);
460 } else if (dir == v3s16(0, -1, 0)) {
461 // faces towards Y- (assume Z+ as "down" in texture)
462 vertex_dirs[0] = v3s16( 1,-1, 1);
463 vertex_dirs[1] = v3s16(-1,-1, 1);
464 vertex_dirs[2] = v3s16(-1,-1,-1);
465 vertex_dirs[3] = v3s16( 1,-1,-1);
469 static void getNodeTextureCoords(v3f base, const v3f &scale, v3s16 dir, float *u, float *v)
471 if (dir.X > 0 || dir.Y > 0 || dir.Z < 0)
473 if (dir == v3s16(0,0,1)) {
476 } else if (dir == v3s16(0,0,-1)) {
479 } else if (dir == v3s16(1,0,0)) {
482 } else if (dir == v3s16(-1,0,0)) {
485 } else if (dir == v3s16(0,1,0)) {
488 } else if (dir == v3s16(0,-1,0)) {
497 video::S3DVertex vertices[4]; // Precalculated vertices
499 * The face is divided into two triangles. If this is true,
500 * vertices 0 and 2 are connected, othervise vertices 1 and 3
503 bool vertex_0_2_connected;
508 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
509 v3f tp, v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
511 // Position is at the center of the cube.
520 v3s16 vertex_dirs[4];
521 getNodeVertexDirs(dir, vertex_dirs);
522 if (tile.world_aligned)
523 getNodeTextureCoords(tp, scale, dir, &x0, &y0);
527 switch (tile.rotation) {
532 vertex_dirs[0] = vertex_dirs[3];
533 vertex_dirs[3] = vertex_dirs[2];
534 vertex_dirs[2] = vertex_dirs[1];
544 vertex_dirs[0] = vertex_dirs[2];
547 vertex_dirs[1] = vertex_dirs[3];
558 vertex_dirs[0] = vertex_dirs[1];
559 vertex_dirs[1] = vertex_dirs[2];
560 vertex_dirs[2] = vertex_dirs[3];
570 vertex_dirs[0] = vertex_dirs[3];
571 vertex_dirs[3] = vertex_dirs[2];
572 vertex_dirs[2] = vertex_dirs[1];
584 vertex_dirs[0] = vertex_dirs[1];
585 vertex_dirs[1] = vertex_dirs[2];
586 vertex_dirs[2] = vertex_dirs[3];
598 vertex_dirs[0] = vertex_dirs[3];
599 vertex_dirs[3] = vertex_dirs[2];
600 vertex_dirs[2] = vertex_dirs[1];
612 vertex_dirs[0] = vertex_dirs[1];
613 vertex_dirs[1] = vertex_dirs[2];
614 vertex_dirs[2] = vertex_dirs[3];
636 for (u16 i = 0; i < 4; i++) {
638 BS / 2 * vertex_dirs[i].X,
639 BS / 2 * vertex_dirs[i].Y,
640 BS / 2 * vertex_dirs[i].Z
644 for (v3f &vpos : vertex_pos) {
651 f32 abs_scale = 1.0f;
652 if (scale.X < 0.999f || scale.X > 1.001f) abs_scale = scale.X;
653 else if (scale.Y < 0.999f || scale.Y > 1.001f) abs_scale = scale.Y;
654 else if (scale.Z < 0.999f || scale.Z > 1.001f) abs_scale = scale.Z;
656 v3f normal(dir.X, dir.Y, dir.Z);
658 u16 li[4] = { li0, li1, li2, li3 };
662 for (u8 i = 0; i < 4; i++) {
664 night[i] = li[i] & 0xFF;
667 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
668 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
671 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
672 core::vector2d<f32>(x0, y0 + h),
673 core::vector2d<f32>(x0, y0),
674 core::vector2d<f32>(x0 + w * abs_scale, y0) };
676 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
677 const TileLayer *layer = &tile.layers[layernum];
678 if (layer->texture_id == 0)
681 // equivalent to dest.push_back(FastFace()) but faster
683 FastFace& face = *dest.rbegin();
685 for (u8 i = 0; i < 4; i++) {
686 video::SColor c = encode_light(li[i], tile.emissive_light);
687 if (!tile.emissive_light)
688 applyFacesShading(c, normal);
690 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
694 Revert triangles for nicer looking gradient if the
695 brightness of vertices 1 and 3 differ less than
696 the brightness of vertices 0 and 2.
698 face.vertex_0_2_connected = vertex_0_2_connected;
701 face.layernum = layernum;
703 face.world_aligned = tile.world_aligned;
708 Nodes make a face if contents differ and solidness differs.
711 1: Face uses m1's content
712 2: Face uses m2's content
713 equivalent: Whether the blocks share the same face (eg. water and glass)
715 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
717 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
718 INodeDefManager *ndef)
722 if (m1 == m2 || m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
725 const ContentFeatures &f1 = ndef->get(m1);
726 const ContentFeatures &f2 = ndef->get(m2);
728 // Contents don't differ for different forms of same liquid
729 if (f1.sameLiquid(f2))
732 u8 c1 = f1.solidness;
733 u8 c2 = f2.solidness;
739 c1 = f1.visual_solidness;
741 c2 = f2.visual_solidness;
745 // If same solidness, liquid takes precense
759 Gets nth node tile (0 <= n <= 5).
761 void getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
763 INodeDefManager *ndef = data->m_client->ndef();
764 const ContentFeatures &f = ndef->get(mn);
765 tile = f.tiles[tileindex];
766 bool has_crack = p == data->m_crack_pos_relative;
767 for (TileLayer &layer : tile.layers) {
768 if (layer.texture_id == 0)
770 if (!layer.has_color)
771 mn.getColor(f, &(layer.color));
772 // Apply temporary crack
774 layer.material_flags |= MATERIAL_FLAG_CRACK;
779 Gets node tile given a face direction.
781 void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &tile)
783 INodeDefManager *ndef = data->m_client->ndef();
785 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
786 // (0,0,1), (0,0,-1) or (0,0,0)
787 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
789 // Convert direction to single integer for table lookup
794 // 4 = invalid, treat as (0,0,0)
798 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7) * 2;
800 // Get rotation for things like chests
801 u8 facedir = mn.getFaceDir(ndef);
803 static const u16 dir_to_tile[24 * 16] =
805 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
806 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
807 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
808 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
809 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
811 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
812 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
813 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
814 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
816 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
817 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
818 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
819 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
821 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
822 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
823 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
824 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
826 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
827 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
828 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
829 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
831 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
832 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
833 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
834 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
837 u16 tile_index = facedir * 16 + dir_i;
838 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
839 tile.rotation = tile.world_aligned ? 0 : dir_to_tile[tile_index + 1];
842 static void getTileInfo(
846 const v3s16 &face_dir,
850 v3s16 &face_dir_corrected,
855 VoxelManipulator &vmanip = data->m_vmanip;
856 INodeDefManager *ndef = data->m_client->ndef();
857 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
859 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
861 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
862 if (n0.getContent() == CONTENT_IGNORE) {
867 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
869 if (n1.getContent() == CONTENT_IGNORE) {
875 bool equivalent = false;
876 u8 mf = face_contents(n0.getContent(), n1.getContent(),
890 face_dir_corrected = face_dir;
893 p_corrected = p + face_dir;
894 face_dir_corrected = -face_dir;
897 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
898 const ContentFeatures &f = ndef->get(n);
899 tile.emissive_light = f.light_source;
901 // eg. water and glass
903 for (TileLayer &layer : tile.layers)
904 layer.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
907 if (!data->m_smooth_lighting) {
908 lights[0] = lights[1] = lights[2] = lights[3] =
909 getFaceLight(n0, n1, face_dir, ndef);
911 v3s16 vertex_dirs[4];
912 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
914 v3s16 light_p = blockpos_nodes + p_corrected;
915 for (u16 i = 0; i < 4; i++)
916 lights[i] = getSmoothLightSolid(light_p, face_dir_corrected, vertex_dirs[i], data);
922 translate_dir: unit vector with only one of x, y or z
923 face_dir: unit vector with only one of x, y or z
925 static void updateFastFaceRow(
927 const v3s16 &&startpos,
929 const v3f &&translate_dir_f,
930 const v3s16 &&face_dir,
931 std::vector<FastFace> &dest)
935 u16 continuous_tiles_count = 1;
937 bool makes_face = false;
939 v3s16 face_dir_corrected;
940 u16 lights[4] = {0, 0, 0, 0};
942 getTileInfo(data, p, face_dir,
943 makes_face, p_corrected, face_dir_corrected,
946 // Unroll this variable which has a significant build cost
948 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
949 // If tiling can be done, this is set to false in the next step
950 bool next_is_different = true;
954 bool next_makes_face = false;
955 v3s16 next_p_corrected;
956 v3s16 next_face_dir_corrected;
957 u16 next_lights[4] = {0, 0, 0, 0};
959 // If at last position, there is nothing to compare to and
960 // the face must be drawn anyway
961 if (j != MAP_BLOCKSIZE - 1) {
962 p_next = p + translate_dir;
964 getTileInfo(data, p_next, face_dir,
965 next_makes_face, next_p_corrected,
966 next_face_dir_corrected, next_lights,
969 if (next_makes_face == makes_face
970 && next_p_corrected == p_corrected + translate_dir
971 && next_face_dir_corrected == face_dir_corrected
972 && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
973 && next_tile.isTileable(tile)) {
974 next_is_different = false;
975 continuous_tiles_count++;
978 if (next_is_different) {
980 Create a face if there should be one
983 // Floating point conversion of the position vector
984 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
985 // Center point of face (kind of)
986 v3f sp = pf - ((f32)continuous_tiles_count * 0.5f - 0.5f)
990 if (translate_dir.X != 0)
991 scale.X = continuous_tiles_count;
992 if (translate_dir.Y != 0)
993 scale.Y = continuous_tiles_count;
994 if (translate_dir.Z != 0)
995 scale.Z = continuous_tiles_count;
997 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
998 pf, sp, face_dir_corrected, scale, dest);
1000 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
1001 for (int i = 1; i < continuous_tiles_count; i++)
1002 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
1005 continuous_tiles_count = 1;
1008 makes_face = next_makes_face;
1009 p_corrected = next_p_corrected;
1010 face_dir_corrected = next_face_dir_corrected;
1011 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
1012 if (next_is_different)
1018 static void updateAllFastFaceRows(MeshMakeData *data,
1019 std::vector<FastFace> &dest)
1022 Go through every y,z and get top(y+) faces in rows of x+
1024 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1025 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1026 updateFastFaceRow(data,
1028 v3s16(1, 0, 0), //dir
1030 v3s16(0, 1, 0), //face dir
1034 Go through every x,y and get right(x+) faces in rows of z+
1036 for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
1037 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1038 updateFastFaceRow(data,
1040 v3s16(0, 0, 1), //dir
1042 v3s16(1, 0, 0), //face dir
1046 Go through every y,z and get back(z+) faces in rows of x+
1048 for (s16 z = 0; z < MAP_BLOCKSIZE; z++)
1049 for (s16 y = 0; y < MAP_BLOCKSIZE; y++)
1050 updateFastFaceRow(data,
1052 v3s16(1, 0, 0), //dir
1054 v3s16(0, 0, 1), //face dir
1062 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1063 m_minimap_mapblock(NULL),
1064 m_tsrc(data->m_client->getTextureSource()),
1065 m_shdrsrc(data->m_client->getShaderSource()),
1066 m_animation_force_timer(0), // force initial animation
1068 m_last_daynight_ratio((u32) -1)
1070 for (auto &m : m_mesh)
1071 m = new scene::SMesh();
1072 m_enable_shaders = data->m_use_shaders;
1073 m_use_tangent_vertices = data->m_use_tangent_vertices;
1074 m_enable_vbo = g_settings->getBool("enable_vbo");
1076 if (g_settings->getBool("enable_minimap")) {
1077 m_minimap_mapblock = new MinimapMapblock;
1078 m_minimap_mapblock->getMinimapNodes(
1079 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1082 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1083 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1084 //TimeTaker timer1("MapBlockMesh()");
1086 std::vector<FastFace> fastfaces_new;
1087 fastfaces_new.reserve(512);
1090 We are including the faces of the trailing edges of the block.
1091 This means that when something changes, the caller must
1092 also update the meshes of the blocks at the leading edges.
1094 NOTE: This is the slowest part of this method.
1097 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1098 //TimeTaker timer2("updateAllFastFaceRows()");
1099 updateAllFastFaceRows(data, fastfaces_new);
1104 Convert FastFaces to MeshCollector
1107 MeshCollector collector(m_use_tangent_vertices);
1110 // avg 0ms (100ms spikes when loading textures the first time)
1111 // (NOTE: probably outdated)
1112 //TimeTaker timer2("MeshCollector building");
1114 for (const FastFace &f : fastfaces_new) {
1115 static const u16 indices[] = {0, 1, 2, 2, 3, 0};
1116 static const u16 indices_alternate[] = {0, 1, 3, 2, 3, 1};
1118 if (!f.layer.texture)
1121 const u16 *indices_p =
1122 f.vertex_0_2_connected ? indices : indices_alternate;
1124 collector.append(f.layer, f.vertices, 4, indices_p, 6,
1125 f.layernum, f.world_aligned);
1130 Add special graphics:
1138 MapblockMeshGenerator generator(data, &collector);
1139 generator.generate();
1142 collector.applyTileColors();
1145 Convert MeshCollector to SMesh
1148 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1149 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1151 PreMeshBuffer &p = collector.prebuffers[layer][i];
1153 // Generate animation data
1155 if (p.layer.material_flags & MATERIAL_FLAG_CRACK) {
1156 // Find the texture name plus ^[crack:N:
1157 std::ostringstream os(std::ios::binary);
1158 os << m_tsrc->getTextureName(p.layer.texture_id) << "^[crack";
1159 if (p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1160 os << "o"; // use ^[cracko
1161 u8 tiles = p.layer.scale;
1163 os << ":" << (u32)tiles;
1164 os << ":" << (u32)p.layer.animation_frame_count << ":";
1165 m_crack_materials.insert(std::make_pair(
1166 std::pair<u8, u32>(layer, i), os.str()));
1167 // Replace tile texture with the cracked one
1168 p.layer.texture = m_tsrc->getTextureForMesh(
1170 &p.layer.texture_id);
1172 // - Texture animation
1173 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1174 // Add to MapBlockMesh in order to animate these tiles
1175 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1176 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1177 if (g_settings->getBool(
1178 "desynchronize_mapblock_texture_animation")) {
1179 // Get starting position from noise
1180 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] =
1181 100000 * (2.0 + noise3d(
1182 data->m_blockpos.X, data->m_blockpos.Y,
1183 data->m_blockpos.Z, 0));
1185 // Play all synchronized
1186 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1188 // Replace tile texture with the first animation frame
1189 p.layer.texture = (*p.layer.frames)[0].texture;
1192 if (!m_enable_shaders) {
1193 // Extract colors for day-night animation
1194 // Dummy sunlight to handle non-sunlit areas
1195 video::SColorf sunlight;
1196 get_sunlight_color(&sunlight, 0);
1197 u32 vertex_count = m_use_tangent_vertices ?
1198 p.tangent_vertices.size() : p.vertices.size();
1199 for (u32 j = 0; j < vertex_count; j++) {
1201 if (m_use_tangent_vertices) {
1202 vc = &p.tangent_vertices[j].Color;
1204 vc = &p.vertices[j].Color;
1206 video::SColor copy(*vc);
1207 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1208 final_color_blend(vc, copy, sunlight); // Finalize color
1209 else // Record color to animate
1210 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1212 // The sunlight ratio has been stored,
1213 // delete alpha (for the final rendering).
1219 video::SMaterial material;
1220 material.setFlag(video::EMF_LIGHTING, false);
1221 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1222 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1223 material.setFlag(video::EMF_FOG_ENABLE, true);
1224 material.setTexture(0, p.layer.texture);
1226 if (m_enable_shaders) {
1227 material.MaterialType = m_shdrsrc->getShaderInfo(
1228 p.layer.shader_id).material;
1229 p.layer.applyMaterialOptionsWithShaders(material);
1230 if (p.layer.normal_texture)
1231 material.setTexture(1, p.layer.normal_texture);
1232 material.setTexture(2, p.layer.flags_texture);
1234 p.layer.applyMaterialOptions(material);
1237 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1239 // Create meshbuffer, add to mesh
1240 if (m_use_tangent_vertices) {
1241 scene::SMeshBufferTangents *buf =
1242 new scene::SMeshBufferTangents();
1244 buf->Material = material;
1246 mesh->addMeshBuffer(buf);
1249 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1250 &p.indices[0], p.indices.size());
1252 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1254 buf->Material = material;
1256 mesh->addMeshBuffer(buf);
1259 buf->append(&p.vertices[0], p.vertices.size(),
1260 &p.indices[0], p.indices.size());
1265 Do some stuff to the mesh
1267 m_camera_offset = camera_offset;
1268 translateMesh(m_mesh[layer],
1269 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1271 if (m_use_tangent_vertices) {
1272 scene::IMeshManipulator* meshmanip =
1273 RenderingEngine::get_scene_manager()->getMeshManipulator();
1274 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1277 if (m_mesh[layer]) {
1279 // Usually 1-700 faces and 1-7 materials
1280 std::cout << "Updated MapBlock has " << fastfaces_new.size()
1281 << " faces and uses " << m_mesh[layer]->getMeshBufferCount()
1282 << " materials (meshbuffers)" << std::endl;
1285 // Use VBO for mesh (this just would set this for ever buffer)
1287 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1291 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1293 // Check if animation is required for this mesh
1295 !m_crack_materials.empty() ||
1296 !m_daynight_diffs.empty() ||
1297 !m_animation_tiles.empty();
1300 MapBlockMesh::~MapBlockMesh()
1302 for (scene::IMesh *m : m_mesh) {
1303 if (m_enable_vbo && m)
1304 for (u32 i = 0; i < m->getMeshBufferCount(); i++) {
1305 scene::IMeshBuffer *buf = m->getMeshBuffer(i);
1306 RenderingEngine::get_video_driver()->removeHardwareBuffer(buf);
1311 delete m_minimap_mapblock;
1314 bool MapBlockMesh::animate(bool faraway, float time, int crack,
1317 if (!m_has_animation) {
1318 m_animation_force_timer = 100000;
1322 m_animation_force_timer = myrand_range(5, 100);
1325 if (crack != m_last_crack) {
1326 for (auto &crack_material : m_crack_materials) {
1327 scene::IMeshBuffer *buf = m_mesh[crack_material.first.first]->
1328 getMeshBuffer(crack_material.first.second);
1329 std::string basename = crack_material.second;
1331 // Create new texture name from original
1332 std::ostringstream os;
1333 os << basename << crack;
1334 u32 new_texture_id = 0;
1335 video::ITexture *new_texture =
1336 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1337 buf->getMaterial().setTexture(0, new_texture);
1339 // If the current material is also animated,
1340 // update animation info
1341 auto anim_iter = m_animation_tiles.find(crack_material.first);
1342 if (anim_iter != m_animation_tiles.end()) {
1343 TileLayer &tile = anim_iter->second;
1344 tile.texture = new_texture;
1345 tile.texture_id = new_texture_id;
1346 // force animation update
1347 m_animation_frames[crack_material.first] = -1;
1351 m_last_crack = crack;
1354 // Texture animation
1355 for (auto &animation_tile : m_animation_tiles) {
1356 const TileLayer &tile = animation_tile.second;
1357 // Figure out current frame
1358 int frameoffset = m_animation_frame_offsets[animation_tile.first];
1359 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1360 + frameoffset) % tile.animation_frame_count;
1361 // If frame doesn't change, skip
1362 if (frame == m_animation_frames[animation_tile.first])
1365 m_animation_frames[animation_tile.first] = frame;
1367 scene::IMeshBuffer *buf = m_mesh[animation_tile.first.first]->
1368 getMeshBuffer(animation_tile.first.second);
1370 const FrameSpec &animation_frame = (*tile.frames)[frame];
1371 buf->getMaterial().setTexture(0, animation_frame.texture);
1372 if (m_enable_shaders) {
1373 if (animation_frame.normal_texture)
1374 buf->getMaterial().setTexture(1,
1375 animation_frame.normal_texture);
1376 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1380 // Day-night transition
1381 if (!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio)) {
1382 // Force reload mesh to VBO
1384 for (scene::IMesh *m : m_mesh)
1386 video::SColorf day_color;
1387 get_sunlight_color(&day_color, daynight_ratio);
1389 for (auto &daynight_diff : m_daynight_diffs) {
1390 scene::IMeshBuffer *buf = m_mesh[daynight_diff.first.first]->
1391 getMeshBuffer(daynight_diff.first.second);
1392 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1393 for (const auto &j : daynight_diff.second)
1394 final_color_blend(&(vertices[j.first].Color), j.second,
1397 m_last_daynight_ratio = daynight_ratio;
1403 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1405 if (camera_offset != m_camera_offset) {
1406 for (scene::IMesh *layer : m_mesh) {
1407 translateMesh(layer,
1408 intToFloat(m_camera_offset - camera_offset, BS));
1412 m_camera_offset = camera_offset;
1420 void MeshCollector::append(const TileSpec &tile,
1421 const video::S3DVertex *vertices, u32 numVertices,
1422 const u16 *indices, u32 numIndices)
1424 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1425 const TileLayer *layer = &tile.layers[layernum];
1426 if (layer->texture_id == 0)
1428 append(*layer, vertices, numVertices, indices, numIndices,
1429 layernum, tile.world_aligned);
1433 void MeshCollector::append(const TileLayer &layer,
1434 const video::S3DVertex *vertices, u32 numVertices,
1435 const u16 *indices, u32 numIndices, u8 layernum,
1438 if (numIndices > 65535) {
1439 dstream << "FIXME: MeshCollector::append() called with numIndices="
1440 << numIndices << " (limit 65535)" << std::endl;
1443 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1445 PreMeshBuffer *p = NULL;
1446 for (PreMeshBuffer &pp : *buffers) {
1447 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1456 buffers->push_back(pp);
1457 p = &(*buffers)[buffers->size() - 1];
1462 scale = 1.0 / layer.scale;
1465 if (m_use_tangent_vertices) {
1466 vertex_count = p->tangent_vertices.size();
1467 for (u32 i = 0; i < numVertices; i++) {
1469 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1470 vertices[i].Color, scale * vertices[i].TCoords);
1471 p->tangent_vertices.push_back(vert);
1474 vertex_count = p->vertices.size();
1475 for (u32 i = 0; i < numVertices; i++) {
1476 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1477 vertices[i].Color, scale * vertices[i].TCoords);
1479 p->vertices.push_back(vert);
1483 for (u32 i = 0; i < numIndices; i++) {
1484 u32 j = indices[i] + vertex_count;
1485 p->indices.push_back(j);
1490 MeshCollector - for meshnodes and converted drawtypes.
1493 void MeshCollector::append(const TileSpec &tile,
1494 const video::S3DVertex *vertices, u32 numVertices,
1495 const u16 *indices, u32 numIndices,
1496 v3f pos, video::SColor c, u8 light_source)
1498 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1499 const TileLayer *layer = &tile.layers[layernum];
1500 if (layer->texture_id == 0)
1502 append(*layer, vertices, numVertices, indices, numIndices, pos,
1503 c, light_source, layernum, tile.world_aligned);
1507 void MeshCollector::append(const TileLayer &layer,
1508 const video::S3DVertex *vertices, u32 numVertices,
1509 const u16 *indices, u32 numIndices,
1510 v3f pos, video::SColor c, u8 light_source, u8 layernum,
1513 if (numIndices > 65535) {
1514 dstream << "FIXME: MeshCollector::append() called with numIndices="
1515 << numIndices << " (limit 65535)" << std::endl;
1518 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1520 PreMeshBuffer *p = NULL;
1521 for (PreMeshBuffer &pp : *buffers) {
1522 if (pp.layer == layer && pp.indices.size() + numIndices <= 65535) {
1531 buffers->push_back(pp);
1532 p = &(*buffers)[buffers->size() - 1];
1537 scale = 1.0 / layer.scale;
1539 video::SColor original_c = c;
1541 if (m_use_tangent_vertices) {
1542 vertex_count = p->tangent_vertices.size();
1543 for (u32 i = 0; i < numVertices; i++) {
1544 if (!light_source) {
1546 applyFacesShading(c, vertices[i].Normal);
1548 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1549 vertices[i].Normal, c, scale * vertices[i].TCoords);
1550 p->tangent_vertices.push_back(vert);
1553 vertex_count = p->vertices.size();
1554 for (u32 i = 0; i < numVertices; i++) {
1555 if (!light_source) {
1557 applyFacesShading(c, vertices[i].Normal);
1559 video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
1560 scale * vertices[i].TCoords);
1561 p->vertices.push_back(vert);
1565 for (u32 i = 0; i < numIndices; i++) {
1566 u32 j = indices[i] + vertex_count;
1567 p->indices.push_back(j);
1571 void MeshCollector::applyTileColors()
1573 if (m_use_tangent_vertices)
1574 for (auto &prebuffer : prebuffers) {
1575 for (PreMeshBuffer &pmb : prebuffer) {
1576 video::SColor tc = pmb.layer.color;
1577 if (tc == video::SColor(0xFFFFFFFF))
1579 for (video::S3DVertexTangents &tangent_vertex : pmb.tangent_vertices) {
1580 video::SColor *c = &tangent_vertex.Color;
1581 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1582 c->getGreen() * tc.getGreen() / 255,
1583 c->getBlue() * tc.getBlue() / 255);
1588 for (auto &prebuffer : prebuffers) {
1589 for (PreMeshBuffer &pmb : prebuffer) {
1590 video::SColor tc = pmb.layer.color;
1591 if (tc == video::SColor(0xFFFFFFFF))
1593 for (video::S3DVertex &vertex : pmb.vertices) {
1594 video::SColor *c = &vertex.Color;
1595 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1596 c->getGreen() * tc.getGreen() / 255,
1597 c->getBlue() * tc.getBlue() / 255);
1603 video::SColor encode_light(u16 light, u8 emissive_light)
1606 u32 day = (light & 0xff);
1607 u32 night = (light >> 8);
1608 // Add emissive light
1609 night += emissive_light * 2.5f;
1612 // Since we don't know if the day light is sunlight or
1613 // artificial light, assume it is artificial when the night
1614 // light bank is also lit.
1619 u32 sum = day + night;
1620 // Ratio of sunlight:
1623 r = day * 255 / sum;
1627 float b = (day + night) / 2;
1628 return video::SColor(r, b, b, b);