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"
24 #include "main.h" // for g_profiler
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
35 static void applyFacesShading(video::SColor& color, float factor)
37 color.setRed(core::clamp(core::round32(color.getRed()*factor), 0, 255));
38 color.setGreen(core::clamp(core::round32(color.getGreen()*factor), 0, 255));
45 MeshMakeData::MeshMakeData(IGameDef *gamedef):
47 m_blockpos(-1337,-1337,-1337),
48 m_crack_pos_relative(-1337, -1337, -1337),
49 m_highlighted_pos_relative(-1337, -1337, -1337),
50 m_smooth_lighting(false),
52 m_highlight_mesh_color(255, 255, 255, 255),
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 MapNode n = data->m_vmanip.getNodeNoEx(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)
442 // Position is at the center of the cube.
451 v3s16 vertex_dirs[4];
452 getNodeVertexDirs(dir, vertex_dirs);
456 switch (tile.rotation)
462 vertex_dirs[0] = vertex_dirs[3];
463 vertex_dirs[3] = vertex_dirs[2];
464 vertex_dirs[2] = vertex_dirs[1];
474 vertex_dirs[0] = vertex_dirs[2];
477 vertex_dirs[1] = vertex_dirs[3];
488 vertex_dirs[0] = vertex_dirs[1];
489 vertex_dirs[1] = vertex_dirs[2];
490 vertex_dirs[2] = vertex_dirs[3];
500 vertex_dirs[0] = vertex_dirs[3];
501 vertex_dirs[3] = vertex_dirs[2];
502 vertex_dirs[2] = vertex_dirs[1];
514 vertex_dirs[0] = vertex_dirs[1];
515 vertex_dirs[1] = vertex_dirs[2];
516 vertex_dirs[2] = vertex_dirs[3];
528 vertex_dirs[0] = vertex_dirs[3];
529 vertex_dirs[3] = vertex_dirs[2];
530 vertex_dirs[2] = vertex_dirs[1];
542 vertex_dirs[0] = vertex_dirs[1];
543 vertex_dirs[1] = vertex_dirs[2];
544 vertex_dirs[2] = vertex_dirs[3];
566 for(u16 i=0; i<4; i++)
569 BS/2*vertex_dirs[i].X,
570 BS/2*vertex_dirs[i].Y,
571 BS/2*vertex_dirs[i].Z
575 for(u16 i=0; i<4; i++)
577 vertex_pos[i].X *= scale.X;
578 vertex_pos[i].Y *= scale.Y;
579 vertex_pos[i].Z *= scale.Z;
580 vertex_pos[i] += pos;
584 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
585 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
586 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
588 v3f normal(dir.X, dir.Y, dir.Z);
590 u8 alpha = tile.alpha;
592 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
593 MapBlock_LightColor(alpha, li0, light_source),
594 core::vector2d<f32>(x0+w*abs_scale, y0+h));
595 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
596 MapBlock_LightColor(alpha, li1, light_source),
597 core::vector2d<f32>(x0, y0+h));
598 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
599 MapBlock_LightColor(alpha, li2, light_source),
600 core::vector2d<f32>(x0, y0));
601 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
602 MapBlock_LightColor(alpha, li3, light_source),
603 core::vector2d<f32>(x0+w*abs_scale, y0));
606 dest.push_back(face);
610 Nodes make a face if contents differ and solidness differs.
613 1: Face uses m1's content
614 2: Face uses m2's content
615 equivalent: Whether the blocks share the same face (eg. water and glass)
617 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
619 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
620 INodeDefManager *ndef)
624 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
627 bool contents_differ = (m1 != m2);
629 const ContentFeatures &f1 = ndef->get(m1);
630 const ContentFeatures &f2 = ndef->get(m2);
632 // Contents don't differ for different forms of same liquid
633 if(f1.sameLiquid(f2))
634 contents_differ = false;
636 u8 c1 = f1.solidness;
637 u8 c2 = f2.solidness;
639 bool solidness_differs = (c1 != c2);
640 bool makes_face = contents_differ && solidness_differs;
642 if(makes_face == false)
646 c1 = f1.visual_solidness;
648 c2 = f2.visual_solidness;
652 // If same solidness, liquid takes precense
666 Gets nth node tile (0 <= n <= 5).
668 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
670 INodeDefManager *ndef = data->m_gamedef->ndef();
671 TileSpec spec = ndef->get(mn).tiles[tileindex];
672 // Apply temporary crack
673 if (p == data->m_crack_pos_relative)
674 spec.material_flags |= MATERIAL_FLAG_CRACK;
679 Gets node tile given a face direction.
681 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
683 INodeDefManager *ndef = data->m_gamedef->ndef();
685 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
686 // (0,0,1), (0,0,-1) or (0,0,0)
687 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
689 // Convert direction to single integer for table lookup
694 // 4 = invalid, treat as (0,0,0)
698 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
700 // Get rotation for things like chests
701 u8 facedir = mn.getFaceDir(ndef);
703 static const u16 dir_to_tile[24 * 16] =
705 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
706 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
707 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
708 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
709 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
711 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
712 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
713 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
714 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
716 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
717 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
718 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
719 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
721 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
722 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
723 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
724 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
726 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
727 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
728 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
729 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
731 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
732 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
733 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
734 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
737 u16 tile_index=facedir*16 + dir_i;
738 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
739 spec.rotation=dir_to_tile[tile_index + 1];
740 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
744 static void getTileInfo(
752 v3s16 &face_dir_corrected,
758 VoxelManipulator &vmanip = data->m_vmanip;
759 INodeDefManager *ndef = data->m_gamedef->ndef();
760 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
762 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
764 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
765 if (n0.getContent() == CONTENT_IGNORE ) {
769 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
772 bool equivalent = false;
773 u8 mf = face_contents(n0.getContent(), n1.getContent(),
786 tile = getNodeTile(n0, p, face_dir, data);
788 face_dir_corrected = face_dir;
789 light_source = ndef->get(n0).light_source;
793 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
794 p_corrected = p + face_dir;
795 face_dir_corrected = -face_dir;
796 light_source = ndef->get(n1).light_source;
799 // eg. water and glass
801 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
803 if(data->m_smooth_lighting == false)
805 lights[0] = lights[1] = lights[2] = lights[3] =
806 getFaceLight(n0, n1, face_dir, ndef);
810 v3s16 vertex_dirs[4];
811 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
812 for(u16 i=0; i<4; i++)
814 lights[i] = getSmoothLight(
815 blockpos_nodes + p_corrected,
816 vertex_dirs[i], data);
825 translate_dir: unit vector with only one of x, y or z
826 face_dir: unit vector with only one of x, y or z
828 static void updateFastFaceRow(
835 std::vector<FastFace> &dest)
839 u16 continuous_tiles_count = 0;
841 bool makes_face = false;
843 v3s16 face_dir_corrected;
844 u16 lights[4] = {0,0,0,0};
847 getTileInfo(data, p, face_dir,
848 makes_face, p_corrected, face_dir_corrected,
849 lights, tile, light_source);
851 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
853 // If tiling can be done, this is set to false in the next step
854 bool next_is_different = true;
858 bool next_makes_face = false;
859 v3s16 next_p_corrected;
860 v3s16 next_face_dir_corrected;
861 u16 next_lights[4] = {0,0,0,0};
863 u8 next_light_source = 0;
865 // If at last position, there is nothing to compare to and
866 // the face must be drawn anyway
867 if(j != MAP_BLOCKSIZE - 1)
869 p_next = p + translate_dir;
871 getTileInfo(data, p_next, face_dir,
872 next_makes_face, next_p_corrected,
873 next_face_dir_corrected, next_lights,
874 next_tile, next_light_source);
876 if(next_makes_face == makes_face
877 && next_p_corrected == p_corrected + translate_dir
878 && next_face_dir_corrected == face_dir_corrected
879 && next_lights[0] == lights[0]
880 && next_lights[1] == lights[1]
881 && next_lights[2] == lights[2]
882 && next_lights[3] == lights[3]
884 && tile.rotation == 0
885 && next_light_source == light_source)
887 next_is_different = false;
891 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
892 next_makes_face != makes_face ? 1 : 0);
893 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
894 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
895 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
896 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
897 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
898 (next_lights[0] != lights[0] ||
899 next_lights[0] != lights[0] ||
900 next_lights[0] != lights[0] ||
901 next_lights[0] != lights[0]) ? 1 : 0);
902 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
903 !(next_tile == tile) ? 1 : 0);
906 /*g_profiler->add("Meshgen: Total faces checked", 1);
908 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
911 g_profiler->add("Meshgen: diff: last position", 1);*/
914 continuous_tiles_count++;
916 if(next_is_different)
919 Create a face if there should be one
923 // Floating point conversion of the position vector
924 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
925 // Center point of face (kind of)
926 v3f sp = pf - ((f32)continuous_tiles_count / 2.0 - 0.5) * translate_dir_f;
927 if(continuous_tiles_count != 1)
928 sp += translate_dir_f;
931 if(translate_dir.X != 0) {
932 scale.X = continuous_tiles_count;
934 if(translate_dir.Y != 0) {
935 scale.Y = continuous_tiles_count;
937 if(translate_dir.Z != 0) {
938 scale.Z = continuous_tiles_count;
941 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
942 sp, face_dir_corrected, scale, light_source,
945 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
946 for(int i = 1; i < continuous_tiles_count; i++){
947 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
951 continuous_tiles_count = 0;
953 makes_face = next_makes_face;
954 p_corrected = next_p_corrected;
955 face_dir_corrected = next_face_dir_corrected;
956 lights[0] = next_lights[0];
957 lights[1] = next_lights[1];
958 lights[2] = next_lights[2];
959 lights[3] = next_lights[3];
961 light_source = next_light_source;
968 static void updateAllFastFaceRows(MeshMakeData *data,
969 std::vector<FastFace> &dest)
972 Go through every y,z and get top(y+) faces in rows of x+
974 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
975 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
976 updateFastFaceRow(data,
980 v3s16(0,1,0), //face dir
987 Go through every x,y and get right(x+) faces in rows of z+
989 for(s16 x = 0; x < MAP_BLOCKSIZE; x++) {
990 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
991 updateFastFaceRow(data,
995 v3s16(1,0,0), //face dir
1002 Go through every y,z and get back(z+) faces in rows of x+
1004 for(s16 z = 0; z < MAP_BLOCKSIZE; z++) {
1005 for(s16 y = 0; y < MAP_BLOCKSIZE; y++) {
1006 updateFastFaceRow(data,
1010 v3s16(0,0,1), //face dir
1021 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1022 m_mesh(new scene::SMesh()),
1023 m_gamedef(data->m_gamedef),
1024 m_animation_force_timer(0), // force initial animation
1026 m_crack_materials(),
1027 m_highlighted_materials(),
1028 m_last_daynight_ratio((u32) -1),
1031 m_enable_shaders = g_settings->getBool("enable_shaders");
1032 m_enable_highlighting = g_settings->getBool("enable_node_highlighting");
1034 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1035 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1036 //TimeTaker timer1("MapBlockMesh()");
1038 std::vector<FastFace> fastfaces_new;
1041 We are including the faces of the trailing edges of the block.
1042 This means that when something changes, the caller must
1043 also update the meshes of the blocks at the leading edges.
1045 NOTE: This is the slowest part of this method.
1048 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1049 //TimeTaker timer2("updateAllFastFaceRows()");
1050 updateAllFastFaceRows(data, fastfaces_new);
1055 Convert FastFaces to MeshCollector
1058 MeshCollector collector;
1061 // avg 0ms (100ms spikes when loading textures the first time)
1062 // (NOTE: probably outdated)
1063 //TimeTaker timer2("MeshCollector building");
1065 for(u32 i=0; i<fastfaces_new.size(); i++)
1067 FastFace &f = fastfaces_new[i];
1069 const u16 indices[] = {0,1,2,2,3,0};
1070 const u16 indices_alternate[] = {0,1,3,2,3,1};
1072 if(f.tile.texture == NULL)
1075 const u16 *indices_p = indices;
1078 Revert triangles for nicer looking gradient if vertices
1079 1 and 3 have same color or 0 and 2 have different color.
1080 getRed() is the day color.
1082 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1083 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1084 indices_p = indices_alternate;
1086 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1091 Add special graphics:
1098 mapblock_mesh_generate_special(data, collector);
1100 m_highlight_mesh_color = data->m_highlight_mesh_color;
1103 Convert MeshCollector to SMesh
1105 ITextureSource *tsrc = m_gamedef->tsrc();
1106 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1108 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1110 PreMeshBuffer &p = collector.prebuffers[i];
1112 // Generate animation data
1114 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1116 // Find the texture name plus ^[crack:N:
1117 std::ostringstream os(std::ios::binary);
1118 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1119 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1120 os<<"o"; // use ^[cracko
1121 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1122 m_crack_materials.insert(std::make_pair(i, os.str()));
1123 // Replace tile texture with the cracked one
1124 p.tile.texture = tsrc->getTexture(
1126 &p.tile.texture_id);
1128 // - Texture animation
1129 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1131 // Add to MapBlockMesh in order to animate these tiles
1132 m_animation_tiles[i] = p.tile;
1133 m_animation_frames[i] = 0;
1134 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1135 // Get starting position from noise
1136 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1137 data->m_blockpos.X, data->m_blockpos.Y,
1138 data->m_blockpos.Z, 0));
1140 // Play all synchronized
1141 m_animation_frame_offsets[i] = 0;
1143 // Replace tile texture with the first animation frame
1144 FrameSpec animation_frame = p.tile.frames[0];
1145 p.tile.texture = animation_frame.texture;
1148 if(m_enable_highlighting && p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED)
1149 m_highlighted_materials.push_back(i);
1151 for(u32 j = 0; j < p.vertices.size(); j++)
1153 // Note applyFacesShading second parameter is precalculated sqrt
1154 // value for speed improvement
1155 // Skip it for lightsources and top faces.
1156 video::SColor &vc = p.vertices[j].Color;
1157 if (!vc.getBlue()) {
1158 if (p.vertices[j].Normal.Y < -0.5) {
1159 applyFacesShading (vc, 0.447213);
1160 } else if (p.vertices[j].Normal.X > 0.5) {
1161 applyFacesShading (vc, 0.670820);
1162 } else if (p.vertices[j].Normal.X < -0.5) {
1163 applyFacesShading (vc, 0.670820);
1164 } else if (p.vertices[j].Normal.Z > 0.5) {
1165 applyFacesShading (vc, 0.836660);
1166 } else if (p.vertices[j].Normal.Z < -0.5) {
1167 applyFacesShading (vc, 0.836660);
1170 if(!m_enable_shaders)
1172 // - Classic lighting (shaders handle this by themselves)
1173 // Set initial real color and store for later updates
1174 u8 day = vc.getRed();
1175 u8 night = vc.getGreen();
1176 finalColorBlend(vc, day, night, 1000);
1178 m_daynight_diffs[i][j] = std::make_pair(day, night);
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.tile.texture);
1190 if (p.tile.material_flags & MATERIAL_FLAG_HIGHLIGHTED) {
1191 material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR;
1193 if (m_enable_shaders) {
1194 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1195 p.tile.applyMaterialOptionsWithShaders(material);
1196 if (p.tile.normal_texture) {
1197 material.setTexture(1, p.tile.normal_texture);
1198 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1200 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1203 p.tile.applyMaterialOptions(material);
1207 // Create meshbuffer
1208 // This is a "Standard MeshBuffer",
1209 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1210 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1212 buf->Material = material;
1214 m_mesh->addMeshBuffer(buf);
1217 buf->append(&p.vertices[0], p.vertices.size(),
1218 &p.indices[0], p.indices.size());
1221 m_camera_offset = camera_offset;
1224 Do some stuff to the mesh
1227 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1232 // Usually 1-700 faces and 1-7 materials
1233 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1234 <<"and uses "<<m_mesh->getMeshBufferCount()
1235 <<" materials (meshbuffers)"<<std::endl;
1238 // Use VBO for mesh (this just would set this for ever buffer)
1239 // This will lead to infinite memory usage because or irrlicht.
1240 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1243 NOTE: If that is enabled, some kind of a queue to the main
1244 thread should be made which would call irrlicht to delete
1245 the hardware buffer and then delete the mesh
1249 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1251 // Check if animation is required for this mesh
1253 !m_crack_materials.empty() ||
1254 !m_daynight_diffs.empty() ||
1255 !m_animation_tiles.empty() ||
1256 !m_highlighted_materials.empty();
1259 MapBlockMesh::~MapBlockMesh()
1265 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1268 if(!m_has_animation)
1270 m_animation_force_timer = 100000;
1274 m_animation_force_timer = myrand_range(5, 100);
1277 if(crack != m_last_crack)
1279 for(std::map<u32, std::string>::iterator
1280 i = m_crack_materials.begin();
1281 i != m_crack_materials.end(); i++)
1283 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1284 std::string basename = i->second;
1286 // Create new texture name from original
1287 ITextureSource *tsrc = m_gamedef->getTextureSource();
1288 std::ostringstream os;
1289 os<<basename<<crack;
1290 u32 new_texture_id = 0;
1291 video::ITexture *new_texture =
1292 tsrc->getTexture(os.str(), &new_texture_id);
1293 buf->getMaterial().setTexture(0, new_texture);
1295 // If the current material is also animated,
1296 // update animation info
1297 std::map<u32, TileSpec>::iterator anim_iter =
1298 m_animation_tiles.find(i->first);
1299 if(anim_iter != m_animation_tiles.end()){
1300 TileSpec &tile = anim_iter->second;
1301 tile.texture = new_texture;
1302 tile.texture_id = new_texture_id;
1303 // force animation update
1304 m_animation_frames[i->first] = -1;
1308 m_last_crack = crack;
1311 // Texture animation
1312 for(std::map<u32, TileSpec>::iterator
1313 i = m_animation_tiles.begin();
1314 i != m_animation_tiles.end(); i++)
1316 const TileSpec &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->getMeshBuffer(i->first);
1328 ITextureSource *tsrc = m_gamedef->getTextureSource();
1330 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);
1335 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1337 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1342 // Day-night transition
1343 if(!m_enable_shaders && (daynight_ratio != m_last_daynight_ratio))
1345 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1346 i = m_daynight_diffs.begin();
1347 i != m_daynight_diffs.end(); i++)
1349 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1350 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1351 for(std::map<u32, std::pair<u8, u8 > >::iterator
1352 j = i->second.begin();
1353 j != i->second.end(); j++)
1355 u32 vertexIndex = j->first;
1356 u8 day = j->second.first;
1357 u8 night = j->second.second;
1358 finalColorBlend(vertices[vertexIndex].Color,
1359 day, night, daynight_ratio);
1362 m_last_daynight_ratio = daynight_ratio;
1365 // Node highlighting
1366 if (m_enable_highlighting) {
1367 u8 day = m_highlight_mesh_color.getRed();
1368 u8 night = m_highlight_mesh_color.getGreen();
1370 finalColorBlend(hc, day, night, daynight_ratio);
1371 float sin_r = 0.07 * sin(1.5 * time);
1372 float sin_g = 0.07 * sin(1.5 * time + irr::core::PI * 0.5);
1373 float sin_b = 0.07 * sin(1.5 * time + irr::core::PI);
1374 hc.setRed(core::clamp(core::round32(hc.getRed() * (0.8 + sin_r)), 0, 255));
1375 hc.setGreen(core::clamp(core::round32(hc.getGreen() * (0.8 + sin_g)), 0, 255));
1376 hc.setBlue(core::clamp(core::round32(hc.getBlue() * (0.8 + sin_b)), 0, 255));
1378 for(std::list<u32>::iterator
1379 i = m_highlighted_materials.begin();
1380 i != m_highlighted_materials.end(); i++)
1382 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(*i);
1383 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1384 for (u32 j = 0; j < buf->getVertexCount() ;j++)
1385 vertices[j].Color = hc;
1392 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1394 if (camera_offset != m_camera_offset) {
1395 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1396 m_camera_offset = camera_offset;
1404 void MeshCollector::append(const TileSpec &tile,
1405 const video::S3DVertex *vertices, u32 numVertices,
1406 const u16 *indices, u32 numIndices)
1408 if(numIndices > 65535)
1410 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1414 PreMeshBuffer *p = NULL;
1415 for(u32 i=0; i<prebuffers.size(); i++)
1417 PreMeshBuffer &pp = prebuffers[i];
1420 if(pp.indices.size() + numIndices > 65535)
1431 prebuffers.push_back(pp);
1432 p = &prebuffers[prebuffers.size()-1];
1435 u32 vertex_count = p->vertices.size();
1436 for(u32 i=0; i<numIndices; i++)
1438 u32 j = indices[i] + vertex_count;
1439 p->indices.push_back(j);
1441 for(u32 i=0; i<numVertices; i++)
1443 p->vertices.push_back(vertices[i]);
1448 MeshCollector - for meshnodes and converted drawtypes.
1451 void MeshCollector::append(const TileSpec &tile,
1452 const video::S3DVertex *vertices, u32 numVertices,
1453 const u16 *indices, u32 numIndices,
1454 v3f pos, video::SColor c)
1456 if(numIndices > 65535)
1458 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1462 PreMeshBuffer *p = NULL;
1463 for(u32 i=0; i<prebuffers.size(); i++)
1465 PreMeshBuffer &pp = prebuffers[i];
1468 if(pp.indices.size() + numIndices > 65535)
1479 prebuffers.push_back(pp);
1480 p = &prebuffers[prebuffers.size()-1];
1483 u32 vertex_count = p->vertices.size();
1484 for(u32 i=0; i<numIndices; i++)
1486 u32 j = indices[i] + vertex_count;
1487 p->indices.push_back(j);
1489 for(u32 i=0; i<numVertices; i++)
1491 video::S3DVertex vert = vertices[i];
1494 p->vertices.push_back(vert);