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"
32 #include "util/directiontables.h"
33 #include <IMeshManipulator.h>
39 MeshMakeData::MeshMakeData(Client *client, bool use_shaders,
40 bool use_tangent_vertices):
42 m_blockpos(-1337,-1337,-1337),
43 m_crack_pos_relative(-1337, -1337, -1337),
44 m_smooth_lighting(false),
47 m_use_shaders(use_shaders),
48 m_use_tangent_vertices(use_tangent_vertices)
51 void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos)
53 m_blockpos = blockpos;
55 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
58 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
59 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
60 m_vmanip.addArea(voxel_area);
63 void MeshMakeData::fillBlockData(const v3s16 &block_offset, MapNode *data)
65 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
66 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
68 v3s16 bp = m_blockpos + block_offset;
69 v3s16 blockpos_nodes = bp * MAP_BLOCKSIZE;
70 m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size);
73 void MeshMakeData::fill(MapBlock *block)
75 fillBlockDataBegin(block->getPos());
77 fillBlockData(v3s16(0,0,0), block->getData());
79 // Get map for reading neigbhor blocks
80 Map *map = block->getParent();
82 for (u16 i=0; i<26; i++) {
83 const v3s16 &dir = g_26dirs[i];
84 v3s16 bp = m_blockpos + dir;
85 MapBlock *b = map->getBlockNoCreateNoEx(bp);
87 fillBlockData(dir, b->getData());
91 void MeshMakeData::fillSingleNode(MapNode *node)
93 m_blockpos = v3s16(0,0,0);
95 v3s16 blockpos_nodes = v3s16(0,0,0);
96 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
97 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
98 s32 volume = area.getVolume();
99 s32 our_node_index = area.index(1,1,1);
101 // Allocate this block + neighbors
103 m_vmanip.addArea(area);
106 MapNode *data = new MapNode[volume];
107 for(s32 i = 0; i < volume; i++)
109 if(i == our_node_index)
115 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
118 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
122 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
125 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
128 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
130 m_smooth_lighting = smooth_lighting;
134 Light and vertex color functions
138 Calculate non-smooth lighting at interior of node.
141 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
142 INodeDefManager *ndef)
144 u8 light = n.getLight(bank, ndef);
148 light = undiminish_light(light);
153 light = diminish_light(light);
157 return decode_light(light);
161 Calculate non-smooth lighting at interior of node.
164 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
166 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
167 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
168 return day | (night << 8);
172 Calculate non-smooth lighting at face of node.
175 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
176 v3s16 face_dir, INodeDefManager *ndef)
179 u8 l1 = n.getLight(bank, ndef);
180 u8 l2 = n2.getLight(bank, ndef);
186 // Boost light level for light sources
187 u8 light_source = MYMAX(ndef->get(n).light_source,
188 ndef->get(n2).light_source);
189 if(light_source > light)
190 light = light_source;
192 return decode_light(light);
196 Calculate non-smooth lighting at face of node.
199 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
201 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
202 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
203 return day | (night << 8);
207 Calculate smooth lighting at the XYZ- corner of p.
210 static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
212 static const v3s16 dirs8[8] = {
223 INodeDefManager *ndef = data->m_client->ndef();
225 u16 ambient_occlusion = 0;
227 u8 light_source_max = 0;
231 for (u32 i = 0; i < 8; i++)
233 MapNode n = data->m_vmanip.getNodeNoExNoEmerge(p - dirs8[i]);
235 // if it's CONTENT_IGNORE we can't do any light calculations
236 if (n.getContent() == CONTENT_IGNORE) {
240 const ContentFeatures &f = ndef->get(n);
241 if (f.light_source > light_source_max)
242 light_source_max = f.light_source;
243 // Check f.solidness because fast-style leaves look better this way
244 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
245 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
246 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
256 light_day /= light_count;
257 light_night /= light_count;
259 // Boost brightness around light sources
260 bool skip_ambient_occlusion_day = false;
261 if(decode_light(light_source_max) >= light_day) {
262 light_day = decode_light(light_source_max);
263 skip_ambient_occlusion_day = true;
266 bool skip_ambient_occlusion_night = false;
267 if(decode_light(light_source_max) >= light_night) {
268 light_night = decode_light(light_source_max);
269 skip_ambient_occlusion_night = true;
272 if (ambient_occlusion > 4)
274 static const float ao_gamma = rangelim(
275 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
277 // Table of gamma space multiply factors.
278 static const float light_amount[3] = {
279 powf(0.75, 1.0 / ao_gamma),
280 powf(0.5, 1.0 / ao_gamma),
281 powf(0.25, 1.0 / ao_gamma)
284 //calculate table index for gamma space multiplier
285 ambient_occlusion -= 5;
287 if (!skip_ambient_occlusion_day)
288 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
289 if (!skip_ambient_occlusion_night)
290 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
293 return light_day | (light_night << 8);
297 Calculate smooth lighting at the given corner of p.
300 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
302 if(corner.X == 1) p.X += 1;
303 // else corner.X == -1
304 if(corner.Y == 1) p.Y += 1;
305 // else corner.Y == -1
306 if(corner.Z == 1) p.Z += 1;
307 // else corner.Z == -1
309 return getSmoothLightCombined(p, data);
312 void get_sunlight_color(video::SColorf *sunlight, u32 daynight_ratio){
313 f32 rg = daynight_ratio / 1000.0f - 0.04f;
314 f32 b = (0.98f * daynight_ratio) / 1000.0f + 0.078f;
320 void final_color_blend(video::SColor *result,
321 u16 light, u32 daynight_ratio)
323 video::SColorf dayLight;
324 get_sunlight_color(&dayLight, daynight_ratio);
325 final_color_blend(result,
326 encode_light(light, 0), dayLight);
329 void final_color_blend(video::SColor *result,
330 const video::SColor &data, const video::SColorf &dayLight)
332 static const video::SColorf artificialColor(1.04f, 1.04f, 1.04f);
334 video::SColorf c(data);
337 f32 r = c.r * (c.a * dayLight.r + n * artificialColor.r) * 2.0f;
338 f32 g = c.g * (c.a * dayLight.g + n * artificialColor.g) * 2.0f;
339 f32 b = c.b * (c.a * dayLight.b + n * artificialColor.b) * 2.0f;
341 // Emphase blue a bit in darker places
342 // Each entry of this array represents a range of 8 blue levels
343 static const u8 emphase_blue_when_dark[32] = {
344 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
345 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
348 b += emphase_blue_when_dark[irr::core::clamp((s32) ((r + g + b) / 3 * 255),
349 0, 255) / 8] / 255.0f;
351 result->setRed(core::clamp((s32) (r * 255.0f), 0, 255));
352 result->setGreen(core::clamp((s32) (g * 255.0f), 0, 255));
353 result->setBlue(core::clamp((s32) (b * 255.0f), 0, 255));
357 Mesh generation helpers
361 vertex_dirs: v3s16[4]
363 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
366 If looked from outside the node towards the face, the corners are:
372 if(dir == v3s16(0,0,1))
374 // If looking towards z+, this is the face that is behind
375 // the center point, facing towards z+.
376 vertex_dirs[0] = v3s16(-1,-1, 1);
377 vertex_dirs[1] = v3s16( 1,-1, 1);
378 vertex_dirs[2] = v3s16( 1, 1, 1);
379 vertex_dirs[3] = v3s16(-1, 1, 1);
381 else if(dir == v3s16(0,0,-1))
384 vertex_dirs[0] = v3s16( 1,-1,-1);
385 vertex_dirs[1] = v3s16(-1,-1,-1);
386 vertex_dirs[2] = v3s16(-1, 1,-1);
387 vertex_dirs[3] = v3s16( 1, 1,-1);
389 else if(dir == v3s16(1,0,0))
392 vertex_dirs[0] = v3s16( 1,-1, 1);
393 vertex_dirs[1] = v3s16( 1,-1,-1);
394 vertex_dirs[2] = v3s16( 1, 1,-1);
395 vertex_dirs[3] = v3s16( 1, 1, 1);
397 else if(dir == v3s16(-1,0,0))
400 vertex_dirs[0] = v3s16(-1,-1,-1);
401 vertex_dirs[1] = v3s16(-1,-1, 1);
402 vertex_dirs[2] = v3s16(-1, 1, 1);
403 vertex_dirs[3] = v3s16(-1, 1,-1);
405 else if(dir == v3s16(0,1,0))
407 // faces towards Y+ (assume Z- as "down" in texture)
408 vertex_dirs[0] = v3s16( 1, 1,-1);
409 vertex_dirs[1] = v3s16(-1, 1,-1);
410 vertex_dirs[2] = v3s16(-1, 1, 1);
411 vertex_dirs[3] = v3s16( 1, 1, 1);
413 else if(dir == v3s16(0,-1,0))
415 // faces towards Y- (assume Z+ as "down" in texture)
416 vertex_dirs[0] = v3s16( 1,-1, 1);
417 vertex_dirs[1] = v3s16(-1,-1, 1);
418 vertex_dirs[2] = v3s16(-1,-1,-1);
419 vertex_dirs[3] = v3s16( 1,-1,-1);
426 video::S3DVertex vertices[4]; // Precalculated vertices
428 * The face is divided into two triangles. If this is true,
429 * vertices 0 and 2 are connected, othervise vertices 1 and 3
432 bool vertex_0_2_connected;
436 static void makeFastFace(const TileSpec &tile, u16 li0, u16 li1, u16 li2, u16 li3,
437 v3f p, v3s16 dir, v3f scale, std::vector<FastFace> &dest)
439 // Position is at the center of the cube.
448 v3s16 vertex_dirs[4];
449 getNodeVertexDirs(dir, vertex_dirs);
453 switch (tile.rotation)
459 vertex_dirs[0] = vertex_dirs[3];
460 vertex_dirs[3] = vertex_dirs[2];
461 vertex_dirs[2] = vertex_dirs[1];
471 vertex_dirs[0] = vertex_dirs[2];
474 vertex_dirs[1] = vertex_dirs[3];
485 vertex_dirs[0] = vertex_dirs[1];
486 vertex_dirs[1] = vertex_dirs[2];
487 vertex_dirs[2] = vertex_dirs[3];
497 vertex_dirs[0] = vertex_dirs[3];
498 vertex_dirs[3] = vertex_dirs[2];
499 vertex_dirs[2] = vertex_dirs[1];
511 vertex_dirs[0] = vertex_dirs[1];
512 vertex_dirs[1] = vertex_dirs[2];
513 vertex_dirs[2] = vertex_dirs[3];
525 vertex_dirs[0] = vertex_dirs[3];
526 vertex_dirs[3] = vertex_dirs[2];
527 vertex_dirs[2] = vertex_dirs[1];
539 vertex_dirs[0] = vertex_dirs[1];
540 vertex_dirs[1] = vertex_dirs[2];
541 vertex_dirs[2] = vertex_dirs[3];
563 for(u16 i=0; i<4; i++)
566 BS/2*vertex_dirs[i].X,
567 BS/2*vertex_dirs[i].Y,
568 BS/2*vertex_dirs[i].Z
572 for(u16 i=0; i<4; i++)
574 vertex_pos[i].X *= scale.X;
575 vertex_pos[i].Y *= scale.Y;
576 vertex_pos[i].Z *= scale.Z;
577 vertex_pos[i] += pos;
581 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
582 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
583 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
585 v3f normal(dir.X, dir.Y, dir.Z);
587 u16 li[4] = { li0, li1, li2, li3 };
591 for (u8 i = 0; i < 4; i++) {
593 night[i] = li[i] & 0xFF;
596 bool vertex_0_2_connected = abs(day[0] - day[2]) + abs(night[0] - night[2])
597 < abs(day[1] - day[3]) + abs(night[1] - night[3]);
600 core::vector2d<f32>(x0 + w * abs_scale, y0 + h),
601 core::vector2d<f32>(x0, y0 + h),
602 core::vector2d<f32>(x0, y0),
603 core::vector2d<f32>(x0 + w * abs_scale, y0) };
605 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
606 const TileLayer *layer = &tile.layers[layernum];
607 if (layer->texture_id == 0)
610 dest.push_back(FastFace());
611 FastFace& face = *dest.rbegin();
613 for (u8 i = 0; i < 4; i++) {
614 video::SColor c = encode_light(li[i], tile.emissive_light);
615 if (!tile.emissive_light)
616 applyFacesShading(c, normal);
618 face.vertices[i] = video::S3DVertex(vertex_pos[i], normal, c, f[i]);
622 Revert triangles for nicer looking gradient if the
623 brightness of vertices 1 and 3 differ less than
624 the brightness of vertices 0 and 2.
626 face.vertex_0_2_connected = vertex_0_2_connected;
629 face.layernum = layernum;
634 Nodes make a face if contents differ and solidness differs.
637 1: Face uses m1's content
638 2: Face uses m2's content
639 equivalent: Whether the blocks share the same face (eg. water and glass)
641 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
643 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
644 INodeDefManager *ndef)
648 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
651 bool contents_differ = (m1 != m2);
653 const ContentFeatures &f1 = ndef->get(m1);
654 const ContentFeatures &f2 = ndef->get(m2);
656 // Contents don't differ for different forms of same liquid
657 if(f1.sameLiquid(f2))
658 contents_differ = false;
660 u8 c1 = f1.solidness;
661 u8 c2 = f2.solidness;
663 bool solidness_differs = (c1 != c2);
664 bool makes_face = contents_differ && solidness_differs;
666 if(makes_face == false)
670 c1 = f1.visual_solidness;
672 c2 = f2.visual_solidness;
676 // If same solidness, liquid takes precense
690 Gets nth node tile (0 <= n <= 5).
692 void getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
694 INodeDefManager *ndef = data->m_client->ndef();
695 const ContentFeatures &f = ndef->get(mn);
696 tile = f.tiles[tileindex];
697 TileLayer *top_layer = NULL;
698 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
699 TileLayer *layer = &tile.layers[layernum];
700 if (layer->texture_id == 0)
703 if (!layer->has_color)
704 mn.getColor(f, &(layer->color));
706 // Apply temporary crack
707 if (p == data->m_crack_pos_relative)
708 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
712 Gets node tile given a face direction.
714 void getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data, TileSpec &tile)
716 INodeDefManager *ndef = data->m_client->ndef();
718 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
719 // (0,0,1), (0,0,-1) or (0,0,0)
720 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
722 // Convert direction to single integer for table lookup
727 // 4 = invalid, treat as (0,0,0)
731 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
733 // Get rotation for things like chests
734 u8 facedir = mn.getFaceDir(ndef);
736 static const u16 dir_to_tile[24 * 16] =
738 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
739 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
740 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
741 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
742 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
744 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
745 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
746 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
747 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
749 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
750 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
751 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
752 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
754 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
755 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
756 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
757 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
759 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
760 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
761 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
762 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
764 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
765 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
766 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
767 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
770 u16 tile_index=facedir*16 + dir_i;
771 getNodeTileN(mn, p, dir_to_tile[tile_index], data, tile);
772 tile.rotation = dir_to_tile[tile_index + 1];
775 static void getTileInfo(
779 const v3s16 &face_dir,
783 v3s16 &face_dir_corrected,
788 VoxelManipulator &vmanip = data->m_vmanip;
789 INodeDefManager *ndef = data->m_client->ndef();
790 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
792 const MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
794 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
795 if (n0.getContent() == CONTENT_IGNORE) {
800 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
802 if (n1.getContent() == CONTENT_IGNORE) {
808 bool equivalent = false;
809 u8 mf = face_contents(n0.getContent(), n1.getContent(),
823 face_dir_corrected = face_dir;
826 p_corrected = p + face_dir;
827 face_dir_corrected = -face_dir;
830 getNodeTile(n, p_corrected, face_dir_corrected, data, tile);
831 const ContentFeatures &f = ndef->get(n);
832 tile.emissive_light = f.light_source;
834 // eg. water and glass
836 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++)
837 tile.layers[layernum].material_flags |=
838 MATERIAL_FLAG_BACKFACE_CULLING;
841 if (!data->m_smooth_lighting) {
842 lights[0] = lights[1] = lights[2] = lights[3] =
843 getFaceLight(n0, n1, face_dir, ndef);
846 v3s16 vertex_dirs[4];
847 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
849 v3s16 light_p = blockpos_nodes + p_corrected;
850 for (u16 i = 0; i < 4; i++) {
851 lights[i] = getSmoothLight(light_p, vertex_dirs[i], data);
858 translate_dir: unit vector with only one of x, y or z
859 face_dir: unit vector with only one of x, y or z
861 static void updateFastFaceRow(
868 std::vector<FastFace> &dest)
872 u16 continuous_tiles_count = 1;
874 bool makes_face = false;
876 v3s16 face_dir_corrected;
877 u16 lights[4] = {0,0,0,0};
879 getTileInfo(data, p, face_dir,
880 makes_face, p_corrected, face_dir_corrected,
883 // Unroll this variable which has a significant build cost
885 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
886 // If tiling can be done, this is set to false in the next step
887 bool next_is_different = true;
891 bool next_makes_face = false;
892 v3s16 next_p_corrected;
893 v3s16 next_face_dir_corrected;
894 u16 next_lights[4] = {0,0,0,0};
897 // If at last position, there is nothing to compare to and
898 // the face must be drawn anyway
899 if (j != MAP_BLOCKSIZE - 1) {
900 p_next = p + translate_dir;
902 getTileInfo(data, p_next, face_dir,
903 next_makes_face, next_p_corrected,
904 next_face_dir_corrected, next_lights,
907 if (next_makes_face == makes_face
908 && next_p_corrected == p_corrected + translate_dir
909 && next_face_dir_corrected == face_dir_corrected
910 && memcmp(next_lights, lights, ARRLEN(lights) * sizeof(u16)) == 0
911 && next_tile.isTileable(tile)) {
912 next_is_different = false;
913 continuous_tiles_count++;
917 if (next_is_different) {
919 Create a face if there should be one
922 // Floating point conversion of the position vector
923 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
924 // Center point of face (kind of)
926 ((f32)continuous_tiles_count / 2.0f - 0.5f) * translate_dir_f;
929 if(translate_dir.X != 0) {
930 scale.X = continuous_tiles_count;
932 if(translate_dir.Y != 0) {
933 scale.Y = continuous_tiles_count;
935 if(translate_dir.Z != 0) {
936 scale.Z = continuous_tiles_count;
939 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
940 sp, face_dir_corrected, scale, dest);
942 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
943 for(int i = 1; i < continuous_tiles_count; i++){
944 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
948 continuous_tiles_count = 1;
951 makes_face = next_makes_face;
952 p_corrected = next_p_corrected;
953 face_dir_corrected = next_face_dir_corrected;
954 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
955 if (next_is_different)
961 static void updateAllFastFaceRows(MeshMakeData *data,
962 std::vector<FastFace> &dest)
965 Go through every y,z and get top(y+) faces in rows of x+
967 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
968 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
969 updateFastFaceRow(data,
973 v3s16(0,1,0), //face dir
980 Go through every x,y and get right(x+) faces in rows of z+
982 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
983 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
984 updateFastFaceRow(data,
988 v3s16(1,0,0), //face dir
995 Go through every y,z and get back(z+) faces in rows of x+
997 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
998 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
999 updateFastFaceRow(data,
1003 v3s16(0,0,1), //face dir
1014 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1015 m_minimap_mapblock(NULL),
1016 m_client(data->m_client),
1017 m_driver(m_client->tsrc()->getDevice()->getVideoDriver()),
1018 m_tsrc(m_client->getTextureSource()),
1019 m_shdrsrc(m_client->getShaderSource()),
1020 m_animation_force_timer(0), // force initial animation
1022 m_crack_materials(),
1023 m_last_daynight_ratio((u32) -1),
1026 for (int m = 0; m < MAX_TILE_LAYERS; m++)
1027 m_mesh[m] = new scene::SMesh();
1028 m_enable_shaders = data->m_use_shaders;
1029 m_use_tangent_vertices = data->m_use_tangent_vertices;
1030 m_enable_vbo = g_settings->getBool("enable_vbo");
1032 if (g_settings->getBool("enable_minimap")) {
1033 m_minimap_mapblock = new MinimapMapblock;
1034 m_minimap_mapblock->getMinimapNodes(
1035 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1038 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1039 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1040 //TimeTaker timer1("MapBlockMesh()");
1042 std::vector<FastFace> fastfaces_new;
1043 fastfaces_new.reserve(512);
1046 We are including the faces of the trailing edges of the block.
1047 This means that when something changes, the caller must
1048 also update the meshes of the blocks at the leading edges.
1050 NOTE: This is the slowest part of this method.
1053 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1054 //TimeTaker timer2("updateAllFastFaceRows()");
1055 updateAllFastFaceRows(data, fastfaces_new);
1060 Convert FastFaces to MeshCollector
1063 MeshCollector collector(m_use_tangent_vertices);
1066 // avg 0ms (100ms spikes when loading textures the first time)
1067 // (NOTE: probably outdated)
1068 //TimeTaker timer2("MeshCollector building");
1070 for (u32 i = 0; i < fastfaces_new.size(); i++) {
1071 FastFace &f = fastfaces_new[i];
1073 const u16 indices[] = {0,1,2,2,3,0};
1074 const u16 indices_alternate[] = {0,1,3,2,3,1};
1076 if (f.layer.texture == NULL)
1079 const u16 *indices_p =
1080 f.vertex_0_2_connected ? indices : indices_alternate;
1082 collector.append(f.layer, f.vertices, 4, indices_p, 6,
1088 Add special graphics:
1096 MapblockMeshGenerator generator(data, &collector);
1097 generator.generate();
1100 collector.applyTileColors();
1103 Convert MeshCollector to SMesh
1106 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1107 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1109 PreMeshBuffer &p = collector.prebuffers[layer][i];
1111 // Generate animation data
1113 if(p.layer.material_flags & MATERIAL_FLAG_CRACK)
1115 // Find the texture name plus ^[crack:N:
1116 std::ostringstream os(std::ios::binary);
1117 os<<m_tsrc->getTextureName(p.layer.texture_id)<<"^[crack";
1118 if(p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1119 os<<"o"; // use ^[cracko
1120 os<<":"<<(u32)p.layer.animation_frame_count<<":";
1121 m_crack_materials.insert(std::make_pair(std::pair<u8, u32>(layer, i), os.str()));
1122 // Replace tile texture with the cracked one
1123 p.layer.texture = m_tsrc->getTextureForMesh(
1125 &p.layer.texture_id);
1127 // - Texture animation
1128 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1129 // Add to MapBlockMesh in order to animate these tiles
1130 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1131 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1132 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1133 // Get starting position from noise
1134 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 100000 * (2.0 + noise3d(
1135 data->m_blockpos.X, data->m_blockpos.Y,
1136 data->m_blockpos.Z, 0));
1138 // Play all synchronized
1139 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1141 // Replace tile texture with the first animation frame
1142 p.layer.texture = p.layer.frames[0].texture;
1145 if (!m_enable_shaders) {
1146 // Extract colors for day-night animation
1147 // Dummy sunlight to handle non-sunlit areas
1148 video::SColorf sunlight;
1149 get_sunlight_color(&sunlight, 0);
1151 m_use_tangent_vertices ?
1152 p.tangent_vertices.size() : p.vertices.size();
1153 for (u32 j = 0; j < vertex_count; j++) {
1155 if (m_use_tangent_vertices) {
1156 vc = &p.tangent_vertices[j].Color;
1158 vc = &p.vertices[j].Color;
1160 video::SColor copy(*vc);
1161 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1162 final_color_blend(vc, copy, sunlight); // Finalize color
1163 else // Record color to animate
1164 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1166 // The sunlight ratio has been stored,
1167 // delete alpha (for the final rendering).
1173 video::SMaterial material;
1174 material.setFlag(video::EMF_LIGHTING, false);
1175 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1176 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1177 material.setFlag(video::EMF_FOG_ENABLE, true);
1178 material.setTexture(0, p.layer.texture);
1180 if (m_enable_shaders) {
1181 material.MaterialType = m_shdrsrc->getShaderInfo(p.layer.shader_id).material;
1182 p.layer.applyMaterialOptionsWithShaders(material);
1183 if (p.layer.normal_texture) {
1184 material.setTexture(1, p.layer.normal_texture);
1186 material.setTexture(2, p.layer.flags_texture);
1188 p.layer.applyMaterialOptions(material);
1191 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1193 // Create meshbuffer, add to mesh
1194 if (m_use_tangent_vertices) {
1195 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1197 buf->Material = material;
1199 mesh->addMeshBuffer(buf);
1202 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1203 &p.indices[0], p.indices.size());
1205 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1207 buf->Material = material;
1209 mesh->addMeshBuffer(buf);
1212 buf->append(&p.vertices[0], p.vertices.size(),
1213 &p.indices[0], p.indices.size());
1219 Do some stuff to the mesh
1221 m_camera_offset = camera_offset;
1222 translateMesh(m_mesh[layer],
1223 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1225 if (m_use_tangent_vertices) {
1226 scene::IMeshManipulator* meshmanip =
1227 m_client->getSceneManager()->getMeshManipulator();
1228 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1234 // Usually 1-700 faces and 1-7 materials
1235 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1236 <<"and uses "<<m_mesh[layer]->getMeshBufferCount()
1237 <<" materials (meshbuffers)"<<std::endl;
1240 // Use VBO for mesh (this just would set this for ever buffer)
1242 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1247 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1249 // Check if animation is required for this mesh
1251 !m_crack_materials.empty() ||
1252 !m_daynight_diffs.empty() ||
1253 !m_animation_tiles.empty();
1256 MapBlockMesh::~MapBlockMesh()
1258 for (int m = 0; m < MAX_TILE_LAYERS; m++) {
1259 if (m_enable_vbo && m_mesh[m])
1260 for (u32 i = 0; i < m_mesh[m]->getMeshBufferCount(); i++) {
1261 scene::IMeshBuffer *buf = m_mesh[m]->getMeshBuffer(i);
1262 m_driver->removeHardwareBuffer(buf);
1267 delete m_minimap_mapblock;
1270 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1272 if(!m_has_animation)
1274 m_animation_force_timer = 100000;
1278 m_animation_force_timer = myrand_range(5, 100);
1281 if(crack != m_last_crack)
1283 for (std::map<std::pair<u8, u32>, std::string>::iterator i =
1284 m_crack_materials.begin(); i != m_crack_materials.end(); ++i) {
1285 scene::IMeshBuffer *buf = m_mesh[i->first.first]->
1286 getMeshBuffer(i->first.second);
1287 std::string basename = i->second;
1289 // Create new texture name from original
1290 std::ostringstream os;
1291 os<<basename<<crack;
1292 u32 new_texture_id = 0;
1293 video::ITexture *new_texture =
1294 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1295 buf->getMaterial().setTexture(0, new_texture);
1297 // If the current material is also animated,
1298 // update animation info
1299 std::map<std::pair<u8, u32>, TileLayer>::iterator anim_iter =
1300 m_animation_tiles.find(i->first);
1301 if (anim_iter != m_animation_tiles.end()){
1302 TileLayer &tile = anim_iter->second;
1303 tile.texture = new_texture;
1304 tile.texture_id = new_texture_id;
1305 // force animation update
1306 m_animation_frames[i->first] = -1;
1310 m_last_crack = crack;
1313 // Texture animation
1314 for (std::map<std::pair<u8, u32>, TileLayer>::iterator i =
1315 m_animation_tiles.begin(); i != m_animation_tiles.end(); ++i) {
1316 const TileLayer &tile = i->second;
1317 // Figure out current frame
1318 int frameoffset = m_animation_frame_offsets[i->first];
1319 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1320 + frameoffset) % tile.animation_frame_count;
1321 // If frame doesn't change, skip
1322 if(frame == m_animation_frames[i->first])
1325 m_animation_frames[i->first] = frame;
1327 scene::IMeshBuffer *buf = m_mesh[i->first.first]->
1328 getMeshBuffer(i->first.second);
1330 const FrameSpec &animation_frame = tile.frames[frame];
1331 buf->getMaterial().setTexture(0, animation_frame.texture);
1332 if (m_enable_shaders) {
1333 if (animation_frame.normal_texture) {
1334 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1336 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1340 // Day-night transition
1341 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1343 // Force reload mesh to VBO
1345 for (int m = 0; m < MAX_TILE_LAYERS; m++)
1346 m_mesh[m]->setDirty();
1347 video::SColorf day_color;
1348 get_sunlight_color(&day_color, daynight_ratio);
1349 for(std::map<std::pair<u8, u32>, std::map<u32, video::SColor > >::iterator
1350 i = m_daynight_diffs.begin();
1351 i != m_daynight_diffs.end(); ++i)
1353 scene::IMeshBuffer *buf = m_mesh[i->first.first]->
1354 getMeshBuffer(i->first.second);
1355 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1356 for(std::map<u32, video::SColor >::iterator
1357 j = i->second.begin();
1358 j != i->second.end(); ++j)
1360 final_color_blend(&(vertices[j->first].Color),
1361 j->second, day_color);
1364 m_last_daynight_ratio = daynight_ratio;
1370 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1372 if (camera_offset != m_camera_offset) {
1373 for (u8 layer = 0; layer < 2; layer++) {
1374 translateMesh(m_mesh[layer],
1375 intToFloat(m_camera_offset - camera_offset, BS));
1377 m_mesh[layer]->setDirty();
1380 m_camera_offset = camera_offset;
1388 void MeshCollector::append(const TileSpec &tile,
1389 const video::S3DVertex *vertices, u32 numVertices,
1390 const u16 *indices, u32 numIndices)
1392 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1393 const TileLayer *layer = &tile.layers[layernum];
1394 if (layer->texture_id == 0)
1396 append(*layer, vertices, numVertices, indices, numIndices,
1401 void MeshCollector::append(const TileLayer &layer,
1402 const video::S3DVertex *vertices, u32 numVertices,
1403 const u16 *indices, u32 numIndices, u8 layernum)
1405 if (numIndices > 65535) {
1406 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1409 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1411 PreMeshBuffer *p = NULL;
1412 for (u32 i = 0; i < buffers->size(); i++) {
1413 PreMeshBuffer &pp = (*buffers)[i];
1414 if (pp.layer != layer)
1416 if (pp.indices.size() + numIndices > 65535)
1426 buffers->push_back(pp);
1427 p = &(*buffers)[buffers->size() - 1];
1431 if (m_use_tangent_vertices) {
1432 vertex_count = p->tangent_vertices.size();
1433 for (u32 i = 0; i < numVertices; i++) {
1434 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1435 vertices[i].Color, vertices[i].TCoords);
1436 p->tangent_vertices.push_back(vert);
1439 vertex_count = p->vertices.size();
1440 for (u32 i = 0; i < numVertices; i++) {
1441 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1442 vertices[i].Color, vertices[i].TCoords);
1443 p->vertices.push_back(vert);
1447 for (u32 i = 0; i < numIndices; i++) {
1448 u32 j = indices[i] + vertex_count;
1449 p->indices.push_back(j);
1454 MeshCollector - for meshnodes and converted drawtypes.
1457 void MeshCollector::append(const TileSpec &tile,
1458 const video::S3DVertex *vertices, u32 numVertices,
1459 const u16 *indices, u32 numIndices,
1460 v3f pos, video::SColor c, u8 light_source)
1462 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1463 const TileLayer *layer = &tile.layers[layernum];
1464 if (layer->texture_id == 0)
1466 append(*layer, vertices, numVertices, indices, numIndices, pos,
1467 c, light_source, layernum);
1471 void MeshCollector::append(const TileLayer &layer,
1472 const video::S3DVertex *vertices, u32 numVertices,
1473 const u16 *indices, u32 numIndices,
1474 v3f pos, video::SColor c, u8 light_source, u8 layernum)
1476 if (numIndices > 65535) {
1477 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1480 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1482 PreMeshBuffer *p = NULL;
1483 for (u32 i = 0; i < buffers->size(); i++) {
1484 PreMeshBuffer &pp = (*buffers)[i];
1485 if(pp.layer != layer)
1487 if(pp.indices.size() + numIndices > 65535)
1497 buffers->push_back(pp);
1498 p = &(*buffers)[buffers->size() - 1];
1501 video::SColor original_c = c;
1503 if (m_use_tangent_vertices) {
1504 vertex_count = p->tangent_vertices.size();
1505 for (u32 i = 0; i < numVertices; i++) {
1506 if (!light_source) {
1508 applyFacesShading(c, vertices[i].Normal);
1510 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1511 vertices[i].Normal, c, vertices[i].TCoords);
1512 p->tangent_vertices.push_back(vert);
1515 vertex_count = p->vertices.size();
1516 for (u32 i = 0; i < numVertices; i++) {
1517 if (!light_source) {
1519 applyFacesShading(c, vertices[i].Normal);
1521 video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
1522 vertices[i].TCoords);
1523 p->vertices.push_back(vert);
1527 for (u32 i = 0; i < numIndices; i++) {
1528 u32 j = indices[i] + vertex_count;
1529 p->indices.push_back(j);
1533 void MeshCollector::applyTileColors()
1535 if (m_use_tangent_vertices)
1536 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1537 std::vector<PreMeshBuffer> *p = &prebuffers[layer];
1538 for (std::vector<PreMeshBuffer>::iterator it = p->begin();
1539 it != p->end(); ++it) {
1540 video::SColor tc = it->layer.color;
1541 if (tc == video::SColor(0xFFFFFFFF))
1543 for (u32 index = 0; index < it->tangent_vertices.size(); index++) {
1544 video::SColor *c = &it->tangent_vertices[index].Color;
1545 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1546 c->getGreen() * tc.getGreen() / 255,
1547 c->getBlue() * tc.getBlue() / 255);
1552 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1553 std::vector<PreMeshBuffer> *p = &prebuffers[layer];
1554 for (std::vector<PreMeshBuffer>::iterator it = p->begin();
1555 it != p->end(); ++it) {
1556 video::SColor tc = it->layer.color;
1557 if (tc == video::SColor(0xFFFFFFFF))
1559 for (u32 index = 0; index < it->vertices.size(); index++) {
1560 video::SColor *c = &it->vertices[index].Color;
1561 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1562 c->getGreen() * tc.getGreen() / 255,
1563 c->getBlue() * tc.getBlue() / 255);
1569 video::SColor encode_light(u16 light, u8 emissive_light)
1572 u32 day = (light & 0xff);
1573 u32 night = (light >> 8);
1574 // Add emissive light
1575 night += emissive_light * 2.5f;
1578 // Since we don't know if the day light is sunlight or
1579 // artificial light, assume it is artificial when the night
1580 // light bank is also lit.
1585 u32 sum = day + night;
1586 // Ratio of sunlight:
1589 r = day * 255 / sum;
1593 float b = (day + night) / 2;
1594 return video::SColor(r, b, b, b);