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"
34 static void applyFacesShading(video::SColor& color, float factor)
36 color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
37 color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
44 MeshMakeData::MeshMakeData(IGameDef *gamedef, bool use_shaders):
46 m_blockpos(-1337,-1337,-1337),
47 m_crack_pos_relative(-1337, -1337, -1337),
48 m_highlighted_pos_relative(-1337, -1337, -1337),
49 m_smooth_lighting(false),
51 m_highlight_mesh_color(255, 255, 255, 255),
53 m_use_shaders(use_shaders)
56 void MeshMakeData::fill(MapBlock *block)
58 m_blockpos = block->getPos();
60 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
66 // Allocate this block + neighbors
68 VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE,
69 blockpos_nodes + v3s16(1,1,1) * MAP_BLOCKSIZE*2-v3s16(1,1,1));
70 m_vmanip.addArea(voxel_area);
73 //TimeTaker timer("copy central block data");
77 block->copyTo(m_vmanip);
80 //TimeTaker timer("copy neighbor block data");
84 Copy neighbors. This is lightning fast.
85 Copying only the borders would be *very* slow.
89 Map *map = block->getParent();
91 for(u16 i=0; i<26; i++)
93 const v3s16 &dir = g_26dirs[i];
94 v3s16 bp = m_blockpos + dir;
95 MapBlock *b = map->getBlockNoCreateNoEx(bp);
102 void MeshMakeData::fillSingleNode(MapNode *node)
104 m_blockpos = v3s16(0,0,0);
106 v3s16 blockpos_nodes = v3s16(0,0,0);
107 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
108 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
109 s32 volume = area.getVolume();
110 s32 our_node_index = area.index(1,1,1);
112 // Allocate this block + neighbors
114 m_vmanip.addArea(area);
117 MapNode *data = new MapNode[volume];
118 for(s32 i = 0; i < volume; i++)
120 if(i == our_node_index)
126 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
129 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
133 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
136 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
139 void MeshMakeData::setHighlighted(v3s16 highlighted_pos, bool show_hud)
141 m_show_hud = show_hud;
142 m_highlighted_pos_relative = highlighted_pos - m_blockpos*MAP_BLOCKSIZE;
145 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
147 m_smooth_lighting = smooth_lighting;
151 Light and vertex color functions
155 Calculate non-smooth lighting at interior of node.
158 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
159 INodeDefManager *ndef)
161 u8 light = n.getLight(bank, ndef);
165 light = undiminish_light(light);
170 light = diminish_light(light);
174 return decode_light(light);
178 Calculate non-smooth lighting at interior of node.
181 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
183 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
184 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
185 return day | (night << 8);
189 Calculate non-smooth lighting at face of node.
192 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
193 v3s16 face_dir, INodeDefManager *ndef)
196 u8 l1 = n.getLight(bank, ndef);
197 u8 l2 = n2.getLight(bank, ndef);
203 // Boost light level for light sources
204 u8 light_source = MYMAX(ndef->get(n).light_source,
205 ndef->get(n2).light_source);
206 if(light_source > light)
207 light = light_source;
209 return decode_light(light);
213 Calculate non-smooth lighting at face of node.
216 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
218 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
219 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
220 return day | (night << 8);
224 Calculate smooth lighting at the XYZ- corner of p.
227 static u16 getSmoothLightCombined(v3s16 p, MeshMakeData *data)
229 static const v3s16 dirs8[8] = {
240 INodeDefManager *ndef = data->m_gamedef->ndef();
242 u16 ambient_occlusion = 0;
244 u8 light_source_max = 0;
248 for (u32 i = 0; i < 8; i++)
250 const MapNode &n = data->m_vmanip.getNodeRefUnsafeCheckFlags(p - dirs8[i]);
252 // if it's CONTENT_IGNORE we can't do any light calculations
253 if (n.getContent() == CONTENT_IGNORE) {
257 const ContentFeatures &f = ndef->get(n);
258 if (f.light_source > light_source_max)
259 light_source_max = f.light_source;
260 // Check f.solidness because fast-style leaves look better this way
261 if (f.param_type == CPT_LIGHT && f.solidness != 2) {
262 light_day += decode_light(n.getLightNoChecks(LIGHTBANK_DAY, &f));
263 light_night += decode_light(n.getLightNoChecks(LIGHTBANK_NIGHT, &f));
273 light_day /= light_count;
274 light_night /= light_count;
276 // Boost brightness around light sources
277 bool skip_ambient_occlusion_day = false;
278 if(decode_light(light_source_max) >= light_day) {
279 light_day = decode_light(light_source_max);
280 skip_ambient_occlusion_day = true;
283 bool skip_ambient_occlusion_night = false;
284 if(decode_light(light_source_max) >= light_night) {
285 light_night = decode_light(light_source_max);
286 skip_ambient_occlusion_night = true;
289 if (ambient_occlusion > 4)
291 static const float ao_gamma = rangelim(
292 g_settings->getFloat("ambient_occlusion_gamma"), 0.25, 4.0);
294 // Table of gamma space multiply factors.
295 static const float light_amount[3] = {
296 powf(0.75, 1.0 / ao_gamma),
297 powf(0.5, 1.0 / ao_gamma),
298 powf(0.25, 1.0 / ao_gamma)
301 //calculate table index for gamma space multiplier
302 ambient_occlusion -= 5;
304 if (!skip_ambient_occlusion_day)
305 light_day = rangelim(core::round32(light_day*light_amount[ambient_occlusion]), 0, 255);
306 if (!skip_ambient_occlusion_night)
307 light_night = rangelim(core::round32(light_night*light_amount[ambient_occlusion]), 0, 255);
310 return light_day | (light_night << 8);
314 Calculate smooth lighting at the given corner of p.
317 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
319 if(corner.X == 1) p.X += 1;
320 // else corner.X == -1
321 if(corner.Y == 1) p.Y += 1;
322 // else corner.Y == -1
323 if(corner.Z == 1) p.Z += 1;
324 // else corner.Z == -1
326 return getSmoothLightCombined(p, data);
330 Converts from day + night color values (0..255)
331 and a given daynight_ratio to the final SColor shown on screen.
333 void finalColorBlend(video::SColor& result,
334 u8 day, u8 night, u32 daynight_ratio)
336 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
340 b += (day - night) / 13;
341 rg -= (day - night) / 23;
343 // Emphase blue a bit in darker places
344 // Each entry of this array represents a range of 8 blue levels
345 static const u8 emphase_blue_when_dark[32] = {
346 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
347 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
349 b += emphase_blue_when_dark[irr::core::clamp(b, 0, 255) / 8];
350 b = irr::core::clamp(b, 0, 255);
352 // Artificial light is yellow-ish
353 static const u8 emphase_yellow_when_artificial[16] = {
354 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
356 rg += emphase_yellow_when_artificial[night/16];
357 rg = irr::core::clamp(rg, 0, 255);
365 Mesh generation helpers
369 vertex_dirs: v3s16[4]
371 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
374 If looked from outside the node towards the face, the corners are:
380 if(dir == v3s16(0,0,1))
382 // If looking towards z+, this is the face that is behind
383 // the center point, facing towards z+.
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(0,0,-1))
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(-1,0,0))
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);
421 else if(dir == v3s16(0,-1,0))
423 // faces towards Y- (assume Z+ as "down" in texture)
424 vertex_dirs[0] = v3s16( 1,-1, 1);
425 vertex_dirs[1] = v3s16(-1,-1, 1);
426 vertex_dirs[2] = v3s16(-1,-1,-1);
427 vertex_dirs[3] = v3s16( 1,-1,-1);
434 video::S3DVertex vertices[4]; // Precalculated vertices
437 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
438 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
440 // Position is at the center of the cube.
449 v3s16 vertex_dirs[4];
450 getNodeVertexDirs(dir, vertex_dirs);
454 switch (tile.rotation)
460 vertex_dirs[0] = vertex_dirs[3];
461 vertex_dirs[3] = vertex_dirs[2];
462 vertex_dirs[2] = vertex_dirs[1];
472 vertex_dirs[0] = vertex_dirs[2];
475 vertex_dirs[1] = vertex_dirs[3];
486 vertex_dirs[0] = vertex_dirs[1];
487 vertex_dirs[1] = vertex_dirs[2];
488 vertex_dirs[2] = vertex_dirs[3];
498 vertex_dirs[0] = vertex_dirs[3];
499 vertex_dirs[3] = vertex_dirs[2];
500 vertex_dirs[2] = vertex_dirs[1];
512 vertex_dirs[0] = vertex_dirs[1];
513 vertex_dirs[1] = vertex_dirs[2];
514 vertex_dirs[2] = vertex_dirs[3];
526 vertex_dirs[0] = vertex_dirs[3];
527 vertex_dirs[3] = vertex_dirs[2];
528 vertex_dirs[2] = vertex_dirs[1];
540 vertex_dirs[0] = vertex_dirs[1];
541 vertex_dirs[1] = vertex_dirs[2];
542 vertex_dirs[2] = vertex_dirs[3];
564 for(u16 i=0; i<4; i++)
567 BS/2*vertex_dirs[i].X,
568 BS/2*vertex_dirs[i].Y,
569 BS/2*vertex_dirs[i].Z
573 for(u16 i=0; i<4; i++)
575 vertex_pos[i].X *= scale.X;
576 vertex_pos[i].Y *= scale.Y;
577 vertex_pos[i].Z *= scale.Z;
578 vertex_pos[i] += pos;
582 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
583 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
584 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
586 v3f normal(dir.X, dir.Y, dir.Z);
588 u8 alpha = tile.alpha;
590 dest.push_back(FastFace());
592 FastFace& face = *dest.rbegin();
594 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
595 MapBlock_LightColor(alpha, li0, light_source),
596 core::vector2d<f32>(x0+w*abs_scale, y0+h));
597 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
598 MapBlock_LightColor(alpha, li1, light_source),
599 core::vector2d<f32>(x0, y0+h));
600 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
601 MapBlock_LightColor(alpha, li2, light_source),
602 core::vector2d<f32>(x0, y0));
603 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
604 MapBlock_LightColor(alpha, li3, light_source),
605 core::vector2d<f32>(x0+w*abs_scale, y0));
611 Nodes make a face if contents differ and solidness differs.
614 1: Face uses m1's content
615 2: Face uses m2's content
616 equivalent: Whether the blocks share the same face (eg. water and glass)
618 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
620 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
621 INodeDefManager *ndef)
625 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
628 bool contents_differ = (m1 != m2);
630 const ContentFeatures &f1 = ndef->get(m1);
631 const ContentFeatures &f2 = ndef->get(m2);
633 // Contents don't differ for different forms of same liquid
634 if(f1.sameLiquid(f2))
635 contents_differ = false;
637 u8 c1 = f1.solidness;
638 u8 c2 = f2.solidness;
640 bool solidness_differs = (c1 != c2);
641 bool makes_face = contents_differ && solidness_differs;
643 if(makes_face == false)
647 c1 = f1.visual_solidness;
649 c2 = f2.visual_solidness;
653 // If same solidness, liquid takes precense
667 Gets nth node tile (0 <= n <= 5).
669 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
671 INodeDefManager *ndef = data->m_gamedef->ndef();
672 TileSpec spec = ndef->get(mn).tiles[tileindex];
673 // Apply temporary crack
674 if (p == data->m_crack_pos_relative)
675 spec.material_flags |= MATERIAL_FLAG_CRACK;
680 Gets node tile given a face direction.
682 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
684 INodeDefManager *ndef = data->m_gamedef->ndef();
686 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
687 // (0,0,1), (0,0,-1) or (0,0,0)
688 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
690 // Convert direction to single integer for table lookup
695 // 4 = invalid, treat as (0,0,0)
699 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
701 // Get rotation for things like chests
702 u8 facedir = mn.getFaceDir(ndef);
704 static const u16 dir_to_tile[24 * 16] =
706 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
707 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
708 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
709 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
710 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
712 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
713 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
714 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
715 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
717 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
718 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
719 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
720 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
722 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
723 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
724 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
725 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
727 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
728 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
729 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
730 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
732 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
733 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
734 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
735 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
738 u16 tile_index=facedir*16 + dir_i;
739 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
740 spec.rotation=dir_to_tile[tile_index + 1];
741 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
745 static void getTileInfo(
749 const v3s16 &face_dir,
753 v3s16 &face_dir_corrected,
759 VoxelManipulator &vmanip = data->m_vmanip;
760 INodeDefManager *ndef = data->m_gamedef->ndef();
761 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
763 MapNode &n0 = vmanip.getNodeRefUnsafe(blockpos_nodes + p);
765 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
766 if (n0.getContent() == CONTENT_IGNORE) {
771 const MapNode &n1 = vmanip.getNodeRefUnsafeCheckFlags(blockpos_nodes + p + face_dir);
773 if (n1.getContent() == CONTENT_IGNORE) {
779 bool equivalent = false;
780 u8 mf = face_contents(n0.getContent(), n1.getContent(),
793 tile = getNodeTile(n0, p, face_dir, data);
795 face_dir_corrected = face_dir;
796 light_source = ndef->get(n0).light_source;
800 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
801 p_corrected = p + face_dir;
802 face_dir_corrected = -face_dir;
803 light_source = ndef->get(n1).light_source;
806 // eg. water and glass
808 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
810 if(data->m_smooth_lighting == false)
812 lights[0] = lights[1] = lights[2] = lights[3] =
813 getFaceLight(n0, n1, face_dir, ndef);
817 v3s16 vertex_dirs[4];
818 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
819 for(u16 i=0; i<4; i++)
821 lights[i] = getSmoothLight(
822 blockpos_nodes + p_corrected,
823 vertex_dirs[i], data);
832 translate_dir: unit vector with only one of x, y or z
833 face_dir: unit vector with only one of x, y or z
835 static void updateFastFaceRow(
842 std::vector<FastFace> &dest)
846 u16 continuous_tiles_count = 0;
848 bool makes_face = false;
850 v3s16 face_dir_corrected;
851 u16 lights[4] = {0,0,0,0};
854 getTileInfo(data, p, face_dir,
855 makes_face, p_corrected, face_dir_corrected,
856 lights, tile, light_source);
858 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
860 // If tiling can be done, this is set to false in the next step
861 bool next_is_different = true;
865 bool next_makes_face = false;
866 v3s16 next_p_corrected;
867 v3s16 next_face_dir_corrected;
868 u16 next_lights[4] = {0,0,0,0};
870 u8 next_light_source = 0;
872 // If at last position, there is nothing to compare to and
873 // the face must be drawn anyway
874 if(j != MAP_BLOCKSIZE - 1)
876 p_next = p + translate_dir;
878 getTileInfo(data, p_next, face_dir,
879 next_makes_face, next_p_corrected,
880 next_face_dir_corrected, next_lights,
881 next_tile, next_light_source);
883 if(next_makes_face == makes_face
884 && next_p_corrected == p_corrected + translate_dir
885 && next_face_dir_corrected == face_dir_corrected
886 && next_lights[0] == lights[0]
887 && next_lights[1] == lights[1]
888 && next_lights[2] == lights[2]
889 && next_lights[3] == lights[3]
891 && tile.rotation == 0
892 && next_light_source == light_source)
894 next_is_different = false;
898 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
899 next_makes_face != makes_face ? 1 : 0);
900 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
901 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
902 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
903 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
904 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
905 (next_lights[0] != lights[0] ||
906 next_lights[0] != lights[0] ||
907 next_lights[0] != lights[0] ||
908 next_lights[0] != lights[0]) ? 1 : 0);
909 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
910 !(next_tile == tile) ? 1 : 0);
913 /*g_profiler->add("Meshgen: Total faces checked", 1);
915 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
918 g_profiler->add("Meshgen: diff: last position", 1);*/
921 continuous_tiles_count++;
923 if(next_is_different)
926 Create a face if there should be one
930 // Floating point conversion of the position vector
931 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
932 // Center point of face (kind of)
933 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
934 if(continuous_tiles_count != 1)
935 sp += 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, light_source,
952 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
953 for(int i = 1; i < continuous_tiles_count; i++){
954 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
958 continuous_tiles_count = 0;
960 makes_face = next_makes_face;
961 p_corrected = next_p_corrected;
962 face_dir_corrected = next_face_dir_corrected;
963 lights[0] = next_lights[0];
964 lights[1] = next_lights[1];
965 lights[2] = next_lights[2];
966 lights[3] = next_lights[3];
968 light_source = next_light_source;
975 static void updateAllFastFaceRows(MeshMakeData *data,
976 std::vector<FastFace> &dest)
979 Go through every y,z and get top(y+) faces in rows of x+
981 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
982 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
983 updateFastFaceRow(data,
987 v3s16(0,1,0), //face dir
994 Go through every x,y and get right(x+) faces in rows of z+
996 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
997 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
998 updateFastFaceRow(data,
1002 v3s16(1,0,0), //face dir
1009 Go through every y,z and get back(z+) faces in rows of x+
1011 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1012 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1013 updateFastFaceRow(data,
1017 v3s16(0,0,1), //face dir
1028 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1029 m_mesh(new scene::SMesh()),
1030 m_gamedef(data->m_gamedef),
1031 m_animation_force_timer(0), // force initial animation
1033 m_crack_materials(),
1034 m_highlighted_materials(),
1035 m_last_daynight_ratio((u32) -1),
1038 m_enable_shaders = data->m_use_shaders;
1039 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1041 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1042 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1043 //TimeTaker timer1("MapBlockMesh()");
1045 std::vector<FastFace> fastfaces_new;
1046 fastfaces_new.reserve(512);
1049 We are including the faces of the trailing edges of the block.
1050 This means that when something changes, the caller must
1051 also update the meshes of the blocks at the leading edges.
1053 NOTE: This is the slowest part of this method.
1056 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1057 //TimeTaker timer2("updateAllFastFaceRows()");
1058 updateAllFastFaceRows(data, fastfaces_new);
1063 Convert FastFaces to MeshCollector
1066 MeshCollector collector;
1069 // avg 0ms (100ms spikes when loading textures the first time)
1070 // (NOTE: probably outdated)
1071 //TimeTaker timer2("MeshCollector building");
1073 for(u32 i=0; i<fastfaces_new.size(); i++)
1075 FastFace &f = fastfaces_new[i];
1077 const u16 indices[] = {0,1,2,2,3,0};
1078 const u16 indices_alternate[] = {0,1,3,2,3,1};
1080 if(f.tile.texture == NULL)
1083 const u16 *indices_p = indices;
1086 Revert triangles for nicer looking gradient if vertices
1087 1 and 3 have same color or 0 and 2 have different color.
1088 getRed() is the day color.
1090 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1091 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1092 indices_p = indices_alternate;
1094 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1099 Add special graphics:
1106 mapblock_mesh_generate_special(data, collector);
1108 m_highlight_mesh_color = data->m_highlight_mesh_color;
1111 Convert MeshCollector to SMesh
1113 ITextureSource *tsrc = m_gamedef->tsrc();
1114 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1116 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1118 PreMeshBuffer &p = collector.prebuffers[i];
1120 // Generate animation data
1122 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1124 // Find the texture name plus ^[crack:N:
1125 std::ostringstream os(std::ios::binary);
1126 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1127 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1128 os<<"o"; // use ^[cracko
1129 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1130 m_crack_materials.insert(std::make_pair(i, os.str()));
1131 // Replace tile texture with the cracked one
1132 p.tile.texture = tsrc->getTextureForMesh(
1134 &p.tile.texture_id);
1136 // - Texture animation
1137 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1139 // Add to MapBlockMesh in order to animate these tiles
1140 m_animation_tiles[i] = p.tile;
1141 m_animation_frames[i] = 0;
1142 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1143 // Get starting position from noise
1144 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1145 data->m_blockpos.X, data->m_blockpos.Y,
1146 data->m_blockpos.Z, 0));
1148 // Play all synchronized
1149 m_animation_frame_offsets[i] = 0;
1151 // Replace tile texture with the first animation frame
1152 FrameSpec animation_frame = p.tile.frames[0];
1153 p.tile.texture = animation_frame.texture;
1156 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1157 m_highlighted_materials.push_back(i);
1159 for(u32 j = 0; j < p.vertices.size(); j++)
1161 // Note applyFacesShading second parameter is precalculated sqrt
1162 // value for speed improvement
1163 // Skip it for lightsources and top faces.
1164 video::SColor &vc = p.vertices[j].Color;
1165 if (!vc.getBlue()) {
1166 if (p.vertices[j].Normal.Y < -0.5) {
1167 applyFacesShading (vc, 0.447213);
1168 } else if (p.vertices[j].Normal.X > 0.5) {
1169 applyFacesShading (vc, 0.670820);
1170 } else if (p.vertices[j].Normal.X < -0.5) {
1171 applyFacesShading (vc, 0.670820);
1172 } else if (p.vertices[j].Normal.Z > 0.5) {
1173 applyFacesShading (vc, 0.836660);
1174 } else if (p.vertices[j].Normal.Z < -0.5) {
1175 applyFacesShading (vc, 0.836660);
1178 if(!m_enable_shaders)
1180 // - Classic lighting (shaders handle this by themselves)
1181 // Set initial real color and store for later updates
1182 u8 day = vc.getRed();
1183 u8 night = vc.getGreen();
1184 finalColorBlend(vc, day, night, 1000);
1186 m_daynight_diffs[i][j] = std::make_pair(day, night);
1191 video::SMaterial material;
1192 material.setFlag(video::EMF_LIGHTING, false);
1193 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1194 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1195 material.setFlag(video::EMF_FOG_ENABLE, true);
1196 material.setTexture(0, p.tile.texture);
1198 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1199 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1201 if (m_enable_shaders) {
1202 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1203 p.tile.applyMaterialOptionsWithShaders(material);
1204 if (p.tile.normal_texture) {
1205 material.setTexture(1, p.tile.normal_texture);
1206 material.setTexture(2, tsrc->getTextureForMesh("enable_img.png"));
1208 material.setTexture(2, tsrc->getTextureForMesh("disable_img.png"));
1211 p.tile.applyMaterialOptions(material);
1215 // Create meshbuffer
1216 // This is a "Standard MeshBuffer",
1217 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1218 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1220 buf->Material = material;
1222 m_mesh->addMeshBuffer(buf);
1225 buf->append(&p.vertices[0], p.vertices.size(),
1226 &p.indices[0], p.indices.size());
1229 m_camera_offset = camera_offset;
1232 Do some stuff to the mesh
1235 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1240 // Usually 1-700 faces and 1-7 materials
1241 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1242 <<"and uses "<<m_mesh->getMeshBufferCount()
1243 <<" materials (meshbuffers)"<<std::endl;
1246 // Use VBO for mesh (this just would set this for ever buffer)
1247 // This will lead to infinite memory usage because or irrlicht.
1248 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1251 NOTE: If that is enabled, some kind of a queue to the main
1252 thread should be made which would call irrlicht to delete
1253 the hardware buffer and then delete the mesh
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() ||
1264 !m_highlighted_materials.empty();
1267 MapBlockMesh::~MapBlockMesh()
1273 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1276 if(!m_has_animation)
1278 m_animation_force_timer = 100000;
1282 m_animation_force_timer = myrand_range(5, 100);
1285 if(crack != m_last_crack)
1287 for(std::map<u32, std::string>::iterator
1288 i = m_crack_materials.begin();
1289 i != m_crack_materials.end(); i++)
1291 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1292 std::string basename = i->second;
1294 // Create new texture name from original
1295 ITextureSource *tsrc = m_gamedef->getTextureSource();
1296 std::ostringstream os;
1297 os<<basename<<crack;
1298 u32 new_texture_id = 0;
1299 video::ITexture *new_texture =
1300 tsrc->getTextureForMesh(os.str(), &new_texture_id);
1301 buf->getMaterial().setTexture(0, new_texture);
1303 // If the current material is also animated,
1304 // update animation info
1305 std::map<u32, TileSpec>::iterator anim_iter =
1306 m_animation_tiles.find(i->first);
1307 if(anim_iter != m_animation_tiles.end()){
1308 TileSpec &tile = anim_iter->second;
1309 tile.texture = new_texture;
1310 tile.texture_id = new_texture_id;
1311 // force animation update
1312 m_animation_frames[i->first] = -1;
1316 m_last_crack = crack;
1319 // Texture animation
1320 for(std::map<u32, TileSpec>::iterator
1321 i = m_animation_tiles.begin();
1322 i != m_animation_tiles.end(); i++)
1324 const TileSpec &tile = i->second;
1325 // Figure out current frame
1326 int frameoffset = m_animation_frame_offsets[i->first];
1327 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1328 + frameoffset) % tile.animation_frame_count;
1329 // If frame doesn't change, skip
1330 if(frame == m_animation_frames[i->first])
1333 m_animation_frames[i->first] = frame;
1335 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1336 ITextureSource *tsrc = m_gamedef->getTextureSource();
1338 FrameSpec animation_frame = tile.frames[frame];
1339 buf->getMaterial().setTexture(0, animation_frame.texture);
1340 if (m_enable_shaders) {
1341 if (animation_frame.normal_texture) {
1342 buf->getMaterial().setTexture(1, animation_frame.normal_texture);
1343 buf->getMaterial().setTexture(2, tsrc->getTextureForMesh("enable_img.png"));
1345 buf->getMaterial().setTexture(2, tsrc->getTextureForMesh("disable_img.png"));
1350 // Day-night transition
1351 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1353 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1354 i = m_daynight_diffs.begin();
1355 i != m_daynight_diffs.end(); i++)
1357 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1358 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1359 for(std::map<u32, std::pair<u8, u8 > >::iterator
1360 j = i->second.begin();
1361 j != i->second.end(); j++)
1363 u32 vertexIndex = j->first;
1364 u8 day = j->second.first;
1365 u8 night = j->second.second;
1366 finalColorBlend(vertices[vertexIndex].Color,
1367 day, night, daynight_ratio);
1370 m_last_daynight_ratio = daynight_ratio;
1373 // Node highlighting
1374 if (m_enable_highlighting) {
1375 u8 day = m_highlight_mesh_color.getRed();
1376 u8 night = m_highlight_mesh_color.getGreen();
1378 finalColorBlend(hc, day, night, daynight_ratio);
1379 float sin_r = 0.07 * sin(1.5 * time);
1380 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1381 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1382 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1383 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1384 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1386 for(std::list<u32>::iterator
1387 i = m_highlighted_materials.begin();
1388 i != m_highlighted_materials.end(); i++)
1390 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1391 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1392 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1393 vertices[j].Color = hc;
1400 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1402 if (camera_offset != m_camera_offset) {
1403 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1404 m_camera_offset = camera_offset;
1412 void MeshCollector::append(const TileSpec &tile,
1413 const video::S3DVertex *vertices, u32 numVertices,
1414 const u16 *indices, u32 numIndices)
1416 if(numIndices > 65535)
1418 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1422 PreMeshBuffer *p = NULL;
1423 for(u32 i=0; i<prebuffers.size(); i++)
1425 PreMeshBuffer &pp = prebuffers[i];
1428 if(pp.indices.size() + numIndices > 65535)
1439 prebuffers.push_back(pp);
1440 p = &prebuffers[prebuffers.size()-1];
1443 u32 vertex_count = p->vertices.size();
1444 for(u32 i=0; i<numIndices; i++)
1446 u32 j = indices[i] + vertex_count;
1447 p->indices.push_back(j);
1449 for(u32 i=0; i<numVertices; i++)
1451 p->vertices.push_back(vertices[i]);
1456 MeshCollector - for meshnodes and converted drawtypes.
1459 void MeshCollector::append(const TileSpec &tile,
1460 const video::S3DVertex *vertices, u32 numVertices,
1461 const u16 *indices, u32 numIndices,
1462 v3f pos, video::SColor c)
1464 if(numIndices > 65535)
1466 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1470 PreMeshBuffer *p = NULL;
1471 for(u32 i=0; i<prebuffers.size(); i++)
1473 PreMeshBuffer &pp = prebuffers[i];
1476 if(pp.indices.size() + numIndices > 65535)
1487 prebuffers.push_back(pp);
1488 p = &prebuffers[prebuffers.size()-1];
1491 u32 vertex_count = p->vertices.size();
1492 for(u32 i=0; i<numIndices; i++)
1494 u32 j = indices[i] + vertex_count;
1495 p->indices.push_back(j);
1497 for(u32 i=0; i<numVertices; i++)
1499 video::S3DVertex vert = vertices[i];
1502 p->vertices.push_back(vert);