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(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 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 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
694 INodeDefManager *ndef = data->m_client->ndef();
695 const ContentFeatures &f = ndef->get(mn);
696 TileSpec 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;
713 Gets node tile given a face direction.
715 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
717 INodeDefManager *ndef = data->m_client->ndef();
719 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
720 // (0,0,1), (0,0,-1) or (0,0,0)
721 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
723 // Convert direction to single integer for table lookup
728 // 4 = invalid, treat as (0,0,0)
732 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
734 // Get rotation for things like chests
735 u8 facedir = mn.getFaceDir(ndef);
737 static const u16 dir_to_tile[24 * 16] =
739 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
740 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
741 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
742 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
743 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
745 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
746 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
747 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
748 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
750 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
751 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
752 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
753 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
755 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
756 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
757 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
758 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
760 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
761 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
762 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
763 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
765 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
766 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
767 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
768 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
771 u16 tile_index=facedir*16 + dir_i;
772 TileSpec tile = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
773 tile.rotation = dir_to_tile[tile_index + 1];
777 static void getTileInfo(
781 const v3s16 &face_dir,
785 v3s16 &face_dir_corrected,
790 VoxelManipulator &vmanip = data->m_vmanip;
791 INodeDefManager *ndef = data->m_client->ndef();
792 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
794 MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
796 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
797 if (n0.getContent() == CONTENT_IGNORE) {
802 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(
803 blockpos_nodes + p + face_dir);
805 if (n1.getContent() == CONTENT_IGNORE) {
811 bool equivalent = false;
812 u8 mf = face_contents(n0.getContent(), n1.getContent(),
827 face_dir_corrected = face_dir;
830 p_corrected = p + face_dir;
831 face_dir_corrected = -face_dir;
833 tile = getNodeTile(n, p_corrected, face_dir_corrected, data);
834 const ContentFeatures &f = ndef->get(n);
835 tile.emissive_light = f.light_source;
837 // eg. water and glass
839 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++)
840 tile.layers[layernum].material_flags |=
841 MATERIAL_FLAG_BACKFACE_CULLING;
843 if (data->m_smooth_lighting == false)
845 lights[0] = lights[1] = lights[2] = lights[3] =
846 getFaceLight(n0, n1, face_dir, ndef);
850 v3s16 vertex_dirs[4];
851 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
852 for(u16 i=0; i<4; i++)
854 lights[i] = getSmoothLight(
855 blockpos_nodes + p_corrected,
856 vertex_dirs[i], data);
865 translate_dir: unit vector with only one of x, y or z
866 face_dir: unit vector with only one of x, y or z
868 static void updateFastFaceRow(
875 std::vector<FastFace> &dest)
879 u16 continuous_tiles_count = 1;
881 bool makes_face = false;
883 v3s16 face_dir_corrected;
884 u16 lights[4] = {0,0,0,0};
886 getTileInfo(data, p, face_dir,
887 makes_face, p_corrected, face_dir_corrected,
890 // Unroll this variable which has a significant build cost
892 for (u16 j = 0; j < MAP_BLOCKSIZE; j++) {
893 // If tiling can be done, this is set to false in the next step
894 bool next_is_different = true;
898 bool next_makes_face = false;
899 v3s16 next_p_corrected;
900 v3s16 next_face_dir_corrected;
901 u16 next_lights[4] = {0,0,0,0};
904 // If at last position, there is nothing to compare to and
905 // the face must be drawn anyway
906 if (j != MAP_BLOCKSIZE - 1) {
907 p_next = p + translate_dir;
909 getTileInfo(data, p_next, face_dir,
910 next_makes_face, next_p_corrected,
911 next_face_dir_corrected, next_lights,
914 if (next_makes_face == makes_face
915 && next_p_corrected == p_corrected + translate_dir
916 && next_face_dir_corrected == face_dir_corrected
917 && next_lights[0] == lights[0]
918 && next_lights[1] == lights[1]
919 && next_lights[2] == lights[2]
920 && next_lights[3] == lights[3]
921 && next_tile.isTileable(tile)) {
922 next_is_different = false;
923 continuous_tiles_count++;
927 if (next_is_different) {
929 Create a face if there should be one
932 // Floating point conversion of the position vector
933 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
934 // Center point of face (kind of)
935 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
938 if(translate_dir.X != 0) {
939 scale.X = continuous_tiles_count;
941 if(translate_dir.Y != 0) {
942 scale.Y = continuous_tiles_count;
944 if(translate_dir.Z != 0) {
945 scale.Z = continuous_tiles_count;
948 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
949 sp, face_dir_corrected, scale, dest);
951 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
952 for(int i = 1; i < continuous_tiles_count; i++){
953 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
957 continuous_tiles_count = 1;
960 makes_face = next_makes_face;
961 p_corrected = next_p_corrected;
962 face_dir_corrected = next_face_dir_corrected;
963 std::memcpy(lights, next_lights, ARRLEN(lights) * sizeof(u16));
964 if (next_is_different)
970 static void updateAllFastFaceRows(MeshMakeData *data,
971 std::vector<FastFace> &dest)
974 Go through every y,z and get top(y+) faces in rows of x+
976 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
977 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
978 updateFastFaceRow(data,
982 v3s16(0,1,0), //face dir
989 Go through every x,y and get right(x+) faces in rows of z+
991 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
992 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
993 updateFastFaceRow(data,
997 v3s16(1,0,0), //face dir
1004 Go through every y,z and get back(z+) faces in rows of x+
1006 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1007 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1008 updateFastFaceRow(data,
1012 v3s16(0,0,1), //face dir
1023 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1024 m_minimap_mapblock(NULL),
1025 m_client(data->m_client),
1026 m_driver(m_client->tsrc()->getDevice()->getVideoDriver()),
1027 m_tsrc(m_client->getTextureSource()),
1028 m_shdrsrc(m_client->getShaderSource()),
1029 m_animation_force_timer(0), // force initial animation
1031 m_crack_materials(),
1032 m_last_daynight_ratio((u32) -1),
1035 for (int m = 0; m < MAX_TILE_LAYERS; m++)
1036 m_mesh[m] = new scene::SMesh();
1037 m_enable_shaders = data->m_use_shaders;
1038 m_use_tangent_vertices = data->m_use_tangent_vertices;
1039 m_enable_vbo = g_settings->getBool("enable_vbo");
1041 if (g_settings->getBool("enable_minimap")) {
1042 m_minimap_mapblock = new MinimapMapblock;
1043 m_minimap_mapblock->getMinimapNodes(
1044 &data->m_vmanip, data->m_blockpos * MAP_BLOCKSIZE);
1047 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1048 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1049 //TimeTaker timer1("MapBlockMesh()");
1051 std::vector<FastFace> fastfaces_new;
1052 fastfaces_new.reserve(512);
1055 We are including the faces of the trailing edges of the block.
1056 This means that when something changes, the caller must
1057 also update the meshes of the blocks at the leading edges.
1059 NOTE: This is the slowest part of this method.
1062 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1063 //TimeTaker timer2("updateAllFastFaceRows()");
1064 updateAllFastFaceRows(data, fastfaces_new);
1069 Convert FastFaces to MeshCollector
1072 MeshCollector collector(m_use_tangent_vertices);
1075 // avg 0ms (100ms spikes when loading textures the first time)
1076 // (NOTE: probably outdated)
1077 //TimeTaker timer2("MeshCollector building");
1079 for (u32 i = 0; i < fastfaces_new.size(); i++) {
1080 FastFace &f = fastfaces_new[i];
1082 const u16 indices[] = {0,1,2,2,3,0};
1083 const u16 indices_alternate[] = {0,1,3,2,3,1};
1085 if (f.layer.texture == NULL)
1088 const u16 *indices_p =
1089 f.vertex_0_2_connected ? indices : indices_alternate;
1091 collector.append(f.layer, f.vertices, 4, indices_p, 6,
1097 Add special graphics:
1105 MapblockMeshGenerator generator(data, &collector);
1106 generator.generate();
1109 collector.applyTileColors();
1112 Convert MeshCollector to SMesh
1115 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1116 for(u32 i = 0; i < collector.prebuffers[layer].size(); i++)
1118 PreMeshBuffer &p = collector.prebuffers[layer][i];
1120 // Generate animation data
1122 if(p.layer.material_flags & MATERIAL_FLAG_CRACK)
1124 // Find the texture name plus ^[crack:N:
1125 std::ostringstream os(std::ios::binary);
1126 os<<m_tsrc->getTextureName(p.layer.texture_id)<<"^[crack";
1127 if(p.layer.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1128 os<<"o"; // use ^[cracko
1129 os<<":"<<(u32)p.layer.animation_frame_count<<":";
1130 m_crack_materials.insert(std::make_pair(std::pair<u8, u32>(layer, i), os.str()));
1131 // Replace tile texture with the cracked one
1132 p.layer.texture = m_tsrc->getTextureForMesh(
1134 &p.layer.texture_id);
1136 // - Texture animation
1137 if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) {
1138 // Add to MapBlockMesh in order to animate these tiles
1139 m_animation_tiles[std::pair<u8, u32>(layer, i)] = p.layer;
1140 m_animation_frames[std::pair<u8, u32>(layer, i)] = 0;
1141 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1142 // Get starting position from noise
1143 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 100000 * (2.0 + noise3d(
1144 data->m_blockpos.X, data->m_blockpos.Y,
1145 data->m_blockpos.Z, 0));
1147 // Play all synchronized
1148 m_animation_frame_offsets[std::pair<u8, u32>(layer, i)] = 0;
1150 // Replace tile texture with the first animation frame
1151 FrameSpec animation_frame = p.layer.frames[0];
1152 p.layer.texture = animation_frame.texture;
1155 if (!m_enable_shaders) {
1156 // Extract colors for day-night animation
1157 // Dummy sunlight to handle non-sunlit areas
1158 video::SColorf sunlight;
1159 get_sunlight_color(&sunlight, 0);
1161 m_use_tangent_vertices ?
1162 p.tangent_vertices.size() : p.vertices.size();
1163 for (u32 j = 0; j < vertex_count; j++) {
1165 if (m_use_tangent_vertices) {
1166 vc = &p.tangent_vertices[j].Color;
1168 vc = &p.vertices[j].Color;
1170 video::SColor copy(*vc);
1171 if (vc->getAlpha() == 0) // No sunlight - no need to animate
1172 final_color_blend(vc, copy, sunlight); // Finalize color
1173 else // Record color to animate
1174 m_daynight_diffs[std::pair<u8, u32>(layer, i)][j] = copy;
1176 // The sunlight ratio has been stored,
1177 // delete alpha (for the final rendering).
1183 video::SMaterial material;
1184 material.setFlag(video::EMF_LIGHTING, false);
1185 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1186 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1187 material.setFlag(video::EMF_FOG_ENABLE, true);
1188 material.setTexture(0, p.layer.texture);
1190 if (m_enable_shaders) {
1191 material.MaterialType = m_shdrsrc->getShaderInfo(p.layer.shader_id).material;
1192 p.layer.applyMaterialOptionsWithShaders(material);
1193 if (p.layer.normal_texture) {
1194 material.setTexture(1, p.layer.normal_texture);
1196 material.setTexture(2, p.layer.flags_texture);
1198 p.layer.applyMaterialOptions(material);
1201 scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer];
1203 // Create meshbuffer, add to mesh
1204 if (m_use_tangent_vertices) {
1205 scene::SMeshBufferTangents *buf = new scene::SMeshBufferTangents();
1207 buf->Material = material;
1209 mesh->addMeshBuffer(buf);
1212 buf->append(&p.tangent_vertices[0], p.tangent_vertices.size(),
1213 &p.indices[0], p.indices.size());
1215 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1217 buf->Material = material;
1219 mesh->addMeshBuffer(buf);
1222 buf->append(&p.vertices[0], p.vertices.size(),
1223 &p.indices[0], p.indices.size());
1229 Do some stuff to the mesh
1231 m_camera_offset = camera_offset;
1232 translateMesh(m_mesh[layer],
1233 intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1235 if (m_use_tangent_vertices) {
1236 scene::IMeshManipulator* meshmanip =
1237 m_client->getSceneManager()->getMeshManipulator();
1238 meshmanip->recalculateTangents(m_mesh[layer], true, false, false);
1244 // Usually 1-700 faces and 1-7 materials
1245 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1246 <<"and uses "<<m_mesh[layer]->getMeshBufferCount()
1247 <<" materials (meshbuffers)"<<std::endl;
1250 // Use VBO for mesh (this just would set this for ever buffer)
1252 m_mesh[layer]->setHardwareMappingHint(scene::EHM_STATIC);
1257 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1259 // Check if animation is required for this mesh
1261 !m_crack_materials.empty() ||
1262 !m_daynight_diffs.empty() ||
1263 !m_animation_tiles.empty();
1266 MapBlockMesh::~MapBlockMesh()
1268 for (int m = 0; m < MAX_TILE_LAYERS; m++) {
1269 if (m_enable_vbo && m_mesh[m])
1270 for (u32 i = 0; i < m_mesh[m]->getMeshBufferCount(); i++) {
1271 scene::IMeshBuffer *buf = m_mesh[m]->getMeshBuffer(i);
1272 m_driver->removeHardwareBuffer(buf);
1277 delete m_minimap_mapblock;
1280 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1282 if(!m_has_animation)
1284 m_animation_force_timer = 100000;
1288 m_animation_force_timer = myrand_range(5, 100);
1291 if(crack != m_last_crack)
1293 for (std::map<std::pair<u8, u32>, std::string>::iterator i =
1294 m_crack_materials.begin(); i != m_crack_materials.end(); ++i) {
1295 scene::IMeshBuffer *buf = m_mesh[i->first.first]->
1296 getMeshBuffer(i->first.second);
1297 std::string basename = i->second;
1299 // Create new texture name from original
1300 std::ostringstream os;
1301 os<<basename<<crack;
1302 u32 new_texture_id = 0;
1303 video::ITexture *new_texture =
1304 m_tsrc->getTextureForMesh(os.str(), &new_texture_id);
1305 buf->getMaterial().setTexture(0, new_texture);
1307 // If the current material is also animated,
1308 // update animation info
1309 std::map<std::pair<u8, u32>, TileLayer>::iterator anim_iter =
1310 m_animation_tiles.find(i->first);
1311 if (anim_iter != m_animation_tiles.end()){
1312 TileLayer &tile = anim_iter->second;
1313 tile.texture = new_texture;
1314 tile.texture_id = new_texture_id;
1315 // force animation update
1316 m_animation_frames[i->first] = -1;
1320 m_last_crack = crack;
1323 // Texture animation
1324 for (std::map<std::pair<u8, u32>, TileLayer>::iterator i =
1325 m_animation_tiles.begin(); i != m_animation_tiles.end(); ++i) {
1326 const TileLayer &tile = i->second;
1327 // Figure out current frame
1328 int frameoffset = m_animation_frame_offsets[i->first];
1329 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1330 + frameoffset) % tile.animation_frame_count;
1331 // If frame doesn't change, skip
1332 if(frame == m_animation_frames[i->first])
1335 m_animation_frames[i->first] = frame;
1337 scene::IMeshBuffer *buf = m_mesh[i->first.first]->
1338 getMeshBuffer(i->first.second);
1340 FrameSpec animation_frame = tile.frames[frame];
1341 buf->getMaterial().setTexture(0, animation_frame.texture);
1342 if (m_enable_shaders) {
1343 if (animation_frame.normal_texture) {
1344 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1346 buf->getMaterial().setTexture(2, animation_frame.flags_texture);
1350 // Day-night transition
1351 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1353 // Force reload mesh to VBO
1355 for (int m = 0; m < MAX_TILE_LAYERS; m++)
1356 m_mesh[m]->setDirty();
1357 video::SColorf day_color;
1358 get_sunlight_color(&day_color, daynight_ratio);
1359 for(std::map<std::pair<u8, u32>, std::map<u32, video::SColor > >::iterator
1360 i = m_daynight_diffs.begin();
1361 i != m_daynight_diffs.end(); ++i)
1363 scene::IMeshBuffer *buf = m_mesh[i->first.first]->
1364 getMeshBuffer(i->first.second);
1365 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1366 for(std::map<u32, video::SColor >::iterator
1367 j = i->second.begin();
1368 j != i->second.end(); ++j)
1370 final_color_blend(&(vertices[j->first].Color),
1371 j->second, day_color);
1374 m_last_daynight_ratio = daynight_ratio;
1380 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1382 if (camera_offset != m_camera_offset) {
1383 for (u8 layer = 0; layer < 2; layer++) {
1384 translateMesh(m_mesh[layer],
1385 intToFloat(m_camera_offset - camera_offset, BS));
1387 m_mesh[layer]->setDirty();
1390 m_camera_offset = camera_offset;
1398 void MeshCollector::append(const TileSpec &tile,
1399 const video::S3DVertex *vertices, u32 numVertices,
1400 const u16 *indices, u32 numIndices)
1402 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1403 const TileLayer *layer = &tile.layers[layernum];
1404 if (layer->texture_id == 0)
1406 append(*layer, vertices, numVertices, indices, numIndices,
1411 void MeshCollector::append(const TileLayer &layer,
1412 const video::S3DVertex *vertices, u32 numVertices,
1413 const u16 *indices, u32 numIndices, u8 layernum)
1415 if (numIndices > 65535) {
1416 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1419 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1421 PreMeshBuffer *p = NULL;
1422 for (u32 i = 0; i < buffers->size(); i++) {
1423 PreMeshBuffer &pp = (*buffers)[i];
1424 if (pp.layer != layer)
1426 if (pp.indices.size() + numIndices > 65535)
1436 buffers->push_back(pp);
1437 p = &(*buffers)[buffers->size() - 1];
1441 if (m_use_tangent_vertices) {
1442 vertex_count = p->tangent_vertices.size();
1443 for (u32 i = 0; i < numVertices; i++) {
1444 video::S3DVertexTangents vert(vertices[i].Pos, vertices[i].Normal,
1445 vertices[i].Color, vertices[i].TCoords);
1446 p->tangent_vertices.push_back(vert);
1449 vertex_count = p->vertices.size();
1450 for (u32 i = 0; i < numVertices; i++) {
1451 video::S3DVertex vert(vertices[i].Pos, vertices[i].Normal,
1452 vertices[i].Color, vertices[i].TCoords);
1453 p->vertices.push_back(vert);
1457 for (u32 i = 0; i < numIndices; i++) {
1458 u32 j = indices[i] + vertex_count;
1459 p->indices.push_back(j);
1464 MeshCollector - for meshnodes and converted drawtypes.
1467 void MeshCollector::append(const TileSpec &tile,
1468 const video::S3DVertex *vertices, u32 numVertices,
1469 const u16 *indices, u32 numIndices,
1470 v3f pos, video::SColor c, u8 light_source)
1472 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
1473 const TileLayer *layer = &tile.layers[layernum];
1474 if (layer->texture_id == 0)
1476 append(*layer, vertices, numVertices, indices, numIndices, pos,
1477 c, light_source, layernum);
1481 void MeshCollector::append(const TileLayer &layer,
1482 const video::S3DVertex *vertices, u32 numVertices,
1483 const u16 *indices, u32 numIndices,
1484 v3f pos, video::SColor c, u8 light_source, u8 layernum)
1486 if (numIndices > 65535) {
1487 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1490 std::vector<PreMeshBuffer> *buffers = &prebuffers[layernum];
1492 PreMeshBuffer *p = NULL;
1493 for (u32 i = 0; i < buffers->size(); i++) {
1494 PreMeshBuffer &pp = (*buffers)[i];
1495 if(pp.layer != layer)
1497 if(pp.indices.size() + numIndices > 65535)
1507 buffers->push_back(pp);
1508 p = &(*buffers)[buffers->size() - 1];
1511 video::SColor original_c = c;
1513 if (m_use_tangent_vertices) {
1514 vertex_count = p->tangent_vertices.size();
1515 for (u32 i = 0; i < numVertices; i++) {
1516 if (!light_source) {
1518 applyFacesShading(c, vertices[i].Normal);
1520 video::S3DVertexTangents vert(vertices[i].Pos + pos,
1521 vertices[i].Normal, c, vertices[i].TCoords);
1522 p->tangent_vertices.push_back(vert);
1525 vertex_count = p->vertices.size();
1526 for (u32 i = 0; i < numVertices; i++) {
1527 if (!light_source) {
1529 applyFacesShading(c, vertices[i].Normal);
1531 video::S3DVertex vert(vertices[i].Pos + pos, vertices[i].Normal, c,
1532 vertices[i].TCoords);
1533 p->vertices.push_back(vert);
1537 for (u32 i = 0; i < numIndices; i++) {
1538 u32 j = indices[i] + vertex_count;
1539 p->indices.push_back(j);
1543 void MeshCollector::applyTileColors()
1545 if (m_use_tangent_vertices)
1546 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1547 std::vector<PreMeshBuffer> *p = &prebuffers[layer];
1548 for (std::vector<PreMeshBuffer>::iterator it = p->begin();
1549 it != p->end(); ++it) {
1550 video::SColor tc = it->layer.color;
1551 if (tc == video::SColor(0xFFFFFFFF))
1553 for (u32 index = 0; index < it->tangent_vertices.size(); index++) {
1554 video::SColor *c = &it->tangent_vertices[index].Color;
1555 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1556 c->getGreen() * tc.getGreen() / 255,
1557 c->getBlue() * tc.getBlue() / 255);
1562 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
1563 std::vector<PreMeshBuffer> *p = &prebuffers[layer];
1564 for (std::vector<PreMeshBuffer>::iterator it = p->begin();
1565 it != p->end(); ++it) {
1566 video::SColor tc = it->layer.color;
1567 if (tc == video::SColor(0xFFFFFFFF))
1569 for (u32 index = 0; index < it->vertices.size(); index++) {
1570 video::SColor *c = &it->vertices[index].Color;
1571 c->set(c->getAlpha(), c->getRed() * tc.getRed() / 255,
1572 c->getGreen() * tc.getGreen() / 255,
1573 c->getBlue() * tc.getBlue() / 255);
1579 video::SColor encode_light(u16 light, u8 emissive_light)
1582 u32 day = (light & 0xff);
1583 u32 night = (light >> 8);
1584 // Add emissive light
1585 night += emissive_light * 2.5f;
1588 // Since we don't know if the day light is sunlight or
1589 // artificial light, assume it is artificial when the night
1590 // light bank is also lit.
1595 u32 sum = day + night;
1596 // Ratio of sunlight:
1599 r = day * 255 / sum;
1603 float b = (day + night) / 2;
1604 return video::SColor(r, b, b, b);