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 it's CONTENT_IGNORE we can't do any light calculations
262 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));
284 light /= light_count;
286 // Boost brightness around light sources
287 if(decode_light(light_source_max) >= light)
288 //return decode_light(undiminish_light(light_source_max));
289 return decode_light(light_source_max);
291 if(ambient_occlusion > 4)
293 //ambient_occlusion -= 4;
294 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
295 float light_amount = (8 - ambient_occlusion) / 4.0;
296 float light_f = (float)light / 255.0;
297 light_f = pow(light_f, 2.2f); // gamma -> linear space
298 light_f = light_f * light_amount;
299 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
302 light = 255.0 * light_f + 0.5;
309 Calculate smooth lighting at the XYZ- corner of p.
312 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
314 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
315 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
316 return day | (night << 8);
320 Calculate smooth lighting at the given corner of p.
323 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
325 if(corner.X == 1) p.X += 1;
326 else assert(corner.X == -1);
327 if(corner.Y == 1) p.Y += 1;
328 else assert(corner.Y == -1);
329 if(corner.Z == 1) p.Z += 1;
330 else assert(corner.Z == -1);
332 return getSmoothLight(p, data);
336 Converts from day + night color values (0..255)
337 and a given daynight_ratio to the final SColor shown on screen.
339 static void finalColorBlend(video::SColor& result,
340 u8 day, u8 night, u32 daynight_ratio)
342 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
346 b += (day - night) / 13;
347 rg -= (day - night) / 23;
349 // Emphase blue a bit in darker places
350 // Each entry of this array represents a range of 8 blue levels
351 static u8 emphase_blue_when_dark[32] = {
352 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
353 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
359 b += emphase_blue_when_dark[b / 8];
361 // Artificial light is yellow-ish
362 static u8 emphase_yellow_when_artificial[16] = {
363 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
365 rg += emphase_yellow_when_artificial[night/16];
377 Mesh generation helpers
381 vertex_dirs: v3s16[4]
383 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
386 If looked from outside the node towards the face, the corners are:
392 if(dir == v3s16(0,0,1))
394 // If looking towards z+, this is the face that is behind
395 // the center point, facing towards z+.
396 vertex_dirs[0] = v3s16(-1,-1, 1);
397 vertex_dirs[1] = v3s16( 1,-1, 1);
398 vertex_dirs[2] = v3s16( 1, 1, 1);
399 vertex_dirs[3] = v3s16(-1, 1, 1);
401 else if(dir == v3s16(0,0,-1))
404 vertex_dirs[0] = v3s16( 1,-1,-1);
405 vertex_dirs[1] = v3s16(-1,-1,-1);
406 vertex_dirs[2] = v3s16(-1, 1,-1);
407 vertex_dirs[3] = v3s16( 1, 1,-1);
409 else if(dir == v3s16(1,0,0))
412 vertex_dirs[0] = v3s16( 1,-1, 1);
413 vertex_dirs[1] = v3s16( 1,-1,-1);
414 vertex_dirs[2] = v3s16( 1, 1,-1);
415 vertex_dirs[3] = v3s16( 1, 1, 1);
417 else if(dir == v3s16(-1,0,0))
420 vertex_dirs[0] = v3s16(-1,-1,-1);
421 vertex_dirs[1] = v3s16(-1,-1, 1);
422 vertex_dirs[2] = v3s16(-1, 1, 1);
423 vertex_dirs[3] = v3s16(-1, 1,-1);
425 else if(dir == v3s16(0,1,0))
427 // faces towards Y+ (assume Z- as "down" in texture)
428 vertex_dirs[0] = v3s16( 1, 1,-1);
429 vertex_dirs[1] = v3s16(-1, 1,-1);
430 vertex_dirs[2] = v3s16(-1, 1, 1);
431 vertex_dirs[3] = v3s16( 1, 1, 1);
433 else if(dir == v3s16(0,-1,0))
435 // faces towards Y- (assume Z+ as "down" in texture)
436 vertex_dirs[0] = v3s16( 1,-1, 1);
437 vertex_dirs[1] = v3s16(-1,-1, 1);
438 vertex_dirs[2] = v3s16(-1,-1,-1);
439 vertex_dirs[3] = v3s16( 1,-1,-1);
446 video::S3DVertex vertices[4]; // Precalculated vertices
449 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
450 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
454 // Position is at the center of the cube.
463 v3s16 vertex_dirs[4];
464 getNodeVertexDirs(dir, vertex_dirs);
468 switch (tile.rotation)
474 vertex_dirs[0] = vertex_dirs[3];
475 vertex_dirs[3] = vertex_dirs[2];
476 vertex_dirs[2] = vertex_dirs[1];
486 vertex_dirs[0] = vertex_dirs[2];
489 vertex_dirs[1] = vertex_dirs[3];
500 vertex_dirs[0] = vertex_dirs[1];
501 vertex_dirs[1] = vertex_dirs[2];
502 vertex_dirs[2] = vertex_dirs[3];
512 vertex_dirs[0] = vertex_dirs[3];
513 vertex_dirs[3] = vertex_dirs[2];
514 vertex_dirs[2] = vertex_dirs[1];
526 vertex_dirs[0] = vertex_dirs[1];
527 vertex_dirs[1] = vertex_dirs[2];
528 vertex_dirs[2] = vertex_dirs[3];
540 vertex_dirs[0] = vertex_dirs[3];
541 vertex_dirs[3] = vertex_dirs[2];
542 vertex_dirs[2] = vertex_dirs[1];
554 vertex_dirs[0] = vertex_dirs[1];
555 vertex_dirs[1] = vertex_dirs[2];
556 vertex_dirs[2] = vertex_dirs[3];
578 for(u16 i=0; i<4; i++)
581 BS/2*vertex_dirs[i].X,
582 BS/2*vertex_dirs[i].Y,
583 BS/2*vertex_dirs[i].Z
587 for(u16 i=0; i<4; i++)
589 vertex_pos[i].X *= scale.X;
590 vertex_pos[i].Y *= scale.Y;
591 vertex_pos[i].Z *= scale.Z;
592 vertex_pos[i] += pos;
596 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
597 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
598 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
600 v3f normal(dir.X, dir.Y, dir.Z);
602 u8 alpha = tile.alpha;
604 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
605 MapBlock_LightColor(alpha, li0, light_source),
606 core::vector2d<f32>(x0+w*abs_scale, y0+h));
607 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
608 MapBlock_LightColor(alpha, li1, light_source),
609 core::vector2d<f32>(x0, y0+h));
610 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
611 MapBlock_LightColor(alpha, li2, light_source),
612 core::vector2d<f32>(x0, y0));
613 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
614 MapBlock_LightColor(alpha, li3, light_source),
615 core::vector2d<f32>(x0+w*abs_scale, y0));
618 dest.push_back(face);
622 Nodes make a face if contents differ and solidness differs.
625 1: Face uses m1's content
626 2: Face uses m2's content
627 equivalent: Whether the blocks share the same face (eg. water and glass)
629 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
631 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
632 INodeDefManager *ndef)
636 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
639 bool contents_differ = (m1 != m2);
641 const ContentFeatures &f1 = ndef->get(m1);
642 const ContentFeatures &f2 = ndef->get(m2);
644 // Contents don't differ for different forms of same liquid
645 if(f1.sameLiquid(f2))
646 contents_differ = false;
648 u8 c1 = f1.solidness;
649 u8 c2 = f2.solidness;
651 bool solidness_differs = (c1 != c2);
652 bool makes_face = contents_differ && solidness_differs;
654 if(makes_face == false)
658 c1 = f1.visual_solidness;
660 c2 = f2.visual_solidness;
664 // If same solidness, liquid takes precense
678 Gets nth node tile (0 <= n <= 5).
680 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
682 INodeDefManager *ndef = data->m_gamedef->ndef();
683 TileSpec spec = ndef->get(mn).tiles[tileindex];
684 // Apply temporary crack
685 if(p == data->m_crack_pos_relative)
687 spec.material_flags |= MATERIAL_FLAG_CRACK;
693 Gets node tile given a face direction.
695 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
697 INodeDefManager *ndef = data->m_gamedef->ndef();
699 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
700 // (0,0,1), (0,0,-1) or (0,0,0)
701 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
703 // Convert direction to single integer for table lookup
708 // 4 = invalid, treat as (0,0,0)
712 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
714 // Get rotation for things like chests
715 u8 facedir = mn.getFaceDir(ndef);
718 static const u16 dir_to_tile[24 * 16] =
720 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
721 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
722 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
723 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
724 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
726 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
727 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
728 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
729 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
731 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
732 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
733 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
734 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
736 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
737 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
738 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
739 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
741 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
742 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
743 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
744 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
746 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
747 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
748 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
749 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
752 u16 tile_index=facedir*16 + dir_i;
753 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
754 spec.rotation=dir_to_tile[tile_index + 1];
755 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
759 static void getTileInfo(
767 v3s16 &face_dir_corrected,
773 VoxelManipulator &vmanip = data->m_vmanip;
774 INodeDefManager *ndef = data->m_gamedef->ndef();
775 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
777 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
779 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
780 if (n0.getContent() == CONTENT_IGNORE ) {
784 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
787 bool equivalent = false;
788 u8 mf = face_contents(n0.getContent(), n1.getContent(),
801 tile = getNodeTile(n0, p, face_dir, data);
803 face_dir_corrected = face_dir;
804 light_source = ndef->get(n0).light_source;
808 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
809 p_corrected = p + face_dir;
810 face_dir_corrected = -face_dir;
811 light_source = ndef->get(n1).light_source;
814 // eg. water and glass
816 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
818 if(data->m_smooth_lighting == false)
820 lights[0] = lights[1] = lights[2] = lights[3] =
821 getFaceLight(n0, n1, face_dir, ndef);
825 v3s16 vertex_dirs[4];
826 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
827 for(u16 i=0; i<4; i++)
829 lights[i] = getSmoothLight(
830 blockpos_nodes + p_corrected,
831 vertex_dirs[i], data);
840 translate_dir: unit vector with only one of x, y or z
841 face_dir: unit vector with only one of x, y or z
843 static void updateFastFaceRow(
850 std::vector<FastFace> &dest)
854 u16 continuous_tiles_count = 0;
856 bool makes_face = false;
858 v3s16 face_dir_corrected;
859 u16 lights[4] = {0,0,0,0};
862 getTileInfo(data, p, face_dir,
863 makes_face, p_corrected, face_dir_corrected,
864 lights, tile, light_source);
866 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
868 // If tiling can be done, this is set to false in the next step
869 bool next_is_different = true;
873 bool next_makes_face = false;
874 v3s16 next_p_corrected;
875 v3s16 next_face_dir_corrected;
876 u16 next_lights[4] = {0,0,0,0};
878 u8 next_light_source = 0;
880 // If at last position, there is nothing to compare to and
881 // the face must be drawn anyway
882 if(j != MAP_BLOCKSIZE - 1)
884 p_next = p + translate_dir;
886 getTileInfo(data, p_next, face_dir,
887 next_makes_face, next_p_corrected,
888 next_face_dir_corrected, next_lights,
889 next_tile, next_light_source);
891 if(next_makes_face == makes_face
892 && next_p_corrected == p_corrected + translate_dir
893 && next_face_dir_corrected == face_dir_corrected
894 && next_lights[0] == lights[0]
895 && next_lights[1] == lights[1]
896 && next_lights[2] == lights[2]
897 && next_lights[3] == lights[3]
899 && tile.rotation == 0
900 && next_light_source == light_source)
902 next_is_different = false;
906 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
907 next_makes_face != makes_face ? 1 : 0);
908 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
909 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
910 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
911 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
912 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
913 (next_lights[0] != lights[0] ||
914 next_lights[0] != lights[0] ||
915 next_lights[0] != lights[0] ||
916 next_lights[0] != lights[0]) ? 1 : 0);
917 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
918 !(next_tile == tile) ? 1 : 0);
921 /*g_profiler->add("Meshgen: Total faces checked", 1);
923 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
926 g_profiler->add("Meshgen: diff: last position", 1);*/
929 continuous_tiles_count++;
931 if(next_is_different)
934 Create a face if there should be one
938 // Floating point conversion of the position vector
939 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
940 // Center point of face (kind of)
941 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
942 if(continuous_tiles_count != 1)
943 sp += translate_dir_f;
946 if(translate_dir.X != 0)
948 scale.X = continuous_tiles_count;
950 if(translate_dir.Y != 0)
952 scale.Y = continuous_tiles_count;
954 if(translate_dir.Z != 0)
956 scale.Z = continuous_tiles_count;
959 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
960 sp, face_dir_corrected, scale, light_source,
963 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
964 for(int i=1; i<continuous_tiles_count; i++){
965 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
969 continuous_tiles_count = 0;
971 makes_face = next_makes_face;
972 p_corrected = next_p_corrected;
973 face_dir_corrected = next_face_dir_corrected;
974 lights[0] = next_lights[0];
975 lights[1] = next_lights[1];
976 lights[2] = next_lights[2];
977 lights[3] = next_lights[3];
979 light_source = next_light_source;
986 static void updateAllFastFaceRows(MeshMakeData *data,
987 std::vector<FastFace> &dest)
990 Go through every y,z and get top(y+) faces in rows of x+
992 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
993 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
994 updateFastFaceRow(data,
998 v3s16(0,1,0), //face dir
1005 Go through every x,y and get right(x+) faces in rows of z+
1007 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
1008 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1009 updateFastFaceRow(data,
1013 v3s16(1,0,0), //face dir
1020 Go through every y,z and get back(z+) faces in rows of x+
1022 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1023 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1024 updateFastFaceRow(data,
1028 v3s16(0,0,1), //face dir
1039 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1040 m_mesh(new scene::SMesh()),
1041 m_gamedef(data->m_gamedef),
1042 m_animation_force_timer(0), // force initial animation
1044 m_crack_materials(),
1045 m_last_daynight_ratio((u32) -1),
1048 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1049 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1050 //TimeTaker timer1("MapBlockMesh()");
1052 std::vector<FastFace> fastfaces_new;
1055 We are including the faces of the trailing edges of the block.
1056 This means that when something changes, the caller must
1057 also update the meshes of the blocks at the leading edges.
1059 NOTE: This is the slowest part of this method.
1062 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1063 //TimeTaker timer2("updateAllFastFaceRows()");
1064 updateAllFastFaceRows(data, fastfaces_new);
1069 Convert FastFaces to MeshCollector
1072 MeshCollector collector;
1075 // avg 0ms (100ms spikes when loading textures the first time)
1076 // (NOTE: probably outdated)
1077 //TimeTaker timer2("MeshCollector building");
1079 for(u32 i=0; i<fastfaces_new.size(); i++)
1081 FastFace &f = fastfaces_new[i];
1083 const u16 indices[] = {0,1,2,2,3,0};
1084 const u16 indices_alternate[] = {0,1,3,2,3,1};
1086 if(f.tile.texture == NULL)
1089 const u16 *indices_p = indices;
1092 Revert triangles for nicer looking gradient if vertices
1093 1 and 3 have same color or 0 and 2 have different color.
1094 getRed() is the day color.
1096 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1097 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1098 indices_p = indices_alternate;
1100 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1105 Add special graphics:
1112 mapblock_mesh_generate_special(data, collector);
1116 Convert MeshCollector to SMesh
1118 ITextureSource *tsrc = m_gamedef->tsrc();
1119 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1121 bool enable_shaders = g_settings->getBool("enable_shaders");
1122 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1123 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
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 // 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 // Add to MapBlockMesh in order to animate these tiles
1152 m_animation_tiles[i] = p.tile;
1153 m_animation_frames[i] = 0;
1154 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1155 // Get starting position from noise
1156 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1157 data->m_blockpos.X, data->m_blockpos.Y,
1158 data->m_blockpos.Z, 0));
1160 // Play all synchronized
1161 m_animation_frame_offsets[i] = 0;
1163 // Replace tile texture with the first animation frame
1164 std::ostringstream os(std::ios::binary);
1165 os<<tsrc->getTextureName(p.tile.texture_id);
1166 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1167 p.tile.texture = tsrc->getTexture(
1169 &p.tile.texture_id);
1171 // - Classic lighting (shaders handle this by themselves)
1174 for(u32 j = 0; j < p.vertices.size(); j++)
1176 video::SColor &vc = p.vertices[j].Color;
1177 // Set initial real color and store for later updates
1178 u8 day = vc.getRed();
1179 u8 night = vc.getGreen();
1180 finalColorBlend(vc, day, night, 1000);
1182 m_daynight_diffs[i][j] = std::make_pair(day, night);
1183 // Brighten topside (no shaders)
1184 if(p.vertices[j].Normal.Y > 0.5)
1186 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1187 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1188 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1194 video::SMaterial material;
1195 material.setFlag(video::EMF_LIGHTING, false);
1196 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1197 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1198 material.setFlag(video::EMF_FOG_ENABLE, true);
1199 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1200 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1201 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1202 material.setTexture(0, p.tile.texture);
1204 if (enable_shaders) {
1205 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1206 p.tile.applyMaterialOptionsWithShaders(material);
1207 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1208 if (enable_bumpmapping || enable_parallax_occlusion) {
1209 if (tsrc->isKnownSourceImage("override_normal.png")){
1210 material.setTexture(1, tsrc->getTexture("override_normal.png"));
1211 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1213 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();
1225 material.setTexture(1, tsrc->getTexture(fname_base));
1226 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1231 p.tile.applyMaterialOptions(material);
1233 // Create meshbuffer
1235 // This is a "Standard MeshBuffer",
1236 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1237 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1239 buf->Material = material;
1241 m_mesh->addMeshBuffer(buf);
1244 buf->append(&p.vertices[0], p.vertices.size(),
1245 &p.indices[0], p.indices.size());
1248 m_camera_offset = camera_offset;
1251 Do some stuff to the mesh
1254 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1259 // Usually 1-700 faces and 1-7 materials
1260 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1261 <<"and uses "<<m_mesh->getMeshBufferCount()
1262 <<" materials (meshbuffers)"<<std::endl;
1265 // Use VBO for mesh (this just would set this for ever buffer)
1266 // This will lead to infinite memory usage because or irrlicht.
1267 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1270 NOTE: If that is enabled, some kind of a queue to the main
1271 thread should be made which would call irrlicht to delete
1272 the hardware buffer and then delete the mesh
1276 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1278 // Check if animation is required for this mesh
1280 !m_crack_materials.empty() ||
1281 !m_daynight_diffs.empty() ||
1282 !m_animation_tiles.empty();
1285 MapBlockMesh::~MapBlockMesh()
1291 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1293 bool enable_shaders = g_settings->getBool("enable_shaders");
1294 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1295 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1297 if(!m_has_animation)
1299 m_animation_force_timer = 100000;
1303 m_animation_force_timer = myrand_range(5, 100);
1306 if(crack != m_last_crack)
1308 for(std::map<u32, std::string>::iterator
1309 i = m_crack_materials.begin();
1310 i != m_crack_materials.end(); i++)
1312 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1313 std::string basename = i->second;
1315 // Create new texture name from original
1316 ITextureSource *tsrc = m_gamedef->getTextureSource();
1317 std::ostringstream os;
1318 os<<basename<<crack;
1319 u32 new_texture_id = 0;
1320 video::ITexture *new_texture =
1321 tsrc->getTexture(os.str(), &new_texture_id);
1322 buf->getMaterial().setTexture(0, new_texture);
1324 // If the current material is also animated,
1325 // update animation info
1326 std::map<u32, TileSpec>::iterator anim_iter =
1327 m_animation_tiles.find(i->first);
1328 if(anim_iter != m_animation_tiles.end()){
1329 TileSpec &tile = anim_iter->second;
1330 tile.texture = new_texture;
1331 tile.texture_id = new_texture_id;
1332 // force animation update
1333 m_animation_frames[i->first] = -1;
1337 m_last_crack = crack;
1340 // Texture animation
1341 for(std::map<u32, TileSpec>::iterator
1342 i = m_animation_tiles.begin();
1343 i != m_animation_tiles.end(); i++)
1345 const TileSpec &tile = i->second;
1346 // Figure out current frame
1347 int frameoffset = m_animation_frame_offsets[i->first];
1348 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1349 + frameoffset) % tile.animation_frame_count;
1350 // If frame doesn't change, skip
1351 if(frame == m_animation_frames[i->first])
1354 m_animation_frames[i->first] = frame;
1356 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1357 ITextureSource *tsrc = m_gamedef->getTextureSource();
1358 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1360 // Create new texture name from original
1361 std::ostringstream os(std::ios::binary);
1362 os<<tsrc->getTextureName(tile.texture_id);
1363 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1365 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1366 if (enable_shaders){
1367 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1368 buf->getMaterial().MaterialType = shdrsrc->getShaderInfo(tile.shader_id).material;
1369 if (enable_bumpmapping || enable_parallax_occlusion){
1370 if (tsrc->isKnownSourceImage("override_normal.png")){
1371 buf->getMaterial().setTexture(1, tsrc->getTexture("override_normal.png"));
1372 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1374 std::string fname_base,fname_normal;
1375 fname_base = tsrc->getTextureName(tile.texture_id);
1377 pos = fname_base.find(".");
1378 fname_normal = fname_base.substr (0, pos);
1379 fname_normal += "_normal.png";
1380 if (tsrc->isKnownSourceImage(fname_normal)){
1382 os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1383 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
1384 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1391 // Day-night transition
1392 if(daynight_ratio != m_last_daynight_ratio)
1394 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1395 i = m_daynight_diffs.begin();
1396 i != m_daynight_diffs.end(); i++)
1398 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1399 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1400 for(std::map<u32, std::pair<u8, u8 > >::iterator
1401 j = i->second.begin();
1402 j != i->second.end(); j++)
1404 u32 vertexIndex = j->first;
1405 u8 day = j->second.first;
1406 u8 night = j->second.second;
1407 finalColorBlend(vertices[vertexIndex].Color,
1408 day, night, daynight_ratio);
1409 // Make sides and bottom darker than the top
1410 video::SColor &vc = vertices[vertexIndex].Color;
1411 if(vertices[vertexIndex].Normal.Y > 0.5) {
1412 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.2, 255.0));
1413 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.2, 255.0));
1414 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.2, 255.0));
1415 } else if (vertices[vertexIndex].Normal.Y < -0.5) {
1416 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.3, 255.0));
1417 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.3, 255.0));
1418 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.3, 255.0));
1419 } else if (vertices[vertexIndex].Normal.X > 0.5) {
1420 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.8, 255.0));
1421 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.8, 255.0));
1422 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.8, 255.0));
1423 } else if (vertices[vertexIndex].Normal.X < -0.5) {
1424 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.8, 255.0));
1425 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.8, 255.0));
1426 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.8, 255.0));
1427 } else if (vertices[vertexIndex].Normal.Z > 0.5) {
1428 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.5, 255.0));
1429 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.5, 255.0));
1430 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.5, 255.0));
1431 } else if (vertices[vertexIndex].Normal.Z < -0.5) {
1432 vc.setRed (srgb_linear_multiply(vc.getRed(), 0.5, 255.0));
1433 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 0.5, 255.0));
1434 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 0.5, 255.0));
1438 m_last_daynight_ratio = daynight_ratio;
1444 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1446 if (camera_offset != m_camera_offset) {
1447 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1448 m_camera_offset = camera_offset;
1456 void MeshCollector::append(const TileSpec &tile,
1457 const video::S3DVertex *vertices, u32 numVertices,
1458 const u16 *indices, u32 numIndices)
1460 if(numIndices > 65535)
1462 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1466 PreMeshBuffer *p = NULL;
1467 for(u32 i=0; i<prebuffers.size(); i++)
1469 PreMeshBuffer &pp = prebuffers[i];
1472 if(pp.indices.size() + numIndices > 65535)
1483 prebuffers.push_back(pp);
1484 p = &prebuffers[prebuffers.size()-1];
1487 u32 vertex_count = p->vertices.size();
1488 for(u32 i=0; i<numIndices; i++)
1490 u32 j = indices[i] + vertex_count;
1491 p->indices.push_back(j);
1493 for(u32 i=0; i<numVertices; i++)
1495 p->vertices.push_back(vertices[i]);