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);
714 assert(facedir <= 23);
715 static const u16 dir_to_tile[24 * 16] =
717 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
718 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
719 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
720 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
721 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
723 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
724 0,0, 4,3 , 2,0 , 0,3 , 0,0, 1,1 , 3,2 , 5,1 ,
725 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
726 0,0, 5,3 , 3,0 , 0,1 , 0,0, 1,3 , 2,2 , 4,1 ,
728 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
729 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
730 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
731 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
733 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
734 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
735 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
736 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
738 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
739 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
740 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
741 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
743 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
744 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
745 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
746 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
749 u16 tile_index=facedir*16 + dir_i;
750 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
751 spec.rotation=dir_to_tile[tile_index + 1];
752 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
756 static void getTileInfo(
764 v3s16 &face_dir_corrected,
770 VoxelManipulator &vmanip = data->m_vmanip;
771 INodeDefManager *ndef = data->m_gamedef->ndef();
772 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
774 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
775 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
776 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
777 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
780 bool equivalent = false;
781 u8 mf = face_contents(n0.getContent(), n1.getContent(),
796 face_dir_corrected = face_dir;
797 light_source = ndef->get(n0).light_source;
802 p_corrected = p + face_dir;
803 face_dir_corrected = -face_dir;
804 light_source = ndef->get(n1).light_source;
807 // eg. water and glass
809 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
811 if(data->m_smooth_lighting == false)
813 lights[0] = lights[1] = lights[2] = lights[3] =
814 getFaceLight(n0, n1, face_dir, data);
818 v3s16 vertex_dirs[4];
819 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
820 for(u16 i=0; i<4; i++)
822 lights[i] = getSmoothLight(
823 blockpos_nodes + p_corrected,
824 vertex_dirs[i], data);
833 translate_dir: unit vector with only one of x, y or z
834 face_dir: unit vector with only one of x, y or z
836 static void updateFastFaceRow(
843 std::vector<FastFace> &dest)
847 u16 continuous_tiles_count = 0;
849 bool makes_face = false;
851 v3s16 face_dir_corrected;
852 u16 lights[4] = {0,0,0,0};
855 getTileInfo(data, p, face_dir,
856 makes_face, p_corrected, face_dir_corrected,
857 lights, tile, light_source);
859 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
861 // If tiling can be done, this is set to false in the next step
862 bool next_is_different = true;
866 bool next_makes_face = false;
867 v3s16 next_p_corrected;
868 v3s16 next_face_dir_corrected;
869 u16 next_lights[4] = {0,0,0,0};
871 u8 next_light_source = 0;
873 // If at last position, there is nothing to compare to and
874 // the face must be drawn anyway
875 if(j != MAP_BLOCKSIZE - 1)
877 p_next = p + translate_dir;
879 getTileInfo(data, p_next, face_dir,
880 next_makes_face, next_p_corrected,
881 next_face_dir_corrected, next_lights,
882 next_tile, next_light_source);
884 if(next_makes_face == makes_face
885 && next_p_corrected == p_corrected + translate_dir
886 && next_face_dir_corrected == face_dir_corrected
887 && next_lights[0] == lights[0]
888 && next_lights[1] == lights[1]
889 && next_lights[2] == lights[2]
890 && next_lights[3] == lights[3]
892 && tile.rotation == 0
893 && next_light_source == light_source)
895 next_is_different = false;
899 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
900 next_makes_face != makes_face ? 1 : 0);
901 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
902 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
903 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
904 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
905 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
906 (next_lights[0] != lights[0] ||
907 next_lights[0] != lights[0] ||
908 next_lights[0] != lights[0] ||
909 next_lights[0] != lights[0]) ? 1 : 0);
910 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
911 !(next_tile == tile) ? 1 : 0);
914 /*g_profiler->add("Meshgen: Total faces checked", 1);
916 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
919 g_profiler->add("Meshgen: diff: last position", 1);*/
922 continuous_tiles_count++;
924 if(next_is_different)
927 Create a face if there should be one
931 // Floating point conversion of the position vector
932 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
933 // Center point of face (kind of)
934 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
935 if(continuous_tiles_count != 1)
936 sp += translate_dir_f;
939 if(translate_dir.X != 0)
941 scale.X = continuous_tiles_count;
943 if(translate_dir.Y != 0)
945 scale.Y = continuous_tiles_count;
947 if(translate_dir.Z != 0)
949 scale.Z = continuous_tiles_count;
952 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
953 sp, face_dir_corrected, scale, light_source,
956 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
957 for(int i=1; i<continuous_tiles_count; i++){
958 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
962 continuous_tiles_count = 0;
964 makes_face = next_makes_face;
965 p_corrected = next_p_corrected;
966 face_dir_corrected = next_face_dir_corrected;
967 lights[0] = next_lights[0];
968 lights[1] = next_lights[1];
969 lights[2] = next_lights[2];
970 lights[3] = next_lights[3];
972 light_source = next_light_source;
979 static void updateAllFastFaceRows(MeshMakeData *data,
980 std::vector<FastFace> &dest)
983 Go through every y,z and get top(y+) faces in rows of x+
985 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
986 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
987 updateFastFaceRow(data,
991 v3s16(0,1,0), //face dir
998 Go through every x,y and get right(x+) faces in rows of z+
1000 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
1001 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1002 updateFastFaceRow(data,
1006 v3s16(1,0,0), //face dir
1013 Go through every y,z and get back(z+) faces in rows of x+
1015 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1016 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1017 updateFastFaceRow(data,
1021 v3s16(0,0,1), //face dir
1032 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
1033 m_mesh(new scene::SMesh()),
1034 m_gamedef(data->m_gamedef),
1035 m_animation_force_timer(0), // force initial animation
1037 m_crack_materials(),
1038 m_last_daynight_ratio((u32) -1),
1041 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1042 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1043 //TimeTaker timer1("MapBlockMesh()");
1045 std::vector<FastFace> fastfaces_new;
1048 We are including the faces of the trailing edges of the block.
1049 This means that when something changes, the caller must
1050 also update the meshes of the blocks at the leading edges.
1052 NOTE: This is the slowest part of this method.
1055 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1056 //TimeTaker timer2("updateAllFastFaceRows()");
1057 updateAllFastFaceRows(data, fastfaces_new);
1062 Convert FastFaces to MeshCollector
1065 MeshCollector collector;
1068 // avg 0ms (100ms spikes when loading textures the first time)
1069 // (NOTE: probably outdated)
1070 //TimeTaker timer2("MeshCollector building");
1072 for(u32 i=0; i<fastfaces_new.size(); i++)
1074 FastFace &f = fastfaces_new[i];
1076 const u16 indices[] = {0,1,2,2,3,0};
1077 const u16 indices_alternate[] = {0,1,3,2,3,1};
1079 if(f.tile.texture == NULL)
1082 const u16 *indices_p = indices;
1085 Revert triangles for nicer looking gradient if vertices
1086 1 and 3 have same color or 0 and 2 have different color.
1087 getRed() is the day color.
1089 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1090 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1091 indices_p = indices_alternate;
1093 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1098 Add special graphics:
1105 mapblock_mesh_generate_special(data, collector);
1109 Convert MeshCollector to SMesh
1111 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1112 bool enable_shaders = g_settings->getBool("enable_shaders");
1113 video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
1114 getShader("test_shader_1").material;
1115 video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
1116 getShader("test_shader_2").material;
1117 video::E_MATERIAL_TYPE shadermat3 = m_gamedef->getShaderSource()->
1118 getShader("test_shader_3").material;
1119 video::E_MATERIAL_TYPE bumpmaps1 = m_gamedef->getShaderSource()->
1120 getShader("bumpmaps_solids").material;
1121 video::E_MATERIAL_TYPE bumpmaps2 = m_gamedef->getShaderSource()->
1122 getShader("bumpmaps_liquids").material;
1124 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1126 PreMeshBuffer &p = collector.prebuffers[i];
1127 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1128 <<", p.indices.size()="<<p.indices.size()
1131 // Generate animation data
1133 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1135 ITextureSource *tsrc = data->m_gamedef->tsrc();
1136 // Find the texture name plus ^[crack:N:
1137 std::ostringstream os(std::ios::binary);
1138 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1139 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1140 os<<"o"; // use ^[cracko
1141 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1142 m_crack_materials.insert(std::make_pair(i, os.str()));
1143 // Replace tile texture with the cracked one
1144 p.tile.texture = tsrc->getTexture(
1146 &p.tile.texture_id);
1148 // - Texture animation
1149 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1151 ITextureSource *tsrc = data->m_gamedef->tsrc();
1152 // Add to MapBlockMesh in order to animate these tiles
1153 m_animation_tiles[i] = p.tile;
1154 m_animation_frames[i] = 0;
1155 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1156 // Get starting position from noise
1157 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1158 data->m_blockpos.X, data->m_blockpos.Y,
1159 data->m_blockpos.Z, 0));
1161 // Play all synchronized
1162 m_animation_frame_offsets[i] = 0;
1164 // Replace tile texture with the first animation frame
1165 std::ostringstream os(std::ios::binary);
1166 os<<tsrc->getTextureName(p.tile.texture_id);
1167 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1168 p.tile.texture = tsrc->getTexture(
1170 &p.tile.texture_id);
1172 // - Classic lighting (shaders handle this by themselves)
1175 for(u32 j = 0; j < p.vertices.size(); j++)
1177 video::SColor &vc = p.vertices[j].Color;
1178 // Set initial real color and store for later updates
1179 u8 day = vc.getRed();
1180 u8 night = vc.getGreen();
1181 finalColorBlend(vc, day, night, 1000);
1183 m_daynight_diffs[i][j] = std::make_pair(day, night);
1184 // Brighten topside (no shaders)
1185 if(p.vertices[j].Normal.Y > 0.5)
1187 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1188 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1189 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1195 video::SMaterial material;
1196 material.setFlag(video::EMF_LIGHTING, false);
1197 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1198 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1199 material.setFlag(video::EMF_FOG_ENABLE, true);
1200 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1201 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1202 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1203 material.setTexture(0, p.tile.texture);
1205 if (enable_shaders) {
1206 video::E_MATERIAL_TYPE smat1 = shadermat1;
1207 video::E_MATERIAL_TYPE smat2 = shadermat2;
1208 video::E_MATERIAL_TYPE smat3 = shadermat3;
1210 if (enable_bumpmapping) {
1211 ITextureSource *tsrc = data->m_gamedef->tsrc();
1212 std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
1214 std::string normal_ext = "_normal.png";
1215 size_t pos = fname_base.find(".");
1216 std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1218 if (tsrc->isKnownSourceImage(fname_normal)) {
1219 // look for image extension and replace it
1221 while ((i = fname_base.find(".", i)) != std::string::npos) {
1222 fname_base.replace(i, 4, normal_ext);
1223 i += normal_ext.length();
1226 material.setTexture(1, tsrc->getTexture(fname_base));
1233 p.tile.applyMaterialOptionsWithShaders(material, smat1, smat2, smat3);
1235 p.tile.applyMaterialOptions(material);
1238 // Create meshbuffer
1240 // This is a "Standard MeshBuffer",
1241 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1242 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1244 buf->Material = material;
1246 m_mesh->addMeshBuffer(buf);
1249 buf->append(&p.vertices[0], p.vertices.size(),
1250 &p.indices[0], p.indices.size());
1254 Do some stuff to the mesh
1257 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1262 // Usually 1-700 faces and 1-7 materials
1263 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1264 <<"and uses "<<m_mesh->getMeshBufferCount()
1265 <<" materials (meshbuffers)"<<std::endl;
1268 // Use VBO for mesh (this just would set this for ever buffer)
1269 // This will lead to infinite memory usage because or irrlicht.
1270 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1273 NOTE: If that is enabled, some kind of a queue to the main
1274 thread should be made which would call irrlicht to delete
1275 the hardware buffer and then delete the mesh
1279 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1281 // Check if animation is required for this mesh
1283 !m_crack_materials.empty() ||
1284 !m_daynight_diffs.empty() ||
1285 !m_animation_tiles.empty();
1288 MapBlockMesh::~MapBlockMesh()
1294 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1296 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1297 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1299 if(!m_has_animation)
1301 m_animation_force_timer = 100000;
1305 m_animation_force_timer = myrand_range(5, 100);
1308 if(crack != m_last_crack)
1310 for(std::map<u32, std::string>::iterator
1311 i = m_crack_materials.begin();
1312 i != m_crack_materials.end(); i++)
1314 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1315 std::string basename = i->second;
1317 // Create new texture name from original
1318 ITextureSource *tsrc = m_gamedef->getTextureSource();
1319 std::ostringstream os;
1320 os<<basename<<crack;
1321 u32 new_texture_id = 0;
1322 video::ITexture *new_texture =
1323 tsrc->getTexture(os.str(), &new_texture_id);
1324 buf->getMaterial().setTexture(0, new_texture);
1326 // If the current material is also animated,
1327 // update animation info
1328 std::map<u32, TileSpec>::iterator anim_iter =
1329 m_animation_tiles.find(i->first);
1330 if(anim_iter != m_animation_tiles.end()){
1331 TileSpec &tile = anim_iter->second;
1332 tile.texture = new_texture;
1333 tile.texture_id = new_texture_id;
1334 // force animation update
1335 m_animation_frames[i->first] = -1;
1339 m_last_crack = crack;
1342 // Texture animation
1343 for(std::map<u32, TileSpec>::iterator
1344 i = m_animation_tiles.begin();
1345 i != m_animation_tiles.end(); i++)
1347 const TileSpec &tile = i->second;
1348 // Figure out current frame
1349 int frameoffset = m_animation_frame_offsets[i->first];
1350 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1351 + frameoffset) % tile.animation_frame_count;
1352 // If frame doesn't change, skip
1353 if(frame == m_animation_frames[i->first])
1356 m_animation_frames[i->first] = frame;
1358 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1359 ITextureSource *tsrc = m_gamedef->getTextureSource();
1361 // Create new texture name from original
1362 std::ostringstream os(std::ios::binary);
1363 os<<tsrc->getTextureName(tile.texture_id);
1364 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1366 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1367 if (enable_shaders && enable_bumpmapping)
1369 std::string basename,normal;
1370 basename = tsrc->getTextureName(tile.texture_id);
1372 pos = basename.find(".");
1373 normal = basename.substr (0, pos);
1374 normal += "_normal.png";
1376 os<<normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1377 if (tsrc->isKnownSourceImage(normal))
1378 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
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 // Brighten topside (no shaders)
1401 if(vertices[vertexIndex].Normal.Y > 0.5)
1403 video::SColor &vc = vertices[vertexIndex].Color;
1404 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1405 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1406 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1410 m_last_daynight_ratio = daynight_ratio;
1420 void MeshCollector::append(const TileSpec &tile,
1421 const video::S3DVertex *vertices, u32 numVertices,
1422 const u16 *indices, u32 numIndices)
1424 if(numIndices > 65535)
1426 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1430 PreMeshBuffer *p = NULL;
1431 for(u32 i=0; i<prebuffers.size(); i++)
1433 PreMeshBuffer &pp = prebuffers[i];
1436 if(pp.indices.size() + numIndices > 65535)
1447 prebuffers.push_back(pp);
1448 p = &prebuffers[prebuffers.size()-1];
1451 u32 vertex_count = p->vertices.size();
1452 for(u32 i=0; i<numIndices; i++)
1454 u32 j = indices[i] + vertex_count;
1455 p->indices.push_back(j);
1457 for(u32 i=0; i<numVertices; i++)
1459 p->vertices.push_back(vertices[i]);