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,
155 INodeDefManager *ndef = data->m_gamedef->ndef();
156 u8 light = n.getLight(bank, ndef);
160 light = undiminish_light(light);
165 light = diminish_light(light);
169 return decode_light(light);
173 Calculate non-smooth lighting at interior of node.
176 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
178 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
179 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
180 return day | (night << 8);
184 Calculate non-smooth lighting at face of node.
187 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
188 v3s16 face_dir, MeshMakeData *data)
190 INodeDefManager *ndef = data->m_gamedef->ndef();
193 u8 l1 = n.getLight(bank, ndef);
194 u8 l2 = n2.getLight(bank, ndef);
200 // Boost light level for light sources
201 u8 light_source = MYMAX(ndef->get(n).light_source,
202 ndef->get(n2).light_source);
203 //if(light_source >= light)
204 //return decode_light(undiminish_light(light_source));
205 if(light_source > light)
206 //return decode_light(light_source);
207 light = light_source;
209 // Make some nice difference to different sides
211 // This makes light come from a corner
212 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
213 light = diminish_light(diminish_light(light));
214 else if(face_dir.X == -1 || face_dir.Z == -1)
215 light = diminish_light(light);*/
217 // All neighboring faces have different shade (like in minecraft)
218 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
219 light = diminish_light(diminish_light(light));
220 else if(face_dir.Z == 1 || face_dir.Z == -1)
221 light = diminish_light(light);
223 return decode_light(light);
227 Calculate non-smooth lighting at face of node.
230 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
232 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
233 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
234 return day | (night << 8);
238 Calculate smooth lighting at the XYZ- corner of p.
241 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
243 static v3s16 dirs8[8] = {
254 INodeDefManager *ndef = data->m_gamedef->ndef();
256 u16 ambient_occlusion = 0;
259 u8 light_source_max = 0;
260 for(u32 i=0; i<8; i++)
262 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
263 const ContentFeatures &f = ndef->get(n);
264 if(f.light_source > light_source_max)
265 light_source_max = f.light_source;
266 // Check f.solidness because fast-style leaves look
268 if(f.param_type == CPT_LIGHT && f.solidness != 2)
270 light += decode_light(n.getLight(bank, ndef));
273 else if(n.getContent() != CONTENT_IGNORE)
282 light /= light_count;
284 // Boost brightness around light sources
285 if(decode_light(light_source_max) >= light)
286 //return decode_light(undiminish_light(light_source_max));
287 return decode_light(light_source_max);
289 if(ambient_occlusion > 4)
291 //ambient_occlusion -= 4;
292 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
293 float light_amount = (8 - ambient_occlusion) / 4.0;
294 float light_f = (float)light / 255.0;
295 light_f = pow(light_f, 2.2f); // gamma -> linear space
296 light_f = light_f * light_amount;
297 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
300 light = 255.0 * light_f + 0.5;
307 Calculate smooth lighting at the XYZ- corner of p.
310 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
312 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
313 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
314 return day | (night << 8);
318 Calculate smooth lighting at the given corner of p.
321 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
323 if(corner.X == 1) p.X += 1;
324 else assert(corner.X == -1);
325 if(corner.Y == 1) p.Y += 1;
326 else assert(corner.Y == -1);
327 if(corner.Z == 1) p.Z += 1;
328 else assert(corner.Z == -1);
330 return getSmoothLight(p, data);
334 Converts from day + night color values (0..255)
335 and a given daynight_ratio to the final SColor shown on screen.
337 static void finalColorBlend(video::SColor& result,
338 u8 day, u8 night, u32 daynight_ratio)
340 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
344 b += (day - night) / 13;
345 rg -= (day - night) / 23;
347 // Emphase blue a bit in darker places
348 // Each entry of this array represents a range of 8 blue levels
349 static u8 emphase_blue_when_dark[32] = {
350 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 b += emphase_blue_when_dark[b / 8];
359 // Artificial light is yellow-ish
360 static u8 emphase_yellow_when_artificial[16] = {
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
363 rg += emphase_yellow_when_artificial[night/16];
375 Mesh generation helpers
379 vertex_dirs: v3s16[4]
381 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
384 If looked from outside the node towards the face, the corners are:
390 if(dir == v3s16(0,0,1))
392 // If looking towards z+, this is the face that is behind
393 // the center point, facing towards z+.
394 vertex_dirs[0] = v3s16(-1,-1, 1);
395 vertex_dirs[1] = v3s16( 1,-1, 1);
396 vertex_dirs[2] = v3s16( 1, 1, 1);
397 vertex_dirs[3] = v3s16(-1, 1, 1);
399 else if(dir == v3s16(0,0,-1))
402 vertex_dirs[0] = v3s16( 1,-1,-1);
403 vertex_dirs[1] = v3s16(-1,-1,-1);
404 vertex_dirs[2] = v3s16(-1, 1,-1);
405 vertex_dirs[3] = v3s16( 1, 1,-1);
407 else if(dir == v3s16(1,0,0))
410 vertex_dirs[0] = v3s16( 1,-1, 1);
411 vertex_dirs[1] = v3s16( 1,-1,-1);
412 vertex_dirs[2] = v3s16( 1, 1,-1);
413 vertex_dirs[3] = v3s16( 1, 1, 1);
415 else if(dir == v3s16(-1,0,0))
418 vertex_dirs[0] = v3s16(-1,-1,-1);
419 vertex_dirs[1] = v3s16(-1,-1, 1);
420 vertex_dirs[2] = v3s16(-1, 1, 1);
421 vertex_dirs[3] = v3s16(-1, 1,-1);
423 else if(dir == v3s16(0,1,0))
425 // faces towards Y+ (assume Z- as "down" in texture)
426 vertex_dirs[0] = v3s16( 1, 1,-1);
427 vertex_dirs[1] = v3s16(-1, 1,-1);
428 vertex_dirs[2] = v3s16(-1, 1, 1);
429 vertex_dirs[3] = v3s16( 1, 1, 1);
431 else if(dir == v3s16(0,-1,0))
433 // faces towards Y- (assume Z+ as "down" in texture)
434 vertex_dirs[0] = v3s16( 1,-1, 1);
435 vertex_dirs[1] = v3s16(-1,-1, 1);
436 vertex_dirs[2] = v3s16(-1,-1,-1);
437 vertex_dirs[3] = v3s16( 1,-1,-1);
444 video::S3DVertex vertices[4]; // Precalculated vertices
447 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
448 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
452 // Position is at the center of the cube.
461 v3s16 vertex_dirs[4];
462 getNodeVertexDirs(dir, vertex_dirs);
466 switch (tile.rotation)
472 vertex_dirs[0] = vertex_dirs[3];
473 vertex_dirs[3] = vertex_dirs[2];
474 vertex_dirs[2] = vertex_dirs[1];
484 vertex_dirs[0] = vertex_dirs[2];
487 vertex_dirs[1] = vertex_dirs[3];
498 vertex_dirs[0] = vertex_dirs[1];
499 vertex_dirs[1] = vertex_dirs[2];
500 vertex_dirs[2] = vertex_dirs[3];
510 vertex_dirs[0] = vertex_dirs[3];
511 vertex_dirs[3] = vertex_dirs[2];
512 vertex_dirs[2] = vertex_dirs[1];
524 vertex_dirs[0] = vertex_dirs[1];
525 vertex_dirs[1] = vertex_dirs[2];
526 vertex_dirs[2] = vertex_dirs[3];
538 vertex_dirs[0] = vertex_dirs[3];
539 vertex_dirs[3] = vertex_dirs[2];
540 vertex_dirs[2] = vertex_dirs[1];
552 vertex_dirs[0] = vertex_dirs[1];
553 vertex_dirs[1] = vertex_dirs[2];
554 vertex_dirs[2] = vertex_dirs[3];
576 for(u16 i=0; i<4; i++)
579 BS/2*vertex_dirs[i].X,
580 BS/2*vertex_dirs[i].Y,
581 BS/2*vertex_dirs[i].Z
585 for(u16 i=0; i<4; i++)
587 vertex_pos[i].X *= scale.X;
588 vertex_pos[i].Y *= scale.Y;
589 vertex_pos[i].Z *= scale.Z;
590 vertex_pos[i] += pos;
594 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
595 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
596 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
598 v3f normal(dir.X, dir.Y, dir.Z);
600 u8 alpha = tile.alpha;
602 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
603 MapBlock_LightColor(alpha, li0, light_source),
604 core::vector2d<f32>(x0+w*abs_scale, y0+h));
605 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
606 MapBlock_LightColor(alpha, li1, light_source),
607 core::vector2d<f32>(x0, y0+h));
608 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
609 MapBlock_LightColor(alpha, li2, light_source),
610 core::vector2d<f32>(x0, y0));
611 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
612 MapBlock_LightColor(alpha, li3, light_source),
613 core::vector2d<f32>(x0+w*abs_scale, y0));
616 dest.push_back(face);
620 Nodes make a face if contents differ and solidness differs.
623 1: Face uses m1's content
624 2: Face uses m2's content
625 equivalent: Whether the blocks share the same face (eg. water and glass)
627 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
629 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
630 INodeDefManager *ndef)
634 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
637 bool contents_differ = (m1 != m2);
639 const ContentFeatures &f1 = ndef->get(m1);
640 const ContentFeatures &f2 = ndef->get(m2);
642 // Contents don't differ for different forms of same liquid
643 if(f1.sameLiquid(f2))
644 contents_differ = false;
646 u8 c1 = f1.solidness;
647 u8 c2 = f2.solidness;
649 bool solidness_differs = (c1 != c2);
650 bool makes_face = contents_differ && solidness_differs;
652 if(makes_face == false)
656 c1 = f1.visual_solidness;
658 c2 = f2.visual_solidness;
662 // If same solidness, liquid takes precense
676 Gets nth node tile (0 <= n <= 5).
678 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
680 INodeDefManager *ndef = data->m_gamedef->ndef();
681 TileSpec spec = ndef->get(mn).tiles[tileindex];
682 // Apply temporary crack
683 if(p == data->m_crack_pos_relative)
685 spec.material_flags |= MATERIAL_FLAG_CRACK;
691 Gets node tile given a face direction.
693 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
695 INodeDefManager *ndef = data->m_gamedef->ndef();
697 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
698 // (0,0,1), (0,0,-1) or (0,0,0)
699 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
701 // Convert direction to single integer for table lookup
706 // 4 = invalid, treat as (0,0,0)
710 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
712 // Get rotation for things like chests
713 u8 facedir = mn.getFaceDir(ndef);
716 static const u16 dir_to_tile[24 * 16] =
718 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
719 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
720 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
721 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
722 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
724 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
725 0,0, 4,3 , 2,0 , 0,3 , 0,0, 1,1 , 3,2 , 5,1 ,
726 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
727 0,0, 5,3 , 3,0 , 0,1 , 0,0, 1,3 , 2,2 , 4,1 ,
729 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
730 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
731 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
732 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
734 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
735 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
736 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
737 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
739 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
740 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
741 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
742 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
744 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
745 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
746 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
747 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
750 u16 tile_index=facedir*16 + dir_i;
751 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
752 spec.rotation=dir_to_tile[tile_index + 1];
753 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
757 static void getTileInfo(
765 v3s16 &face_dir_corrected,
771 VoxelManipulator &vmanip = data->m_vmanip;
772 INodeDefManager *ndef = data->m_gamedef->ndef();
773 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
775 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
776 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
777 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
778 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
781 bool equivalent = false;
782 u8 mf = face_contents(n0.getContent(), n1.getContent(),
797 face_dir_corrected = face_dir;
798 light_source = ndef->get(n0).light_source;
803 p_corrected = p + face_dir;
804 face_dir_corrected = -face_dir;
805 light_source = ndef->get(n1).light_source;
808 // eg. water and glass
810 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
812 if(data->m_smooth_lighting == false)
814 lights[0] = lights[1] = lights[2] = lights[3] =
815 getFaceLight(n0, n1, face_dir, data);
819 v3s16 vertex_dirs[4];
820 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
821 for(u16 i=0; i<4; i++)
823 lights[i] = getSmoothLight(
824 blockpos_nodes + p_corrected,
825 vertex_dirs[i], data);
834 translate_dir: unit vector with only one of x, y or z
835 face_dir: unit vector with only one of x, y or z
837 static void updateFastFaceRow(
844 std::vector<FastFace> &dest)
848 u16 continuous_tiles_count = 0;
850 bool makes_face = false;
852 v3s16 face_dir_corrected;
853 u16 lights[4] = {0,0,0,0};
856 getTileInfo(data, p, face_dir,
857 makes_face, p_corrected, face_dir_corrected,
858 lights, tile, light_source);
860 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
862 // If tiling can be done, this is set to false in the next step
863 bool next_is_different = true;
867 bool next_makes_face = false;
868 v3s16 next_p_corrected;
869 v3s16 next_face_dir_corrected;
870 u16 next_lights[4] = {0,0,0,0};
872 u8 next_light_source = 0;
874 // If at last position, there is nothing to compare to and
875 // the face must be drawn anyway
876 if(j != MAP_BLOCKSIZE - 1)
878 p_next = p + translate_dir;
880 getTileInfo(data, p_next, face_dir,
881 next_makes_face, next_p_corrected,
882 next_face_dir_corrected, next_lights,
883 next_tile, next_light_source);
885 if(next_makes_face == makes_face
886 && next_p_corrected == p_corrected + translate_dir
887 && next_face_dir_corrected == face_dir_corrected
888 && next_lights[0] == lights[0]
889 && next_lights[1] == lights[1]
890 && next_lights[2] == lights[2]
891 && next_lights[3] == lights[3]
893 && tile.rotation == 0
894 && next_light_source == light_source)
896 next_is_different = false;
900 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
901 next_makes_face != makes_face ? 1 : 0);
902 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
903 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
904 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
905 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
906 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
907 (next_lights[0] != lights[0] ||
908 next_lights[0] != lights[0] ||
909 next_lights[0] != lights[0] ||
910 next_lights[0] != lights[0]) ? 1 : 0);
911 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
912 !(next_tile == tile) ? 1 : 0);
915 /*g_profiler->add("Meshgen: Total faces checked", 1);
917 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
920 g_profiler->add("Meshgen: diff: last position", 1);*/
923 continuous_tiles_count++;
925 if(next_is_different)
928 Create a face if there should be one
932 // Floating point conversion of the position vector
933 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
934 // Center point of face (kind of)
935 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
936 if(continuous_tiles_count != 1)
937 sp += translate_dir_f;
940 if(translate_dir.X != 0)
942 scale.X = continuous_tiles_count;
944 if(translate_dir.Y != 0)
946 scale.Y = continuous_tiles_count;
948 if(translate_dir.Z != 0)
950 scale.Z = continuous_tiles_count;
953 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
954 sp, face_dir_corrected, scale, light_source,
957 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
958 for(int i=1; i<continuous_tiles_count; i++){
959 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
963 continuous_tiles_count = 0;
965 makes_face = next_makes_face;
966 p_corrected = next_p_corrected;
967 face_dir_corrected = next_face_dir_corrected;
968 lights[0] = next_lights[0];
969 lights[1] = next_lights[1];
970 lights[2] = next_lights[2];
971 lights[3] = next_lights[3];
973 light_source = next_light_source;
980 static void updateAllFastFaceRows(MeshMakeData *data,
981 std::vector<FastFace> &dest)
984 Go through every y,z and get top(y+) faces in rows of x+
986 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
987 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
988 updateFastFaceRow(data,
992 v3s16(0,1,0), //face dir
999 Go through every x,y and get right(x+) faces in rows of z+
1001 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
1002 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1003 updateFastFaceRow(data,
1007 v3s16(1,0,0), //face dir
1014 Go through every y,z and get back(z+) faces in rows of x+
1016 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1017 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1018 updateFastFaceRow(data,
1022 v3s16(0,0,1), //face dir
1033 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
1034 m_mesh(new scene::SMesh()),
1035 m_gamedef(data->m_gamedef),
1036 m_animation_force_timer(0), // force initial animation
1038 m_crack_materials(),
1039 m_last_daynight_ratio((u32) -1),
1042 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1043 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1044 //TimeTaker timer1("MapBlockMesh()");
1046 std::vector<FastFace> fastfaces_new;
1049 We are including the faces of the trailing edges of the block.
1050 This means that when something changes, the caller must
1051 also update the meshes of the blocks at the leading edges.
1053 NOTE: This is the slowest part of this method.
1056 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1057 //TimeTaker timer2("updateAllFastFaceRows()");
1058 updateAllFastFaceRows(data, fastfaces_new);
1063 Convert FastFaces to MeshCollector
1066 MeshCollector collector;
1069 // avg 0ms (100ms spikes when loading textures the first time)
1070 // (NOTE: probably outdated)
1071 //TimeTaker timer2("MeshCollector building");
1073 for(u32 i=0; i<fastfaces_new.size(); i++)
1075 FastFace &f = fastfaces_new[i];
1077 const u16 indices[] = {0,1,2,2,3,0};
1078 const u16 indices_alternate[] = {0,1,3,2,3,1};
1080 if(f.tile.texture == NULL)
1083 const u16 *indices_p = indices;
1086 Revert triangles for nicer looking gradient if vertices
1087 1 and 3 have same color or 0 and 2 have different color.
1088 getRed() is the day color.
1090 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1091 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1092 indices_p = indices_alternate;
1094 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1099 Add special graphics:
1106 mapblock_mesh_generate_special(data, collector);
1110 Convert MeshCollector to SMesh
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 video::E_MATERIAL_TYPE shadermat1, shadermat2, shadermat3,
1117 shadermat4, shadermat5;
1118 shadermat1 = shadermat2 = shadermat3 = shadermat4 = shadermat5 =
1121 if (enable_shaders) {
1122 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1123 shadermat1 = shdrsrc->getShader("solids_shader").material;
1124 shadermat2 = shdrsrc->getShader("liquids_shader").material;
1125 shadermat3 = shdrsrc->getShader("alpha_shader").material;
1126 shadermat4 = shdrsrc->getShader("leaves_shader").material;
1127 shadermat5 = shdrsrc->getShader("plants_shader").material;
1130 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1132 PreMeshBuffer &p = collector.prebuffers[i];
1133 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1134 <<", p.indices.size()="<<p.indices.size()
1137 // Generate animation data
1139 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1141 ITextureSource *tsrc = data->m_gamedef->tsrc();
1142 // Find the texture name plus ^[crack:N:
1143 std::ostringstream os(std::ios::binary);
1144 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1145 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1146 os<<"o"; // use ^[cracko
1147 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1148 m_crack_materials.insert(std::make_pair(i, os.str()));
1149 // Replace tile texture with the cracked one
1150 p.tile.texture = tsrc->getTexture(
1152 &p.tile.texture_id);
1154 // - Texture animation
1155 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1157 ITextureSource *tsrc = data->m_gamedef->tsrc();
1158 // Add to MapBlockMesh in order to animate these tiles
1159 m_animation_tiles[i] = p.tile;
1160 m_animation_frames[i] = 0;
1161 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1162 // Get starting position from noise
1163 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1164 data->m_blockpos.X, data->m_blockpos.Y,
1165 data->m_blockpos.Z, 0));
1167 // Play all synchronized
1168 m_animation_frame_offsets[i] = 0;
1170 // Replace tile texture with the first animation frame
1171 std::ostringstream os(std::ios::binary);
1172 os<<tsrc->getTextureName(p.tile.texture_id);
1173 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1174 p.tile.texture = tsrc->getTexture(
1176 &p.tile.texture_id);
1178 // - Classic lighting (shaders handle this by themselves)
1181 for(u32 j = 0; j < p.vertices.size(); j++)
1183 video::SColor &vc = p.vertices[j].Color;
1184 // Set initial real color and store for later updates
1185 u8 day = vc.getRed();
1186 u8 night = vc.getGreen();
1187 finalColorBlend(vc, day, night, 1000);
1189 m_daynight_diffs[i][j] = std::make_pair(day, night);
1190 // Brighten topside (no shaders)
1191 if(p.vertices[j].Normal.Y > 0.5)
1193 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1194 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1195 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1201 video::SMaterial material;
1202 material.setFlag(video::EMF_LIGHTING, false);
1203 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1204 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1205 material.setFlag(video::EMF_FOG_ENABLE, true);
1206 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1207 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1208 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1209 material.setTexture(0, p.tile.texture);
1211 if (enable_shaders) {
1212 ITextureSource *tsrc = data->m_gamedef->tsrc();
1213 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1214 if (enable_bumpmapping || enable_parallax_occlusion) {
1215 std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
1216 std::string normal_ext = "_normal.png";
1217 size_t pos = fname_base.find(".");
1218 std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1220 if (tsrc->isKnownSourceImage(fname_normal)) {
1221 // look for image extension and replace it
1223 while ((i = fname_base.find(".", i)) != std::string::npos) {
1224 fname_base.replace(i, 4, normal_ext);
1225 i += normal_ext.length();
1227 material.setTexture(1, tsrc->getTexture(fname_base));
1228 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1231 p.tile.applyMaterialOptionsWithShaders(material,
1232 shadermat1, shadermat2, shadermat3, shadermat4, shadermat5);
1234 p.tile.applyMaterialOptions(material);
1236 // Create meshbuffer
1238 // This is a "Standard MeshBuffer",
1239 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1240 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1242 buf->Material = material;
1244 m_mesh->addMeshBuffer(buf);
1247 buf->append(&p.vertices[0], p.vertices.size(),
1248 &p.indices[0], p.indices.size());
1252 Do some stuff to the mesh
1255 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1260 // Usually 1-700 faces and 1-7 materials
1261 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1262 <<"and uses "<<m_mesh->getMeshBufferCount()
1263 <<" materials (meshbuffers)"<<std::endl;
1266 // Use VBO for mesh (this just would set this for ever buffer)
1267 // This will lead to infinite memory usage because or irrlicht.
1268 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1271 NOTE: If that is enabled, some kind of a queue to the main
1272 thread should be made which would call irrlicht to delete
1273 the hardware buffer and then delete the mesh
1277 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1279 // Check if animation is required for this mesh
1281 !m_crack_materials.empty() ||
1282 !m_daynight_diffs.empty() ||
1283 !m_animation_tiles.empty();
1286 MapBlockMesh::~MapBlockMesh()
1292 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1294 bool enable_shaders = g_settings->getBool("enable_shaders");
1295 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1296 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1298 if(!m_has_animation)
1300 m_animation_force_timer = 100000;
1304 m_animation_force_timer = myrand_range(5, 100);
1307 if(crack != m_last_crack)
1309 for(std::map<u32, std::string>::iterator
1310 i = m_crack_materials.begin();
1311 i != m_crack_materials.end(); i++)
1313 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1314 std::string basename = i->second;
1316 // Create new texture name from original
1317 ITextureSource *tsrc = m_gamedef->getTextureSource();
1318 std::ostringstream os;
1319 os<<basename<<crack;
1320 u32 new_texture_id = 0;
1321 video::ITexture *new_texture =
1322 tsrc->getTexture(os.str(), &new_texture_id);
1323 buf->getMaterial().setTexture(0, new_texture);
1325 // If the current material is also animated,
1326 // update animation info
1327 std::map<u32, TileSpec>::iterator anim_iter =
1328 m_animation_tiles.find(i->first);
1329 if(anim_iter != m_animation_tiles.end()){
1330 TileSpec &tile = anim_iter->second;
1331 tile.texture = new_texture;
1332 tile.texture_id = new_texture_id;
1333 // force animation update
1334 m_animation_frames[i->first] = -1;
1338 m_last_crack = crack;
1341 // Texture animation
1342 for(std::map<u32, TileSpec>::iterator
1343 i = m_animation_tiles.begin();
1344 i != m_animation_tiles.end(); i++)
1346 const TileSpec &tile = i->second;
1347 // Figure out current frame
1348 int frameoffset = m_animation_frame_offsets[i->first];
1349 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1350 + frameoffset) % tile.animation_frame_count;
1351 // If frame doesn't change, skip
1352 if(frame == m_animation_frames[i->first])
1355 m_animation_frames[i->first] = frame;
1357 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1358 ITextureSource *tsrc = m_gamedef->getTextureSource();
1360 // Create new texture name from original
1361 std::ostringstream os(std::ios::binary);
1362 os<<tsrc->getTextureName(tile.texture_id);
1363 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1365 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1366 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1367 if (enable_shaders && (enable_bumpmapping || enable_parallax_occlusion))
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"));
1384 // Day-night transition
1385 if(daynight_ratio != m_last_daynight_ratio)
1387 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1388 i = m_daynight_diffs.begin();
1389 i != m_daynight_diffs.end(); i++)
1391 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1392 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1393 for(std::map<u32, std::pair<u8, u8 > >::iterator
1394 j = i->second.begin();
1395 j != i->second.end(); j++)
1397 u32 vertexIndex = j->first;
1398 u8 day = j->second.first;
1399 u8 night = j->second.second;
1400 finalColorBlend(vertices[vertexIndex].Color,
1401 day, night, daynight_ratio);
1402 // Brighten topside (no shaders)
1403 if(vertices[vertexIndex].Normal.Y > 0.5)
1405 video::SColor &vc = vertices[vertexIndex].Color;
1406 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1407 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1408 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1412 m_last_daynight_ratio = daynight_ratio;
1422 void MeshCollector::append(const TileSpec &tile,
1423 const video::S3DVertex *vertices, u32 numVertices,
1424 const u16 *indices, u32 numIndices)
1426 if(numIndices > 65535)
1428 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1432 PreMeshBuffer *p = NULL;
1433 for(u32 i=0; i<prebuffers.size(); i++)
1435 PreMeshBuffer &pp = prebuffers[i];
1438 if(pp.indices.size() + numIndices > 65535)
1449 prebuffers.push_back(pp);
1450 p = &prebuffers[prebuffers.size()-1];
1453 u32 vertex_count = p->vertices.size();
1454 for(u32 i=0; i<numIndices; i++)
1456 u32 j = indices[i] + vertex_count;
1457 p->indices.push_back(j);
1459 for(u32 i=0; i<numVertices; i++)
1461 p->vertices.push_back(vertices[i]);