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);
773 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
774 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
775 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
778 bool equivalent = false;
779 u8 mf = face_contents(n0.getContent(), n1.getContent(),
794 face_dir_corrected = face_dir;
795 light_source = ndef->get(n0).light_source;
800 p_corrected = p + face_dir;
801 face_dir_corrected = -face_dir;
802 light_source = ndef->get(n1).light_source;
805 // eg. water and glass
807 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
809 if(data->m_smooth_lighting == false)
811 lights[0] = lights[1] = lights[2] = lights[3] =
812 getFaceLight(n0, n1, face_dir, ndef);
816 v3s16 vertex_dirs[4];
817 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
818 for(u16 i=0; i<4; i++)
820 lights[i] = getSmoothLight(
821 blockpos_nodes + p_corrected,
822 vertex_dirs[i], data);
831 translate_dir: unit vector with only one of x, y or z
832 face_dir: unit vector with only one of x, y or z
834 static void updateFastFaceRow(
841 std::vector<FastFace> &dest)
845 u16 continuous_tiles_count = 0;
847 bool makes_face = false;
849 v3s16 face_dir_corrected;
850 u16 lights[4] = {0,0,0,0};
853 getTileInfo(data, p, face_dir,
854 makes_face, p_corrected, face_dir_corrected,
855 lights, tile, light_source);
857 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
859 // If tiling can be done, this is set to false in the next step
860 bool next_is_different = true;
864 bool next_makes_face = false;
865 v3s16 next_p_corrected;
866 v3s16 next_face_dir_corrected;
867 u16 next_lights[4] = {0,0,0,0};
869 u8 next_light_source = 0;
871 // If at last position, there is nothing to compare to and
872 // the face must be drawn anyway
873 if(j != MAP_BLOCKSIZE - 1)
875 p_next = p + translate_dir;
877 getTileInfo(data, p_next, face_dir,
878 next_makes_face, next_p_corrected,
879 next_face_dir_corrected, next_lights,
880 next_tile, next_light_source);
882 if(next_makes_face == makes_face
883 && next_p_corrected == p_corrected + translate_dir
884 && next_face_dir_corrected == face_dir_corrected
885 && next_lights[0] == lights[0]
886 && next_lights[1] == lights[1]
887 && next_lights[2] == lights[2]
888 && next_lights[3] == lights[3]
890 && tile.rotation == 0
891 && next_light_source == light_source)
893 next_is_different = false;
897 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
898 next_makes_face != makes_face ? 1 : 0);
899 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
900 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
901 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
902 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
903 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
904 (next_lights[0] != lights[0] ||
905 next_lights[0] != lights[0] ||
906 next_lights[0] != lights[0] ||
907 next_lights[0] != lights[0]) ? 1 : 0);
908 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
909 !(next_tile == tile) ? 1 : 0);
912 /*g_profiler->add("Meshgen: Total faces checked", 1);
914 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
917 g_profiler->add("Meshgen: diff: last position", 1);*/
920 continuous_tiles_count++;
922 if(next_is_different)
925 Create a face if there should be one
929 // Floating point conversion of the position vector
930 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
931 // Center point of face (kind of)
932 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
933 if(continuous_tiles_count != 1)
934 sp += translate_dir_f;
937 if(translate_dir.X != 0)
939 scale.X = continuous_tiles_count;
941 if(translate_dir.Y != 0)
943 scale.Y = continuous_tiles_count;
945 if(translate_dir.Z != 0)
947 scale.Z = continuous_tiles_count;
950 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
951 sp, face_dir_corrected, scale, light_source,
954 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
955 for(int i=1; i<continuous_tiles_count; i++){
956 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
960 continuous_tiles_count = 0;
962 makes_face = next_makes_face;
963 p_corrected = next_p_corrected;
964 face_dir_corrected = next_face_dir_corrected;
965 lights[0] = next_lights[0];
966 lights[1] = next_lights[1];
967 lights[2] = next_lights[2];
968 lights[3] = next_lights[3];
970 light_source = next_light_source;
977 static void updateAllFastFaceRows(MeshMakeData *data,
978 std::vector<FastFace> &dest)
981 Go through every y,z and get top(y+) faces in rows of x+
983 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
984 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
985 updateFastFaceRow(data,
989 v3s16(0,1,0), //face dir
996 Go through every x,y and get right(x+) faces in rows of z+
998 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
999 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1000 updateFastFaceRow(data,
1004 v3s16(1,0,0), //face dir
1011 Go through every y,z and get back(z+) faces in rows of x+
1013 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1014 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1015 updateFastFaceRow(data,
1019 v3s16(0,0,1), //face dir
1030 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1031 m_mesh(new scene::SMesh()),
1032 m_gamedef(data->m_gamedef),
1033 m_animation_force_timer(0), // force initial animation
1035 m_crack_materials(),
1036 m_last_daynight_ratio((u32) -1),
1039 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1040 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1041 //TimeTaker timer1("MapBlockMesh()");
1043 std::vector<FastFace> fastfaces_new;
1046 We are including the faces of the trailing edges of the block.
1047 This means that when something changes, the caller must
1048 also update the meshes of the blocks at the leading edges.
1050 NOTE: This is the slowest part of this method.
1053 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1054 //TimeTaker timer2("updateAllFastFaceRows()");
1055 updateAllFastFaceRows(data, fastfaces_new);
1060 Convert FastFaces to MeshCollector
1063 MeshCollector collector;
1066 // avg 0ms (100ms spikes when loading textures the first time)
1067 // (NOTE: probably outdated)
1068 //TimeTaker timer2("MeshCollector building");
1070 for(u32 i=0; i<fastfaces_new.size(); i++)
1072 FastFace &f = fastfaces_new[i];
1074 const u16 indices[] = {0,1,2,2,3,0};
1075 const u16 indices_alternate[] = {0,1,3,2,3,1};
1077 if(f.tile.texture == NULL)
1080 const u16 *indices_p = indices;
1083 Revert triangles for nicer looking gradient if vertices
1084 1 and 3 have same color or 0 and 2 have different color.
1085 getRed() is the day color.
1087 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1088 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1089 indices_p = indices_alternate;
1091 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1096 Add special graphics:
1103 mapblock_mesh_generate_special(data, collector);
1107 Convert MeshCollector to SMesh
1109 ITextureSource *tsrc = m_gamedef->tsrc();
1110 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1112 bool enable_shaders = g_settings->getBool("enable_shaders");
1113 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1114 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1116 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1118 PreMeshBuffer &p = collector.prebuffers[i];
1119 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1120 <<", p.indices.size()="<<p.indices.size()
1123 // Generate animation data
1125 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1127 // Find the texture name plus ^[crack:N:
1128 std::ostringstream os(std::ios::binary);
1129 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1130 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1131 os<<"o"; // use ^[cracko
1132 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1133 m_crack_materials.insert(std::make_pair(i, os.str()));
1134 // Replace tile texture with the cracked one
1135 p.tile.texture = tsrc->getTexture(
1137 &p.tile.texture_id);
1139 // - Texture animation
1140 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1142 // Add to MapBlockMesh in order to animate these tiles
1143 m_animation_tiles[i] = p.tile;
1144 m_animation_frames[i] = 0;
1145 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1146 // Get starting position from noise
1147 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1148 data->m_blockpos.X, data->m_blockpos.Y,
1149 data->m_blockpos.Z, 0));
1151 // Play all synchronized
1152 m_animation_frame_offsets[i] = 0;
1154 // Replace tile texture with the first animation frame
1155 std::ostringstream os(std::ios::binary);
1156 os<<tsrc->getTextureName(p.tile.texture_id);
1157 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1158 p.tile.texture = tsrc->getTexture(
1160 &p.tile.texture_id);
1162 // - Classic lighting (shaders handle this by themselves)
1165 for(u32 j = 0; j < p.vertices.size(); j++)
1167 video::SColor &vc = p.vertices[j].Color;
1168 // Set initial real color and store for later updates
1169 u8 day = vc.getRed();
1170 u8 night = vc.getGreen();
1171 finalColorBlend(vc, day, night, 1000);
1173 m_daynight_diffs[i][j] = std::make_pair(day, night);
1174 // Brighten topside (no shaders)
1175 if(p.vertices[j].Normal.Y > 0.5)
1177 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1178 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1179 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1185 video::SMaterial material;
1186 material.setFlag(video::EMF_LIGHTING, false);
1187 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1188 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1189 material.setFlag(video::EMF_FOG_ENABLE, true);
1190 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1191 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1192 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1193 material.setTexture(0, p.tile.texture);
1195 if (enable_shaders) {
1196 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1197 p.tile.applyMaterialOptionsWithShaders(material);
1198 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1199 if (enable_bumpmapping || enable_parallax_occlusion) {
1200 if (tsrc->isKnownSourceImage("override_normal.png")){
1201 material.setTexture(1, tsrc->getTexture("override_normal.png"));
1202 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1204 std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
1205 std::string normal_ext = "_normal.png";
1206 size_t pos = fname_base.find(".");
1207 std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1209 if (tsrc->isKnownSourceImage(fname_normal)) {
1210 // look for image extension and replace it
1212 while ((i = fname_base.find(".", i)) != std::string::npos) {
1213 fname_base.replace(i, 4, normal_ext);
1214 i += normal_ext.length();
1216 material.setTexture(1, tsrc->getTexture(fname_base));
1217 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1222 p.tile.applyMaterialOptions(material);
1224 // Create meshbuffer
1226 // This is a "Standard MeshBuffer",
1227 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1228 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1230 buf->Material = material;
1232 m_mesh->addMeshBuffer(buf);
1235 buf->append(&p.vertices[0], p.vertices.size(),
1236 &p.indices[0], p.indices.size());
1239 m_camera_offset = camera_offset;
1242 Do some stuff to the mesh
1245 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1250 // Usually 1-700 faces and 1-7 materials
1251 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1252 <<"and uses "<<m_mesh->getMeshBufferCount()
1253 <<" materials (meshbuffers)"<<std::endl;
1256 // Use VBO for mesh (this just would set this for ever buffer)
1257 // This will lead to infinite memory usage because or irrlicht.
1258 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1261 NOTE: If that is enabled, some kind of a queue to the main
1262 thread should be made which would call irrlicht to delete
1263 the hardware buffer and then delete the mesh
1267 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1269 // Check if animation is required for this mesh
1271 !m_crack_materials.empty() ||
1272 !m_daynight_diffs.empty() ||
1273 !m_animation_tiles.empty();
1276 MapBlockMesh::~MapBlockMesh()
1282 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1284 bool enable_shaders = g_settings->getBool("enable_shaders");
1285 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1286 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1288 if(!m_has_animation)
1290 m_animation_force_timer = 100000;
1294 m_animation_force_timer = myrand_range(5, 100);
1297 if(crack != m_last_crack)
1299 for(std::map<u32, std::string>::iterator
1300 i = m_crack_materials.begin();
1301 i != m_crack_materials.end(); i++)
1303 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1304 std::string basename = i->second;
1306 // Create new texture name from original
1307 ITextureSource *tsrc = m_gamedef->getTextureSource();
1308 std::ostringstream os;
1309 os<<basename<<crack;
1310 u32 new_texture_id = 0;
1311 video::ITexture *new_texture =
1312 tsrc->getTexture(os.str(), &new_texture_id);
1313 buf->getMaterial().setTexture(0, new_texture);
1315 // If the current material is also animated,
1316 // update animation info
1317 std::map<u32, TileSpec>::iterator anim_iter =
1318 m_animation_tiles.find(i->first);
1319 if(anim_iter != m_animation_tiles.end()){
1320 TileSpec &tile = anim_iter->second;
1321 tile.texture = new_texture;
1322 tile.texture_id = new_texture_id;
1323 // force animation update
1324 m_animation_frames[i->first] = -1;
1328 m_last_crack = crack;
1331 // Texture animation
1332 for(std::map<u32, TileSpec>::iterator
1333 i = m_animation_tiles.begin();
1334 i != m_animation_tiles.end(); i++)
1336 const TileSpec &tile = i->second;
1337 // Figure out current frame
1338 int frameoffset = m_animation_frame_offsets[i->first];
1339 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1340 + frameoffset) % tile.animation_frame_count;
1341 // If frame doesn't change, skip
1342 if(frame == m_animation_frames[i->first])
1345 m_animation_frames[i->first] = frame;
1347 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1348 ITextureSource *tsrc = m_gamedef->getTextureSource();
1349 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1351 // Create new texture name from original
1352 std::ostringstream os(std::ios::binary);
1353 os<<tsrc->getTextureName(tile.texture_id);
1354 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1356 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1357 if (enable_shaders){
1358 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1359 buf->getMaterial().MaterialType = shdrsrc->getShaderInfo(tile.shader_id).material;
1360 if (enable_bumpmapping || enable_parallax_occlusion){
1361 if (tsrc->isKnownSourceImage("override_normal.png")){
1362 buf->getMaterial().setTexture(1, tsrc->getTexture("override_normal.png"));
1363 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1365 std::string fname_base,fname_normal;
1366 fname_base = tsrc->getTextureName(tile.texture_id);
1368 pos = fname_base.find(".");
1369 fname_normal = fname_base.substr (0, pos);
1370 fname_normal += "_normal.png";
1371 if (tsrc->isKnownSourceImage(fname_normal)){
1373 os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1374 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
1375 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1382 // Day-night transition
1383 if(daynight_ratio != m_last_daynight_ratio)
1385 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1386 i = m_daynight_diffs.begin();
1387 i != m_daynight_diffs.end(); i++)
1389 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1390 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1391 for(std::map<u32, std::pair<u8, u8 > >::iterator
1392 j = i->second.begin();
1393 j != i->second.end(); j++)
1395 u32 vertexIndex = j->first;
1396 u8 day = j->second.first;
1397 u8 night = j->second.second;
1398 finalColorBlend(vertices[vertexIndex].Color,
1399 day, night, daynight_ratio);
1400 // Make sides and bottom darker than the top
1401 video::SColor &vc = vertices[vertexIndex].Color;
1402 if(vertices[vertexIndex].Normal.Y > 0.5) {
1403 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.2, 255.0));
1404 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.2, 255.0));
1405 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.2, 255.0));
1406 } else if (vertices[vertexIndex].Normal.Y < -0.5) {
1407 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.3, 255.0));
1408 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.3, 255.0));
1409 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.3, 255.0));
1410 } else if (vertices[vertexIndex].Normal.X > 0.5) {
1411 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.8, 255.0));
1412 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.8, 255.0));
1413 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.8, 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.Z > 0.5) {
1419 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.5, 255.0));
1420 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.5, 255.0));
1421 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.5, 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));
1429 m_last_daynight_ratio = daynight_ratio;
1435 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1437 if (camera_offset != m_camera_offset) {
1438 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1439 m_camera_offset = camera_offset;
1447 void MeshCollector::append(const TileSpec &tile,
1448 const video::S3DVertex *vertices, u32 numVertices,
1449 const u16 *indices, u32 numIndices)
1451 if(numIndices > 65535)
1453 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1457 PreMeshBuffer *p = NULL;
1458 for(u32 i=0; i<prebuffers.size(); i++)
1460 PreMeshBuffer &pp = prebuffers[i];
1463 if(pp.indices.size() + numIndices > 65535)
1474 prebuffers.push_back(pp);
1475 p = &prebuffers[prebuffers.size()-1];
1478 u32 vertex_count = p->vertices.size();
1479 for(u32 i=0; i<numIndices; i++)
1481 u32 j = indices[i] + vertex_count;
1482 p->indices.push_back(j);
1484 for(u32 i=0; i<numVertices; i++)
1486 p->vertices.push_back(vertices[i]);