3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "mapblock_mesh.h"
24 #include "main.h" // for g_profiler
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
35 float srgb_linear_multiply(float f, float m, float max)
37 f = f * f; // SRGB -> Linear
39 f = sqrt(f); // Linear -> SRGB
49 MeshMakeData::MeshMakeData(IGameDef *gamedef):
51 m_blockpos(-1337,-1337,-1337),
52 m_crack_pos_relative(-1337, -1337, -1337),
53 m_smooth_lighting(false),
57 void MeshMakeData::fill(MapBlock *block)
59 m_blockpos = block->getPos();
61 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
67 // Allocate this block + neighbors
69 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
70 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
73 //TimeTaker timer("copy central block data");
77 block->copyTo(m_vmanip);
80 //TimeTaker timer("copy neighbor block data");
84 Copy neighbors. This is lightning fast.
85 Copying only the borders would be *very* slow.
89 Map *map = block->getParent();
91 for(u16 i=0; i<26; i++)
93 const v3s16 &dir = g_26dirs[i];
94 v3s16 bp = m_blockpos + dir;
95 MapBlock *b = map->getBlockNoCreateNoEx(bp);
102 void MeshMakeData::fillSingleNode(MapNode *node)
104 m_blockpos = v3s16(0,0,0);
106 v3s16 blockpos_nodes = v3s16(0,0,0);
107 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
108 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
109 s32 volume = area.getVolume();
110 s32 our_node_index = area.index(1,1,1);
112 // Allocate this block + neighbors
114 m_vmanip.addArea(area);
117 MapNode *data = new MapNode[volume];
118 for(s32 i = 0; i < volume; i++)
120 if(i == our_node_index)
126 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
129 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
133 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
136 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
139 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
141 m_smooth_lighting = smooth_lighting;
145 Light and vertex color functions
149 Calculate non-smooth lighting at interior of node.
152 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
153 INodeDefManager *ndef)
155 u8 light = n.getLight(bank, ndef);
159 light = undiminish_light(light);
164 light = diminish_light(light);
168 return decode_light(light);
172 Calculate non-smooth lighting at interior of node.
175 u16 getInteriorLight(MapNode n, s32 increment, INodeDefManager *ndef)
177 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, ndef);
178 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, ndef);
179 return day | (night << 8);
183 Calculate non-smooth lighting at face of node.
186 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
187 v3s16 face_dir, INodeDefManager *ndef)
190 u8 l1 = n.getLight(bank, ndef);
191 u8 l2 = n2.getLight(bank, ndef);
197 // Boost light level for light sources
198 u8 light_source = MYMAX(ndef->get(n).light_source,
199 ndef->get(n2).light_source);
200 //if(light_source >= light)
201 //return decode_light(undiminish_light(light_source));
202 if(light_source > light)
203 //return decode_light(light_source);
204 light = light_source;
206 // Make some nice difference to different sides
208 // This makes light come from a corner
209 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
210 light = diminish_light(diminish_light(light));
211 else if(face_dir.X == -1 || face_dir.Z == -1)
212 light = diminish_light(light);*/
214 // All neighboring faces have different shade (like in minecraft)
215 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
216 light = diminish_light(diminish_light(light));
217 else if(face_dir.Z == 1 || face_dir.Z == -1)
218 light = diminish_light(light);
220 return decode_light(light);
224 Calculate non-smooth lighting at face of node.
227 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
229 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
230 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
231 return day | (night << 8);
235 Calculate smooth lighting at the XYZ- corner of p.
238 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
240 static v3s16 dirs8[8] = {
251 INodeDefManager *ndef = data->m_gamedef->ndef();
253 u16 ambient_occlusion = 0;
256 u8 light_source_max = 0;
257 for(u32 i=0; i<8; i++)
259 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
261 if (n.getContent() == CONTENT_IGNORE) {
266 const ContentFeatures &f = ndef->get(n);
267 if(f.light_source > light_source_max)
268 light_source_max = f.light_source;
269 // Check f.solidness because fast-style leaves look
271 if(f.param_type == CPT_LIGHT && f.solidness != 2)
273 light += decode_light(n.getLight(bank, ndef));
281 light /= light_count;
283 // Boost brightness around light sources
284 if(decode_light(light_source_max) >= light)
285 //return decode_light(undiminish_light(light_source_max));
286 return decode_light(light_source_max);
288 if(ambient_occlusion > 4)
290 //ambient_occlusion -= 4;
291 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
292 float light_amount = (8 - ambient_occlusion) / 4.0;
293 float light_f = (float)light / 255.0;
294 light_f = pow(light_f, 2.2f); // gamma -> linear space
295 light_f = light_f * light_amount;
296 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
299 light = 255.0 * light_f + 0.5;
306 Calculate smooth lighting at the XYZ- corner of p.
309 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
311 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
312 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
313 return day | (night << 8);
317 Calculate smooth lighting at the given corner of p.
320 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
322 if(corner.X == 1) p.X += 1;
323 else assert(corner.X == -1);
324 if(corner.Y == 1) p.Y += 1;
325 else assert(corner.Y == -1);
326 if(corner.Z == 1) p.Z += 1;
327 else assert(corner.Z == -1);
329 return getSmoothLight(p, data);
333 Converts from day + night color values (0..255)
334 and a given daynight_ratio to the final SColor shown on screen.
336 static void finalColorBlend(video::SColor& result,
337 u8 day, u8 night, u32 daynight_ratio)
339 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
343 b += (day - night) / 13;
344 rg -= (day - night) / 23;
346 // Emphase blue a bit in darker places
347 // Each entry of this array represents a range of 8 blue levels
348 static u8 emphase_blue_when_dark[32] = {
349 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
350 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
356 b += emphase_blue_when_dark[b / 8];
358 // Artificial light is yellow-ish
359 static u8 emphase_yellow_when_artificial[16] = {
360 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
362 rg += emphase_yellow_when_artificial[night/16];
374 Mesh generation helpers
378 vertex_dirs: v3s16[4]
380 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
383 If looked from outside the node towards the face, the corners are:
389 if(dir == v3s16(0,0,1))
391 // If looking towards z+, this is the face that is behind
392 // the center point, facing towards z+.
393 vertex_dirs[0] = v3s16(-1,-1, 1);
394 vertex_dirs[1] = v3s16( 1,-1, 1);
395 vertex_dirs[2] = v3s16( 1, 1, 1);
396 vertex_dirs[3] = v3s16(-1, 1, 1);
398 else if(dir == v3s16(0,0,-1))
401 vertex_dirs[0] = v3s16( 1,-1,-1);
402 vertex_dirs[1] = v3s16(-1,-1,-1);
403 vertex_dirs[2] = v3s16(-1, 1,-1);
404 vertex_dirs[3] = v3s16( 1, 1,-1);
406 else if(dir == v3s16(1,0,0))
409 vertex_dirs[0] = v3s16( 1,-1, 1);
410 vertex_dirs[1] = v3s16( 1,-1,-1);
411 vertex_dirs[2] = v3s16( 1, 1,-1);
412 vertex_dirs[3] = v3s16( 1, 1, 1);
414 else if(dir == v3s16(-1,0,0))
417 vertex_dirs[0] = v3s16(-1,-1,-1);
418 vertex_dirs[1] = v3s16(-1,-1, 1);
419 vertex_dirs[2] = v3s16(-1, 1, 1);
420 vertex_dirs[3] = v3s16(-1, 1,-1);
422 else if(dir == v3s16(0,1,0))
424 // faces towards Y+ (assume Z- as "down" in texture)
425 vertex_dirs[0] = v3s16( 1, 1,-1);
426 vertex_dirs[1] = v3s16(-1, 1,-1);
427 vertex_dirs[2] = v3s16(-1, 1, 1);
428 vertex_dirs[3] = v3s16( 1, 1, 1);
430 else if(dir == v3s16(0,-1,0))
432 // faces towards Y- (assume Z+ as "down" in texture)
433 vertex_dirs[0] = v3s16( 1,-1, 1);
434 vertex_dirs[1] = v3s16(-1,-1, 1);
435 vertex_dirs[2] = v3s16(-1,-1,-1);
436 vertex_dirs[3] = v3s16( 1,-1,-1);
443 video::S3DVertex vertices[4]; // Precalculated vertices
446 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
447 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
451 // Position is at the center of the cube.
460 v3s16 vertex_dirs[4];
461 getNodeVertexDirs(dir, vertex_dirs);
465 switch (tile.rotation)
471 vertex_dirs[0] = vertex_dirs[3];
472 vertex_dirs[3] = vertex_dirs[2];
473 vertex_dirs[2] = vertex_dirs[1];
483 vertex_dirs[0] = vertex_dirs[2];
486 vertex_dirs[1] = vertex_dirs[3];
497 vertex_dirs[0] = vertex_dirs[1];
498 vertex_dirs[1] = vertex_dirs[2];
499 vertex_dirs[2] = vertex_dirs[3];
509 vertex_dirs[0] = vertex_dirs[3];
510 vertex_dirs[3] = vertex_dirs[2];
511 vertex_dirs[2] = vertex_dirs[1];
523 vertex_dirs[0] = vertex_dirs[1];
524 vertex_dirs[1] = vertex_dirs[2];
525 vertex_dirs[2] = vertex_dirs[3];
537 vertex_dirs[0] = vertex_dirs[3];
538 vertex_dirs[3] = vertex_dirs[2];
539 vertex_dirs[2] = vertex_dirs[1];
551 vertex_dirs[0] = vertex_dirs[1];
552 vertex_dirs[1] = vertex_dirs[2];
553 vertex_dirs[2] = vertex_dirs[3];
575 for(u16 i=0; i<4; i++)
578 BS/2*vertex_dirs[i].X,
579 BS/2*vertex_dirs[i].Y,
580 BS/2*vertex_dirs[i].Z
584 for(u16 i=0; i<4; i++)
586 vertex_pos[i].X *= scale.X;
587 vertex_pos[i].Y *= scale.Y;
588 vertex_pos[i].Z *= scale.Z;
589 vertex_pos[i] += pos;
593 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
594 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
595 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
597 v3f normal(dir.X, dir.Y, dir.Z);
599 u8 alpha = tile.alpha;
601 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
602 MapBlock_LightColor(alpha, li0, light_source),
603 core::vector2d<f32>(x0+w*abs_scale, y0+h));
604 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
605 MapBlock_LightColor(alpha, li1, light_source),
606 core::vector2d<f32>(x0, y0+h));
607 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
608 MapBlock_LightColor(alpha, li2, light_source),
609 core::vector2d<f32>(x0, y0));
610 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
611 MapBlock_LightColor(alpha, li3, light_source),
612 core::vector2d<f32>(x0+w*abs_scale, y0));
615 dest.push_back(face);
619 Nodes make a face if contents differ and solidness differs.
622 1: Face uses m1's content
623 2: Face uses m2's content
624 equivalent: Whether the blocks share the same face (eg. water and glass)
626 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
628 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
629 INodeDefManager *ndef)
633 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
636 bool contents_differ = (m1 != m2);
638 const ContentFeatures &f1 = ndef->get(m1);
639 const ContentFeatures &f2 = ndef->get(m2);
641 // Contents don't differ for different forms of same liquid
642 if(f1.sameLiquid(f2))
643 contents_differ = false;
645 u8 c1 = f1.solidness;
646 u8 c2 = f2.solidness;
648 bool solidness_differs = (c1 != c2);
649 bool makes_face = contents_differ && solidness_differs;
651 if(makes_face == false)
655 c1 = f1.visual_solidness;
657 c2 = f2.visual_solidness;
661 // If same solidness, liquid takes precense
675 Gets nth node tile (0 <= n <= 5).
677 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
679 INodeDefManager *ndef = data->m_gamedef->ndef();
680 TileSpec spec = ndef->get(mn).tiles[tileindex];
681 // Apply temporary crack
682 if(p == data->m_crack_pos_relative)
684 spec.material_flags |= MATERIAL_FLAG_CRACK;
690 Gets node tile given a face direction.
692 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
694 INodeDefManager *ndef = data->m_gamedef->ndef();
696 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
697 // (0,0,1), (0,0,-1) or (0,0,0)
698 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
700 // Convert direction to single integer for table lookup
705 // 4 = invalid, treat as (0,0,0)
709 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
711 // Get rotation for things like chests
712 u8 facedir = mn.getFaceDir(ndef);
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,1 , 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,3 , 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);
776 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
777 if (n0.getContent() == CONTENT_IGNORE ) {
781 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
784 bool equivalent = false;
785 u8 mf = face_contents(n0.getContent(), n1.getContent(),
798 tile = getNodeTile(n0, p, face_dir, data);
800 face_dir_corrected = face_dir;
801 light_source = ndef->get(n0).light_source;
805 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
806 p_corrected = p + face_dir;
807 face_dir_corrected = -face_dir;
808 light_source = ndef->get(n1).light_source;
811 // eg. water and glass
813 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
815 if(data->m_smooth_lighting == false)
817 lights[0] = lights[1] = lights[2] = lights[3] =
818 getFaceLight(n0, n1, face_dir, ndef);
822 v3s16 vertex_dirs[4];
823 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
824 for(u16 i=0; i<4; i++)
826 lights[i] = getSmoothLight(
827 blockpos_nodes + p_corrected,
828 vertex_dirs[i], data);
837 translate_dir: unit vector with only one of x, y or z
838 face_dir: unit vector with only one of x, y or z
840 static void updateFastFaceRow(
847 std::vector<FastFace> &dest)
851 u16 continuous_tiles_count = 0;
853 bool makes_face = false;
855 v3s16 face_dir_corrected;
856 u16 lights[4] = {0,0,0,0};
859 getTileInfo(data, p, face_dir,
860 makes_face, p_corrected, face_dir_corrected,
861 lights, tile, light_source);
863 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
865 // If tiling can be done, this is set to false in the next step
866 bool next_is_different = true;
870 bool next_makes_face = false;
871 v3s16 next_p_corrected;
872 v3s16 next_face_dir_corrected;
873 u16 next_lights[4] = {0,0,0,0};
875 u8 next_light_source = 0;
877 // If at last position, there is nothing to compare to and
878 // the face must be drawn anyway
879 if(j != MAP_BLOCKSIZE - 1)
881 p_next = p + translate_dir;
883 getTileInfo(data, p_next, face_dir,
884 next_makes_face, next_p_corrected,
885 next_face_dir_corrected, next_lights,
886 next_tile, next_light_source);
888 if(next_makes_face == makes_face
889 && next_p_corrected == p_corrected + translate_dir
890 && next_face_dir_corrected == face_dir_corrected
891 && next_lights[0] == lights[0]
892 && next_lights[1] == lights[1]
893 && next_lights[2] == lights[2]
894 && next_lights[3] == lights[3]
896 && tile.rotation == 0
897 && next_light_source == light_source)
899 next_is_different = false;
903 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
904 next_makes_face != makes_face ? 1 : 0);
905 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
906 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
907 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
908 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
909 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
910 (next_lights[0] != lights[0] ||
911 next_lights[0] != lights[0] ||
912 next_lights[0] != lights[0] ||
913 next_lights[0] != lights[0]) ? 1 : 0);
914 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
915 !(next_tile == tile) ? 1 : 0);
918 /*g_profiler->add("Meshgen: Total faces checked", 1);
920 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
923 g_profiler->add("Meshgen: diff: last position", 1);*/
926 continuous_tiles_count++;
928 if(next_is_different)
931 Create a face if there should be one
935 // Floating point conversion of the position vector
936 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
937 // Center point of face (kind of)
938 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
939 if(continuous_tiles_count != 1)
940 sp += translate_dir_f;
943 if(translate_dir.X != 0)
945 scale.X = continuous_tiles_count;
947 if(translate_dir.Y != 0)
949 scale.Y = continuous_tiles_count;
951 if(translate_dir.Z != 0)
953 scale.Z = continuous_tiles_count;
956 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
957 sp, face_dir_corrected, scale, light_source,
960 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
961 for(int i=1; i<continuous_tiles_count; i++){
962 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
966 continuous_tiles_count = 0;
968 makes_face = next_makes_face;
969 p_corrected = next_p_corrected;
970 face_dir_corrected = next_face_dir_corrected;
971 lights[0] = next_lights[0];
972 lights[1] = next_lights[1];
973 lights[2] = next_lights[2];
974 lights[3] = next_lights[3];
976 light_source = next_light_source;
983 static void updateAllFastFaceRows(MeshMakeData *data,
984 std::vector<FastFace> &dest)
987 Go through every y,z and get top(y+) faces in rows of x+
989 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
990 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
991 updateFastFaceRow(data,
995 v3s16(0,1,0), //face dir
1002 Go through every x,y and get right(x+) faces in rows of z+
1004 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
1005 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1006 updateFastFaceRow(data,
1010 v3s16(1,0,0), //face dir
1017 Go through every y,z and get back(z+) faces in rows of x+
1019 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1020 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1021 updateFastFaceRow(data,
1025 v3s16(0,0,1), //face dir
1036 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1037 m_mesh(new scene::SMesh()),
1038 m_gamedef(data->m_gamedef),
1039 m_animation_force_timer(0), // force initial animation
1041 m_crack_materials(),
1042 m_last_daynight_ratio((u32) -1),
1045 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1046 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1047 //TimeTaker timer1("MapBlockMesh()");
1049 std::vector<FastFace> fastfaces_new;
1052 We are including the faces of the trailing edges of the block.
1053 This means that when something changes, the caller must
1054 also update the meshes of the blocks at the leading edges.
1056 NOTE: This is the slowest part of this method.
1059 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1060 //TimeTaker timer2("updateAllFastFaceRows()");
1061 updateAllFastFaceRows(data, fastfaces_new);
1066 Convert FastFaces to MeshCollector
1069 MeshCollector collector;
1072 // avg 0ms (100ms spikes when loading textures the first time)
1073 // (NOTE: probably outdated)
1074 //TimeTaker timer2("MeshCollector building");
1076 for(u32 i=0; i<fastfaces_new.size(); i++)
1078 FastFace &f = fastfaces_new[i];
1080 const u16 indices[] = {0,1,2,2,3,0};
1081 const u16 indices_alternate[] = {0,1,3,2,3,1};
1083 if(f.tile.texture == NULL)
1086 const u16 *indices_p = indices;
1089 Revert triangles for nicer looking gradient if vertices
1090 1 and 3 have same color or 0 and 2 have different color.
1091 getRed() is the day color.
1093 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1094 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1095 indices_p = indices_alternate;
1097 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1102 Add special graphics:
1109 mapblock_mesh_generate_special(data, collector);
1113 Convert MeshCollector to SMesh
1115 ITextureSource *tsrc = m_gamedef->tsrc();
1116 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1118 bool enable_shaders = g_settings->getBool("enable_shaders");
1119 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1120 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1122 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1124 PreMeshBuffer &p = collector.prebuffers[i];
1125 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1126 <<", p.indices.size()="<<p.indices.size()
1129 // Generate animation data
1131 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1133 // Find the texture name plus ^[crack:N:
1134 std::ostringstream os(std::ios::binary);
1135 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1136 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1137 os<<"o"; // use ^[cracko
1138 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1139 m_crack_materials.insert(std::make_pair(i, os.str()));
1140 // Replace tile texture with the cracked one
1141 p.tile.texture = tsrc->getTexture(
1143 &p.tile.texture_id);
1145 // - Texture animation
1146 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1148 // Add to MapBlockMesh in order to animate these tiles
1149 m_animation_tiles[i] = p.tile;
1150 m_animation_frames[i] = 0;
1151 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1152 // Get starting position from noise
1153 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1154 data->m_blockpos.X, data->m_blockpos.Y,
1155 data->m_blockpos.Z, 0));
1157 // Play all synchronized
1158 m_animation_frame_offsets[i] = 0;
1160 // Replace tile texture with the first animation frame
1161 std::ostringstream os(std::ios::binary);
1162 os<<tsrc->getTextureName(p.tile.texture_id);
1163 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1164 p.tile.texture = tsrc->getTexture(
1166 &p.tile.texture_id);
1168 // - Classic lighting (shaders handle this by themselves)
1171 for(u32 j = 0; j < p.vertices.size(); j++)
1173 video::SColor &vc = p.vertices[j].Color;
1174 // Set initial real color and store for later updates
1175 u8 day = vc.getRed();
1176 u8 night = vc.getGreen();
1177 finalColorBlend(vc, day, night, 1000);
1179 m_daynight_diffs[i][j] = std::make_pair(day, night);
1180 // Brighten topside (no shaders)
1181 if(p.vertices[j].Normal.Y > 0.5)
1183 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1184 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1185 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1191 video::SMaterial material;
1192 material.setFlag(video::EMF_LIGHTING, false);
1193 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1194 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1195 material.setFlag(video::EMF_FOG_ENABLE, true);
1196 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1197 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1198 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1199 material.setTexture(0, p.tile.texture);
1201 if (enable_shaders) {
1202 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1203 p.tile.applyMaterialOptionsWithShaders(material);
1204 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1205 if (enable_bumpmapping || enable_parallax_occlusion) {
1206 if (tsrc->isKnownSourceImage("override_normal.png")){
1207 material.setTexture(1, tsrc->getTexture("override_normal.png"));
1208 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1210 std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
1211 std::string normal_ext = "_normal.png";
1212 size_t pos = fname_base.find(".");
1213 std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1215 if (tsrc->isKnownSourceImage(fname_normal)) {
1216 // look for image extension and replace it
1218 while ((i = fname_base.find(".", i)) != std::string::npos) {
1219 fname_base.replace(i, 4, normal_ext);
1220 i += normal_ext.length();
1222 material.setTexture(1, tsrc->getTexture(fname_base));
1223 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1228 p.tile.applyMaterialOptions(material);
1230 // Create meshbuffer
1232 // This is a "Standard MeshBuffer",
1233 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1234 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1236 buf->Material = material;
1238 m_mesh->addMeshBuffer(buf);
1241 buf->append(&p.vertices[0], p.vertices.size(),
1242 &p.indices[0], p.indices.size());
1245 m_camera_offset = camera_offset;
1248 Do some stuff to the mesh
1251 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1256 // Usually 1-700 faces and 1-7 materials
1257 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1258 <<"and uses "<<m_mesh->getMeshBufferCount()
1259 <<" materials (meshbuffers)"<<std::endl;
1262 // Use VBO for mesh (this just would set this for ever buffer)
1263 // This will lead to infinite memory usage because or irrlicht.
1264 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1267 NOTE: If that is enabled, some kind of a queue to the main
1268 thread should be made which would call irrlicht to delete
1269 the hardware buffer and then delete the mesh
1273 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1275 // Check if animation is required for this mesh
1277 !m_crack_materials.empty() ||
1278 !m_daynight_diffs.empty() ||
1279 !m_animation_tiles.empty();
1282 MapBlockMesh::~MapBlockMesh()
1288 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1290 bool enable_shaders = g_settings->getBool("enable_shaders");
1291 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1292 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1294 if(!m_has_animation)
1296 m_animation_force_timer = 100000;
1300 m_animation_force_timer = myrand_range(5, 100);
1303 if(crack != m_last_crack)
1305 for(std::map<u32, std::string>::iterator
1306 i = m_crack_materials.begin();
1307 i != m_crack_materials.end(); i++)
1309 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1310 std::string basename = i->second;
1312 // Create new texture name from original
1313 ITextureSource *tsrc = m_gamedef->getTextureSource();
1314 std::ostringstream os;
1315 os<<basename<<crack;
1316 u32 new_texture_id = 0;
1317 video::ITexture *new_texture =
1318 tsrc->getTexture(os.str(), &new_texture_id);
1319 buf->getMaterial().setTexture(0, new_texture);
1321 // If the current material is also animated,
1322 // update animation info
1323 std::map<u32, TileSpec>::iterator anim_iter =
1324 m_animation_tiles.find(i->first);
1325 if(anim_iter != m_animation_tiles.end()){
1326 TileSpec &tile = anim_iter->second;
1327 tile.texture = new_texture;
1328 tile.texture_id = new_texture_id;
1329 // force animation update
1330 m_animation_frames[i->first] = -1;
1334 m_last_crack = crack;
1337 // Texture animation
1338 for(std::map<u32, TileSpec>::iterator
1339 i = m_animation_tiles.begin();
1340 i != m_animation_tiles.end(); i++)
1342 const TileSpec &tile = i->second;
1343 // Figure out current frame
1344 int frameoffset = m_animation_frame_offsets[i->first];
1345 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1346 + frameoffset) % tile.animation_frame_count;
1347 // If frame doesn't change, skip
1348 if(frame == m_animation_frames[i->first])
1351 m_animation_frames[i->first] = frame;
1353 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1354 ITextureSource *tsrc = m_gamedef->getTextureSource();
1355 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1357 // Create new texture name from original
1358 std::ostringstream os(std::ios::binary);
1359 os<<tsrc->getTextureName(tile.texture_id);
1360 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1362 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1363 if (enable_shaders){
1364 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1365 buf->getMaterial().MaterialType = shdrsrc->getShaderInfo(tile.shader_id).material;
1366 if (enable_bumpmapping || enable_parallax_occlusion){
1367 if (tsrc->isKnownSourceImage("override_normal.png")){
1368 buf->getMaterial().setTexture(1, tsrc->getTexture("override_normal.png"));
1369 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1371 std::string fname_base,fname_normal;
1372 fname_base = tsrc->getTextureName(tile.texture_id);
1374 pos = fname_base.find(".");
1375 fname_normal = fname_base.substr (0, pos);
1376 fname_normal += "_normal.png";
1377 if (tsrc->isKnownSourceImage(fname_normal)){
1379 os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1380 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
1381 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1388 // Day-night transition
1389 if(daynight_ratio != m_last_daynight_ratio)
1391 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1392 i = m_daynight_diffs.begin();
1393 i != m_daynight_diffs.end(); i++)
1395 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1396 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1397 for(std::map<u32, std::pair<u8, u8 > >::iterator
1398 j = i->second.begin();
1399 j != i->second.end(); j++)
1401 u32 vertexIndex = j->first;
1402 u8 day = j->second.first;
1403 u8 night = j->second.second;
1404 finalColorBlend(vertices[vertexIndex].Color,
1405 day, night, daynight_ratio);
1406 // Make sides and bottom darker than the top
1407 video::SColor &vc = vertices[vertexIndex].Color;
1408 if(vertices[vertexIndex].Normal.Y > 0.5) {
1409 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.2, 255.0));
1410 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.2, 255.0));
1411 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.2, 255.0));
1412 } else if (vertices[vertexIndex].Normal.Y < -0.5) {
1413 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.3, 255.0));
1414 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.3, 255.0));
1415 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.3, 255.0));
1416 } else if (vertices[vertexIndex].Normal.X > 0.5) {
1417 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.8, 255.0));
1418 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.8, 255.0));
1419 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.8, 255.0));
1420 } else if (vertices[vertexIndex].Normal.X < -0.5) {
1421 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.8, 255.0));
1422 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.8, 255.0));
1423 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.8, 255.0));
1424 } else if (vertices[vertexIndex].Normal.Z > 0.5) {
1425 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.5, 255.0));
1426 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.5, 255.0));
1427 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.5, 255.0));
1428 } else if (vertices[vertexIndex].Normal.Z < -0.5) {
1429 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.5, 255.0));
1430 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.5, 255.0));
1431 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.5, 255.0));
1435 m_last_daynight_ratio = daynight_ratio;
1441 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1443 if (camera_offset != m_camera_offset) {
1444 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1445 m_camera_offset = camera_offset;
1453 void MeshCollector::append(const TileSpec &tile,
1454 const video::S3DVertex *vertices, u32 numVertices,
1455 const u16 *indices, u32 numIndices)
1457 if(numIndices > 65535)
1459 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1463 PreMeshBuffer *p = NULL;
1464 for(u32 i=0; i<prebuffers.size(); i++)
1466 PreMeshBuffer &pp = prebuffers[i];
1469 if(pp.indices.size() + numIndices > 65535)
1480 prebuffers.push_back(pp);
1481 p = &prebuffers[prebuffers.size()-1];
1484 u32 vertex_count = p->vertices.size();
1485 for(u32 i=0; i<numIndices; i++)
1487 u32 j = indices[i] + vertex_count;
1488 p->indices.push_back(j);
1490 for(u32 i=0; i<numVertices; i++)
1492 p->vertices.push_back(vertices[i]);