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_bumpmapping = g_settings->getBool("enable_bumpmapping");
1113 bool enable_shaders = g_settings->getBool("enable_shaders");
1114 video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
1115 getShader("test_shader_1").material;
1116 video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
1117 getShader("test_shader_2").material;
1118 video::E_MATERIAL_TYPE shadermat3 = m_gamedef->getShaderSource()->
1119 getShader("test_shader_3").material;
1120 video::E_MATERIAL_TYPE bumpmaps1 = m_gamedef->getShaderSource()->
1121 getShader("bumpmaps_solids").material;
1122 video::E_MATERIAL_TYPE bumpmaps2 = m_gamedef->getShaderSource()->
1123 getShader("bumpmaps_liquids").material;
1125 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1127 PreMeshBuffer &p = collector.prebuffers[i];
1128 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1129 <<", p.indices.size()="<<p.indices.size()
1132 // Generate animation data
1134 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1136 ITextureSource *tsrc = data->m_gamedef->tsrc();
1137 // Find the texture name plus ^[crack:N:
1138 std::ostringstream os(std::ios::binary);
1139 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1140 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1141 os<<"o"; // use ^[cracko
1142 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1143 m_crack_materials.insert(std::make_pair(i, os.str()));
1144 // Replace tile texture with the cracked one
1145 p.tile.texture = tsrc->getTexture(
1147 &p.tile.texture_id);
1149 // - Texture animation
1150 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1152 ITextureSource *tsrc = data->m_gamedef->tsrc();
1153 // Add to MapBlockMesh in order to animate these tiles
1154 m_animation_tiles[i] = p.tile;
1155 m_animation_frames[i] = 0;
1156 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1157 // Get starting position from noise
1158 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1159 data->m_blockpos.X, data->m_blockpos.Y,
1160 data->m_blockpos.Z, 0));
1162 // Play all synchronized
1163 m_animation_frame_offsets[i] = 0;
1165 // Replace tile texture with the first animation frame
1166 std::ostringstream os(std::ios::binary);
1167 os<<tsrc->getTextureName(p.tile.texture_id);
1168 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1169 p.tile.texture = tsrc->getTexture(
1171 &p.tile.texture_id);
1173 // - Classic lighting (shaders handle this by themselves)
1176 for(u32 j = 0; j < p.vertices.size(); j++)
1178 video::SColor &vc = p.vertices[j].Color;
1179 // Set initial real color and store for later updates
1180 u8 day = vc.getRed();
1181 u8 night = vc.getGreen();
1182 finalColorBlend(vc, day, night, 1000);
1184 m_daynight_diffs[i][j] = std::make_pair(day, night);
1185 // Brighten topside (no shaders)
1186 if(p.vertices[j].Normal.Y > 0.5)
1188 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1189 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1190 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1196 video::SMaterial material;
1197 material.setFlag(video::EMF_LIGHTING, false);
1198 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1199 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1200 material.setFlag(video::EMF_FOG_ENABLE, true);
1201 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1202 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1203 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1204 material.setTexture(0, p.tile.texture);
1206 if (enable_shaders) {
1207 video::E_MATERIAL_TYPE smat1 = shadermat1;
1208 video::E_MATERIAL_TYPE smat2 = shadermat2;
1209 video::E_MATERIAL_TYPE smat3 = shadermat3;
1211 if (enable_bumpmapping) {
1212 ITextureSource *tsrc = data->m_gamedef->tsrc();
1213 std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
1215 std::string normal_ext = "_normal.png";
1216 size_t pos = fname_base.find(".");
1217 std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1219 if (tsrc->isKnownSourceImage(fname_normal)) {
1220 // look for image extension and replace it
1222 while ((i = fname_base.find(".", i)) != std::string::npos) {
1223 fname_base.replace(i, 4, normal_ext);
1224 i += normal_ext.length();
1227 material.setTexture(1, tsrc->getTexture(fname_base));
1234 p.tile.applyMaterialOptionsWithShaders(material, smat1, smat2, smat3);
1236 p.tile.applyMaterialOptions(material);
1239 // Create meshbuffer
1241 // This is a "Standard MeshBuffer",
1242 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1243 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1245 buf->Material = material;
1247 m_mesh->addMeshBuffer(buf);
1250 buf->append(&p.vertices[0], p.vertices.size(),
1251 &p.indices[0], p.indices.size());
1255 Do some stuff to the mesh
1258 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1263 // Usually 1-700 faces and 1-7 materials
1264 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1265 <<"and uses "<<m_mesh->getMeshBufferCount()
1266 <<" materials (meshbuffers)"<<std::endl;
1269 // Use VBO for mesh (this just would set this for ever buffer)
1270 // This will lead to infinite memory usage because or irrlicht.
1271 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1274 NOTE: If that is enabled, some kind of a queue to the main
1275 thread should be made which would call irrlicht to delete
1276 the hardware buffer and then delete the mesh
1280 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1282 // Check if animation is required for this mesh
1284 !m_crack_materials.empty() ||
1285 !m_daynight_diffs.empty() ||
1286 !m_animation_tiles.empty();
1289 MapBlockMesh::~MapBlockMesh()
1295 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1297 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1298 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1300 if(!m_has_animation)
1302 m_animation_force_timer = 100000;
1306 m_animation_force_timer = myrand_range(5, 100);
1309 if(crack != m_last_crack)
1311 for(std::map<u32, std::string>::iterator
1312 i = m_crack_materials.begin();
1313 i != m_crack_materials.end(); i++)
1315 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1316 std::string basename = i->second;
1318 // Create new texture name from original
1319 ITextureSource *tsrc = m_gamedef->getTextureSource();
1320 std::ostringstream os;
1321 os<<basename<<crack;
1322 u32 new_texture_id = 0;
1323 video::ITexture *new_texture =
1324 tsrc->getTexture(os.str(), &new_texture_id);
1325 buf->getMaterial().setTexture(0, new_texture);
1327 // If the current material is also animated,
1328 // update animation info
1329 std::map<u32, TileSpec>::iterator anim_iter =
1330 m_animation_tiles.find(i->first);
1331 if(anim_iter != m_animation_tiles.end()){
1332 TileSpec &tile = anim_iter->second;
1333 tile.texture = new_texture;
1334 tile.texture_id = new_texture_id;
1335 // force animation update
1336 m_animation_frames[i->first] = -1;
1340 m_last_crack = crack;
1343 // Texture animation
1344 for(std::map<u32, TileSpec>::iterator
1345 i = m_animation_tiles.begin();
1346 i != m_animation_tiles.end(); i++)
1348 const TileSpec &tile = i->second;
1349 // Figure out current frame
1350 int frameoffset = m_animation_frame_offsets[i->first];
1351 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1352 + frameoffset) % tile.animation_frame_count;
1353 // If frame doesn't change, skip
1354 if(frame == m_animation_frames[i->first])
1357 m_animation_frames[i->first] = frame;
1359 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1360 ITextureSource *tsrc = m_gamedef->getTextureSource();
1362 // Create new texture name from original
1363 std::ostringstream os(std::ios::binary);
1364 os<<tsrc->getTextureName(tile.texture_id);
1365 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1367 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1368 if (enable_shaders && enable_bumpmapping)
1370 std::string basename,normal;
1371 basename = tsrc->getTextureName(tile.texture_id);
1373 pos = basename.find(".");
1374 normal = basename.substr (0, pos);
1375 normal += "_normal.png";
1377 os<<normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1378 if (tsrc->isKnownSourceImage(normal))
1379 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
1383 // Day-night transition
1384 if(daynight_ratio != m_last_daynight_ratio)
1386 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1387 i = m_daynight_diffs.begin();
1388 i != m_daynight_diffs.end(); i++)
1390 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1391 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1392 for(std::map<u32, std::pair<u8, u8 > >::iterator
1393 j = i->second.begin();
1394 j != i->second.end(); j++)
1396 u32 vertexIndex = j->first;
1397 u8 day = j->second.first;
1398 u8 night = j->second.second;
1399 finalColorBlend(vertices[vertexIndex].Color,
1400 day, night, daynight_ratio);
1401 // Brighten topside (no shaders)
1402 if(vertices[vertexIndex].Normal.Y > 0.5)
1404 video::SColor &vc = vertices[vertexIndex].Color;
1405 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1406 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1407 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1411 m_last_daynight_ratio = daynight_ratio;
1421 void MeshCollector::append(const TileSpec &tile,
1422 const video::S3DVertex *vertices, u32 numVertices,
1423 const u16 *indices, u32 numIndices)
1425 if(numIndices > 65535)
1427 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1431 PreMeshBuffer *p = NULL;
1432 for(u32 i=0; i<prebuffers.size(); i++)
1434 PreMeshBuffer &pp = prebuffers[i];
1437 if(pp.indices.size() + numIndices > 65535)
1448 prebuffers.push_back(pp);
1449 p = &prebuffers[prebuffers.size()-1];
1452 u32 vertex_count = p->vertices.size();
1453 for(u32 i=0; i<numIndices; i++)
1455 u32 j = indices[i] + vertex_count;
1456 p->indices.push_back(j);
1458 for(u32 i=0; i<numVertices; i++)
1460 p->vertices.push_back(vertices[i]);