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 float srgb_linear_multiply(float f, float m, float max)
37 f = f * f; // SRGB -> Linear
39 f = sqrt(f); // Linear -> SRGB
49 MeshMakeData::MeshMakeData(IGameDef *gamedef):
51 m_blockpos(-1337,-1337,-1337),
52 m_crack_pos_relative(-1337, -1337, -1337),
53 m_smooth_lighting(false),
57 void MeshMakeData::fill(MapBlock *block)
59 m_blockpos = block->getPos();
61 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
67 // Allocate this block + neighbors
69 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
70 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
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::setSmoothLighting(bool smooth_lighting)
141 m_smooth_lighting = smooth_lighting;
145 Light and vertex color functions
149 Calculate non-smooth lighting at interior of node.
152 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
153 INodeDefManager *ndef)
155 u8 light = n.getLight(bank, ndef);
159 light = undiminish_light(light);
164 light = diminish_light(light);
168 return decode_light(light);
172 Calculate non-smooth lighting at interior of node.
175 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
177 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
178 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
179 return day | (night << 8);
183 Calculate non-smooth lighting at face of node.
186 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
187 v3s16 face_dir, INodeDefManager *ndef)
190 u8 l1 = n.getLight(bank, ndef);
191 u8 l2 = n2.getLight(bank, ndef);
197 // Boost light level for light sources
198 u8 light_source = MYMAX(ndef->get(n).light_source,
199 ndef->get(n2).light_source);
200 //if(light_source >= light)
201 //return decode_light(undiminish_light(light_source));
202 if(light_source > light)
203 //return decode_light(light_source);
204 light = light_source;
206 // Make some nice difference to different sides
208 // This makes light come from a corner
209 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
210 light = diminish_light(diminish_light(light));
211 else if(face_dir.X == -1 || face_dir.Z == -1)
212 light = diminish_light(light);*/
214 // All neighboring faces have different shade (like in minecraft)
215 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
216 light = diminish_light(diminish_light(light));
217 else if(face_dir.Z == 1 || face_dir.Z == -1)
218 light = diminish_light(light);
220 return decode_light(light);
224 Calculate non-smooth lighting at face of node.
227 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
229 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
230 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
231 return day | (night << 8);
235 Calculate smooth lighting at the XYZ- corner of p.
238 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
240 static v3s16 dirs8[8] = {
251 INodeDefManager *ndef = data->m_gamedef->ndef();
253 u16 ambient_occlusion = 0;
256 u8 light_source_max = 0;
257 for(u32 i=0; i<8; i++)
259 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
260 const ContentFeatures &f = ndef->get(n);
261 if(f.light_source > light_source_max)
262 light_source_max = f.light_source;
263 // Check f.solidness because fast-style leaves look
265 if(f.param_type == CPT_LIGHT && f.solidness != 2)
267 light += decode_light(n.getLight(bank, ndef));
270 else if(n.getContent() != CONTENT_IGNORE)
279 light /= light_count;
281 // Boost brightness around light sources
282 if(decode_light(light_source_max) >= light)
283 //return decode_light(undiminish_light(light_source_max));
284 return decode_light(light_source_max);
286 if(ambient_occlusion > 4)
288 //ambient_occlusion -= 4;
289 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
290 float light_amount = (8 - ambient_occlusion) / 4.0;
291 float light_f = (float)light / 255.0;
292 light_f = pow(light_f, 2.2f); // gamma -> linear space
293 light_f = light_f * light_amount;
294 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
297 light = 255.0 * light_f + 0.5;
304 Calculate smooth lighting at the XYZ- corner of p.
307 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
309 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
310 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
311 return day | (night << 8);
315 Calculate smooth lighting at the given corner of p.
318 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
320 if(corner.X == 1) p.X += 1;
321 else assert(corner.X == -1);
322 if(corner.Y == 1) p.Y += 1;
323 else assert(corner.Y == -1);
324 if(corner.Z == 1) p.Z += 1;
325 else assert(corner.Z == -1);
327 return getSmoothLight(p, data);
331 Converts from day + night color values (0..255)
332 and a given daynight_ratio to the final SColor shown on screen.
334 static void finalColorBlend(video::SColor& result,
335 u8 day, u8 night, u32 daynight_ratio)
337 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
341 b += (day - night) / 13;
342 rg -= (day - night) / 23;
344 // Emphase blue a bit in darker places
345 // Each entry of this array represents a range of 8 blue levels
346 static u8 emphase_blue_when_dark[32] = {
347 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
348 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
354 b += emphase_blue_when_dark[b / 8];
356 // Artificial light is yellow-ish
357 static u8 emphase_yellow_when_artificial[16] = {
358 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
360 rg += emphase_yellow_when_artificial[night/16];
372 Mesh generation helpers
376 vertex_dirs: v3s16[4]
378 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
381 If looked from outside the node towards the face, the corners are:
387 if(dir == v3s16(0,0,1))
389 // If looking towards z+, this is the face that is behind
390 // the center point, facing towards z+.
391 vertex_dirs[0] = v3s16(-1,-1, 1);
392 vertex_dirs[1] = v3s16( 1,-1, 1);
393 vertex_dirs[2] = v3s16( 1, 1, 1);
394 vertex_dirs[3] = v3s16(-1, 1, 1);
396 else if(dir == v3s16(0,0,-1))
399 vertex_dirs[0] = v3s16( 1,-1,-1);
400 vertex_dirs[1] = v3s16(-1,-1,-1);
401 vertex_dirs[2] = v3s16(-1, 1,-1);
402 vertex_dirs[3] = v3s16( 1, 1,-1);
404 else if(dir == v3s16(1,0,0))
407 vertex_dirs[0] = v3s16( 1,-1, 1);
408 vertex_dirs[1] = v3s16( 1,-1,-1);
409 vertex_dirs[2] = v3s16( 1, 1,-1);
410 vertex_dirs[3] = v3s16( 1, 1, 1);
412 else if(dir == v3s16(-1,0,0))
415 vertex_dirs[0] = v3s16(-1,-1,-1);
416 vertex_dirs[1] = v3s16(-1,-1, 1);
417 vertex_dirs[2] = v3s16(-1, 1, 1);
418 vertex_dirs[3] = v3s16(-1, 1,-1);
420 else if(dir == v3s16(0,1,0))
422 // faces towards Y+ (assume Z- as "down" in texture)
423 vertex_dirs[0] = v3s16( 1, 1,-1);
424 vertex_dirs[1] = v3s16(-1, 1,-1);
425 vertex_dirs[2] = v3s16(-1, 1, 1);
426 vertex_dirs[3] = v3s16( 1, 1, 1);
428 else if(dir == v3s16(0,-1,0))
430 // faces towards Y- (assume Z+ as "down" in texture)
431 vertex_dirs[0] = v3s16( 1,-1, 1);
432 vertex_dirs[1] = v3s16(-1,-1, 1);
433 vertex_dirs[2] = v3s16(-1,-1,-1);
434 vertex_dirs[3] = v3s16( 1,-1,-1);
441 video::S3DVertex vertices[4]; // Precalculated vertices
444 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
445 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
449 // Position is at the center of the cube.
458 v3s16 vertex_dirs[4];
459 getNodeVertexDirs(dir, vertex_dirs);
463 switch (tile.rotation)
469 vertex_dirs[0] = vertex_dirs[3];
470 vertex_dirs[3] = vertex_dirs[2];
471 vertex_dirs[2] = vertex_dirs[1];
481 vertex_dirs[0] = vertex_dirs[2];
484 vertex_dirs[1] = vertex_dirs[3];
495 vertex_dirs[0] = vertex_dirs[1];
496 vertex_dirs[1] = vertex_dirs[2];
497 vertex_dirs[2] = vertex_dirs[3];
507 vertex_dirs[0] = vertex_dirs[3];
508 vertex_dirs[3] = vertex_dirs[2];
509 vertex_dirs[2] = vertex_dirs[1];
521 vertex_dirs[0] = vertex_dirs[1];
522 vertex_dirs[1] = vertex_dirs[2];
523 vertex_dirs[2] = vertex_dirs[3];
535 vertex_dirs[0] = vertex_dirs[3];
536 vertex_dirs[3] = vertex_dirs[2];
537 vertex_dirs[2] = vertex_dirs[1];
549 vertex_dirs[0] = vertex_dirs[1];
550 vertex_dirs[1] = vertex_dirs[2];
551 vertex_dirs[2] = vertex_dirs[3];
573 for(u16 i=0; i<4; i++)
576 BS/2*vertex_dirs[i].X,
577 BS/2*vertex_dirs[i].Y,
578 BS/2*vertex_dirs[i].Z
582 for(u16 i=0; i<4; i++)
584 vertex_pos[i].X *= scale.X;
585 vertex_pos[i].Y *= scale.Y;
586 vertex_pos[i].Z *= scale.Z;
587 vertex_pos[i] += pos;
591 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
592 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
593 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
595 v3f normal(dir.X, dir.Y, dir.Z);
597 u8 alpha = tile.alpha;
599 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
600 MapBlock_LightColor(alpha, li0, light_source),
601 core::vector2d<f32>(x0+w*abs_scale, y0+h));
602 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
603 MapBlock_LightColor(alpha, li1, light_source),
604 core::vector2d<f32>(x0, y0+h));
605 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
606 MapBlock_LightColor(alpha, li2, light_source),
607 core::vector2d<f32>(x0, y0));
608 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
609 MapBlock_LightColor(alpha, li3, light_source),
610 core::vector2d<f32>(x0+w*abs_scale, y0));
613 dest.push_back(face);
617 Nodes make a face if contents differ and solidness differs.
620 1: Face uses m1's content
621 2: Face uses m2's content
622 equivalent: Whether the blocks share the same face (eg. water and glass)
624 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
626 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
627 INodeDefManager *ndef)
631 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
634 bool contents_differ = (m1 != m2);
636 const ContentFeatures &f1 = ndef->get(m1);
637 const ContentFeatures &f2 = ndef->get(m2);
639 // Contents don't differ for different forms of same liquid
640 if(f1.sameLiquid(f2))
641 contents_differ = false;
643 u8 c1 = f1.solidness;
644 u8 c2 = f2.solidness;
646 bool solidness_differs = (c1 != c2);
647 bool makes_face = contents_differ && solidness_differs;
649 if(makes_face == false)
653 c1 = f1.visual_solidness;
655 c2 = f2.visual_solidness;
659 // If same solidness, liquid takes precense
673 Gets nth node tile (0 <= n <= 5).
675 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
677 INodeDefManager *ndef = data->m_gamedef->ndef();
678 TileSpec spec = ndef->get(mn).tiles[tileindex];
679 // Apply temporary crack
680 if(p == data->m_crack_pos_relative)
682 spec.material_flags |= MATERIAL_FLAG_CRACK;
688 Gets node tile given a face direction.
690 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
692 INodeDefManager *ndef = data->m_gamedef->ndef();
694 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
695 // (0,0,1), (0,0,-1) or (0,0,0)
696 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
698 // Convert direction to single integer for table lookup
703 // 4 = invalid, treat as (0,0,0)
707 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
709 // Get rotation for things like chests
710 u8 facedir = mn.getFaceDir(ndef);
713 static const u16 dir_to_tile[24 * 16] =
715 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
716 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
717 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
718 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
719 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
721 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
722 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
723 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
724 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
726 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
727 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
728 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
729 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
731 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
732 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
733 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
734 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
736 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
737 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
738 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
739 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
741 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
742 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
743 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
744 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
747 u16 tile_index=facedir*16 + dir_i;
748 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
749 spec.rotation=dir_to_tile[tile_index + 1];
750 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
754 static void getTileInfo(
762 v3s16 &face_dir_corrected,
768 VoxelManipulator &vmanip = data->m_vmanip;
769 INodeDefManager *ndef = data->m_gamedef->ndef();
770 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
772 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
774 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
775 if (n0.getContent() == CONTENT_IGNORE ) {
779 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
782 bool equivalent = false;
783 u8 mf = face_contents(n0.getContent(), n1.getContent(),
796 tile = getNodeTile(n0, p, face_dir, data);
798 face_dir_corrected = face_dir;
799 light_source = ndef->get(n0).light_source;
803 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
804 p_corrected = p + face_dir;
805 face_dir_corrected = -face_dir;
806 light_source = ndef->get(n1).light_source;
809 // eg. water and glass
811 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
813 if(data->m_smooth_lighting == false)
815 lights[0] = lights[1] = lights[2] = lights[3] =
816 getFaceLight(n0, n1, face_dir, ndef);
820 v3s16 vertex_dirs[4];
821 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
822 for(u16 i=0; i<4; i++)
824 lights[i] = getSmoothLight(
825 blockpos_nodes + p_corrected,
826 vertex_dirs[i], data);
835 translate_dir: unit vector with only one of x, y or z
836 face_dir: unit vector with only one of x, y or z
838 static void updateFastFaceRow(
845 std::vector<FastFace> &dest)
849 u16 continuous_tiles_count = 0;
851 bool makes_face = false;
853 v3s16 face_dir_corrected;
854 u16 lights[4] = {0,0,0,0};
857 getTileInfo(data, p, face_dir,
858 makes_face, p_corrected, face_dir_corrected,
859 lights, tile, light_source);
861 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
863 // If tiling can be done, this is set to false in the next step
864 bool next_is_different = true;
868 bool next_makes_face = false;
869 v3s16 next_p_corrected;
870 v3s16 next_face_dir_corrected;
871 u16 next_lights[4] = {0,0,0,0};
873 u8 next_light_source = 0;
875 // If at last position, there is nothing to compare to and
876 // the face must be drawn anyway
877 if(j != MAP_BLOCKSIZE - 1)
879 p_next = p + translate_dir;
881 getTileInfo(data, p_next, face_dir,
882 next_makes_face, next_p_corrected,
883 next_face_dir_corrected, next_lights,
884 next_tile, next_light_source);
886 if(next_makes_face == makes_face
887 && next_p_corrected == p_corrected + translate_dir
888 && next_face_dir_corrected == face_dir_corrected
889 && next_lights[0] == lights[0]
890 && next_lights[1] == lights[1]
891 && next_lights[2] == lights[2]
892 && next_lights[3] == lights[3]
894 && tile.rotation == 0
895 && next_light_source == light_source)
897 next_is_different = false;
901 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
902 next_makes_face != makes_face ? 1 : 0);
903 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
904 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
905 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
906 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
907 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
908 (next_lights[0] != lights[0] ||
909 next_lights[0] != lights[0] ||
910 next_lights[0] != lights[0] ||
911 next_lights[0] != lights[0]) ? 1 : 0);
912 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
913 !(next_tile == tile) ? 1 : 0);
916 /*g_profiler->add("Meshgen: Total faces checked", 1);
918 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
921 g_profiler->add("Meshgen: diff: last position", 1);*/
924 continuous_tiles_count++;
926 if(next_is_different)
929 Create a face if there should be one
933 // Floating point conversion of the position vector
934 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
935 // Center point of face (kind of)
936 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
937 if(continuous_tiles_count != 1)
938 sp += translate_dir_f;
941 if(translate_dir.X != 0)
943 scale.X = continuous_tiles_count;
945 if(translate_dir.Y != 0)
947 scale.Y = continuous_tiles_count;
949 if(translate_dir.Z != 0)
951 scale.Z = continuous_tiles_count;
954 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
955 sp, face_dir_corrected, scale, light_source,
958 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
959 for(int i=1; i<continuous_tiles_count; i++){
960 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
964 continuous_tiles_count = 0;
966 makes_face = next_makes_face;
967 p_corrected = next_p_corrected;
968 face_dir_corrected = next_face_dir_corrected;
969 lights[0] = next_lights[0];
970 lights[1] = next_lights[1];
971 lights[2] = next_lights[2];
972 lights[3] = next_lights[3];
974 light_source = next_light_source;
981 static void updateAllFastFaceRows(MeshMakeData *data,
982 std::vector<FastFace> &dest)
985 Go through every y,z and get top(y+) faces in rows of x+
987 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
988 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
989 updateFastFaceRow(data,
993 v3s16(0,1,0), //face dir
1000 Go through every x,y and get right(x+) faces in rows of z+
1002 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
1003 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1004 updateFastFaceRow(data,
1008 v3s16(1,0,0), //face dir
1015 Go through every y,z and get back(z+) faces in rows of x+
1017 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1018 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1019 updateFastFaceRow(data,
1023 v3s16(0,0,1), //face dir
1034 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1035 m_mesh(new scene::SMesh()),
1036 m_gamedef(data->m_gamedef),
1037 m_animation_force_timer(0), // force initial animation
1039 m_crack_materials(),
1040 m_last_daynight_ratio((u32) -1),
1043 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1044 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1045 //TimeTaker timer1("MapBlockMesh()");
1047 std::vector<FastFace> fastfaces_new;
1050 We are including the faces of the trailing edges of the block.
1051 This means that when something changes, the caller must
1052 also update the meshes of the blocks at the leading edges.
1054 NOTE: This is the slowest part of this method.
1057 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1058 //TimeTaker timer2("updateAllFastFaceRows()");
1059 updateAllFastFaceRows(data, fastfaces_new);
1064 Convert FastFaces to MeshCollector
1067 MeshCollector collector;
1070 // avg 0ms (100ms spikes when loading textures the first time)
1071 // (NOTE: probably outdated)
1072 //TimeTaker timer2("MeshCollector building");
1074 for(u32 i=0; i<fastfaces_new.size(); i++)
1076 FastFace &f = fastfaces_new[i];
1078 const u16 indices[] = {0,1,2,2,3,0};
1079 const u16 indices_alternate[] = {0,1,3,2,3,1};
1081 if(f.tile.texture == NULL)
1084 const u16 *indices_p = indices;
1087 Revert triangles for nicer looking gradient if vertices
1088 1 and 3 have same color or 0 and 2 have different color.
1089 getRed() is the day color.
1091 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1092 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1093 indices_p = indices_alternate;
1095 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1100 Add special graphics:
1107 mapblock_mesh_generate_special(data, collector);
1111 Convert MeshCollector to SMesh
1113 ITextureSource *tsrc = m_gamedef->tsrc();
1114 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1116 bool enable_shaders = g_settings->getBool("enable_shaders");
1117 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1118 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1120 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1122 PreMeshBuffer &p = collector.prebuffers[i];
1123 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1124 <<", p.indices.size()="<<p.indices.size()
1127 // Generate animation data
1129 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1131 // Find the texture name plus ^[crack:N:
1132 std::ostringstream os(std::ios::binary);
1133 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1134 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1135 os<<"o"; // use ^[cracko
1136 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1137 m_crack_materials.insert(std::make_pair(i, os.str()));
1138 // Replace tile texture with the cracked one
1139 p.tile.texture = tsrc->getTexture(
1141 &p.tile.texture_id);
1143 // - Texture animation
1144 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1146 // Add to MapBlockMesh in order to animate these tiles
1147 m_animation_tiles[i] = p.tile;
1148 m_animation_frames[i] = 0;
1149 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1150 // Get starting position from noise
1151 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1152 data->m_blockpos.X, data->m_blockpos.Y,
1153 data->m_blockpos.Z, 0));
1155 // Play all synchronized
1156 m_animation_frame_offsets[i] = 0;
1158 // Replace tile texture with the first animation frame
1159 std::ostringstream os(std::ios::binary);
1160 os<<tsrc->getTextureName(p.tile.texture_id);
1161 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1162 p.tile.texture = tsrc->getTexture(
1164 &p.tile.texture_id);
1166 // - Classic lighting (shaders handle this by themselves)
1169 for(u32 j = 0; j < p.vertices.size(); j++)
1171 video::SColor &vc = p.vertices[j].Color;
1172 // Set initial real color and store for later updates
1173 u8 day = vc.getRed();
1174 u8 night = vc.getGreen();
1175 finalColorBlend(vc, day, night, 1000);
1177 m_daynight_diffs[i][j] = std::make_pair(day, night);
1178 // Brighten topside (no shaders)
1179 if(p.vertices[j].Normal.Y > 0.5)
1181 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1182 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1183 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1189 video::SMaterial material;
1190 material.setFlag(video::EMF_LIGHTING, false);
1191 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1192 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1193 material.setFlag(video::EMF_FOG_ENABLE, true);
1194 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1195 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1196 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1197 material.setTexture(0, p.tile.texture);
1199 if (enable_shaders) {
1200 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1201 p.tile.applyMaterialOptionsWithShaders(material);
1202 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1203 if (enable_bumpmapping || enable_parallax_occlusion) {
1204 if (tsrc->isKnownSourceImage("override_normal.png")){
1205 material.setTexture(1, tsrc->getTexture("override_normal.png"));
1206 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1208 std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
1209 std::string normal_ext = "_normal.png";
1210 size_t pos = fname_base.find(".");
1211 std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1213 if (tsrc->isKnownSourceImage(fname_normal)) {
1214 // look for image extension and replace it
1216 while ((i = fname_base.find(".", i)) != std::string::npos) {
1217 fname_base.replace(i, 4, normal_ext);
1218 i += normal_ext.length();
1220 material.setTexture(1, tsrc->getTexture(fname_base));
1221 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1226 p.tile.applyMaterialOptions(material);
1228 // Create meshbuffer
1230 // This is a "Standard MeshBuffer",
1231 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1232 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1234 buf->Material = material;
1236 m_mesh->addMeshBuffer(buf);
1239 buf->append(&p.vertices[0], p.vertices.size(),
1240 &p.indices[0], p.indices.size());
1243 m_camera_offset = camera_offset;
1246 Do some stuff to the mesh
1249 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1254 // Usually 1-700 faces and 1-7 materials
1255 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1256 <<"and uses "<<m_mesh->getMeshBufferCount()
1257 <<" materials (meshbuffers)"<<std::endl;
1260 // Use VBO for mesh (this just would set this for ever buffer)
1261 // This will lead to infinite memory usage because or irrlicht.
1262 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1265 NOTE: If that is enabled, some kind of a queue to the main
1266 thread should be made which would call irrlicht to delete
1267 the hardware buffer and then delete the mesh
1271 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1273 // Check if animation is required for this mesh
1275 !m_crack_materials.empty() ||
1276 !m_daynight_diffs.empty() ||
1277 !m_animation_tiles.empty();
1280 MapBlockMesh::~MapBlockMesh()
1286 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1288 bool enable_shaders = g_settings->getBool("enable_shaders");
1289 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1290 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1292 if(!m_has_animation)
1294 m_animation_force_timer = 100000;
1298 m_animation_force_timer = myrand_range(5, 100);
1301 if(crack != m_last_crack)
1303 for(std::map<u32, std::string>::iterator
1304 i = m_crack_materials.begin();
1305 i != m_crack_materials.end(); i++)
1307 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1308 std::string basename = i->second;
1310 // Create new texture name from original
1311 ITextureSource *tsrc = m_gamedef->getTextureSource();
1312 std::ostringstream os;
1313 os<<basename<<crack;
1314 u32 new_texture_id = 0;
1315 video::ITexture *new_texture =
1316 tsrc->getTexture(os.str(), &new_texture_id);
1317 buf->getMaterial().setTexture(0, new_texture);
1319 // If the current material is also animated,
1320 // update animation info
1321 std::map<u32, TileSpec>::iterator anim_iter =
1322 m_animation_tiles.find(i->first);
1323 if(anim_iter != m_animation_tiles.end()){
1324 TileSpec &tile = anim_iter->second;
1325 tile.texture = new_texture;
1326 tile.texture_id = new_texture_id;
1327 // force animation update
1328 m_animation_frames[i->first] = -1;
1332 m_last_crack = crack;
1335 // Texture animation
1336 for(std::map<u32, TileSpec>::iterator
1337 i = m_animation_tiles.begin();
1338 i != m_animation_tiles.end(); i++)
1340 const TileSpec &tile = i->second;
1341 // Figure out current frame
1342 int frameoffset = m_animation_frame_offsets[i->first];
1343 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1344 + frameoffset) % tile.animation_frame_count;
1345 // If frame doesn't change, skip
1346 if(frame == m_animation_frames[i->first])
1349 m_animation_frames[i->first] = frame;
1351 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1352 ITextureSource *tsrc = m_gamedef->getTextureSource();
1353 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1355 // Create new texture name from original
1356 std::ostringstream os(std::ios::binary);
1357 os<<tsrc->getTextureName(tile.texture_id);
1358 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1360 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1361 if (enable_shaders){
1362 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1363 buf->getMaterial().MaterialType = shdrsrc->getShaderInfo(tile.shader_id).material;
1364 if (enable_bumpmapping || enable_parallax_occlusion){
1365 if (tsrc->isKnownSourceImage("override_normal.png")){
1366 buf->getMaterial().setTexture(1, tsrc->getTexture("override_normal.png"));
1367 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1369 std::string fname_base,fname_normal;
1370 fname_base = tsrc->getTextureName(tile.texture_id);
1372 pos = fname_base.find(".");
1373 fname_normal = fname_base.substr (0, pos);
1374 fname_normal += "_normal.png";
1375 if (tsrc->isKnownSourceImage(fname_normal)){
1377 os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1378 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
1379 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1386 // Day-night transition
1387 if(daynight_ratio != m_last_daynight_ratio)
1389 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1390 i = m_daynight_diffs.begin();
1391 i != m_daynight_diffs.end(); i++)
1393 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1394 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1395 for(std::map<u32, std::pair<u8, u8 > >::iterator
1396 j = i->second.begin();
1397 j != i->second.end(); j++)
1399 u32 vertexIndex = j->first;
1400 u8 day = j->second.first;
1401 u8 night = j->second.second;
1402 finalColorBlend(vertices[vertexIndex].Color,
1403 day, night, daynight_ratio);
1404 // Make sides and bottom darker than the top
1405 video::SColor &vc = vertices[vertexIndex].Color;
1406 if(vertices[vertexIndex].Normal.Y > 0.5) {
1407 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.2, 255.0));
1408 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.2, 255.0));
1409 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.2, 255.0));
1410 } else if (vertices[vertexIndex].Normal.Y < -0.5) {
1411 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.3, 255.0));
1412 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.3, 255.0));
1413 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.3, 255.0));
1414 } else if (vertices[vertexIndex].Normal.X > 0.5) {
1415 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.8, 255.0));
1416 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.8, 255.0));
1417 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.8, 255.0));
1418 } else if (vertices[vertexIndex].Normal.X < -0.5) {
1419 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.8, 255.0));
1420 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.8, 255.0));
1421 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.8, 255.0));
1422 } else if (vertices[vertexIndex].Normal.Z > 0.5) {
1423 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.5, 255.0));
1424 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.5, 255.0));
1425 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.5, 255.0));
1426 } else if (vertices[vertexIndex].Normal.Z < -0.5) {
1427 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.5, 255.0));
1428 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.5, 255.0));
1429 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.5, 255.0));
1433 m_last_daynight_ratio = daynight_ratio;
1439 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1441 if (camera_offset != m_camera_offset) {
1442 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1443 m_camera_offset = camera_offset;
1451 void MeshCollector::append(const TileSpec &tile,
1452 const video::S3DVertex *vertices, u32 numVertices,
1453 const u16 *indices, u32 numIndices)
1455 if(numIndices > 65535)
1457 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1461 PreMeshBuffer *p = NULL;
1462 for(u32 i=0; i<prebuffers.size(); i++)
1464 PreMeshBuffer &pp = prebuffers[i];
1467 if(pp.indices.size() + numIndices > 65535)
1478 prebuffers.push_back(pp);
1479 p = &prebuffers[prebuffers.size()-1];
1482 u32 vertex_count = p->vertices.size();
1483 for(u32 i=0; i<numIndices; i++)
1485 u32 j = indices[i] + vertex_count;
1486 p->indices.push_back(j);
1488 for(u32 i=0; i<numVertices; i++)
1490 p->vertices.push_back(vertices[i]);