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 void applyContrast(video::SColor& color, float Factor)
37 float r = color.getRed();
38 float g = color.getGreen();
39 float b = color.getBlue();
40 color.setRed(irr::core::clamp((int)sqrt(r * r * Factor), 0, 255));
41 color.setGreen(irr::core::clamp((int)sqrt(g * g * Factor), 0, 255));
42 color.setBlue(irr::core::clamp((int)sqrt(b * b * Factor), 0, 255));
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 return decode_light(light);
210 Calculate non-smooth lighting at face of node.
213 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, INodeDefManager *ndef)
215 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, ndef);
216 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, ndef);
217 return day | (night << 8);
221 Calculate smooth lighting at the XYZ- corner of p.
224 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
226 static v3s16 dirs8[8] = {
237 INodeDefManager *ndef = data->m_gamedef->ndef();
239 u16 ambient_occlusion = 0;
242 u8 light_source_max = 0;
243 for(u32 i=0; i<8; i++)
245 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
247 // if it's CONTENT_IGNORE we can't do any light calculations
248 if (n.getContent() == CONTENT_IGNORE) {
252 const ContentFeatures &f = ndef->get(n);
253 if(f.light_source > light_source_max)
254 light_source_max = f.light_source;
255 // Check f.solidness because fast-style leaves look
257 if(f.param_type == CPT_LIGHT && f.solidness != 2)
259 light += decode_light(n.getLight(bank, ndef));
270 light /= light_count;
272 // Boost brightness around light sources
273 if(decode_light(light_source_max) >= light)
274 //return decode_light(undiminish_light(light_source_max));
275 return decode_light(light_source_max);
277 if(ambient_occlusion > 4)
279 //ambient_occlusion -= 4;
280 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
281 float light_amount = (8 - ambient_occlusion) / 4.0;
282 float light_f = (float)light / 255.0;
283 light_f = pow(light_f, 2.2f); // gamma -> linear space
284 light_f = light_f * light_amount;
285 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
288 light = 255.0 * light_f + 0.5;
295 Calculate smooth lighting at the XYZ- corner of p.
298 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
300 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
301 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
302 return day | (night << 8);
306 Calculate smooth lighting at the given corner of p.
309 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
311 if(corner.X == 1) p.X += 1;
312 else assert(corner.X == -1);
313 if(corner.Y == 1) p.Y += 1;
314 else assert(corner.Y == -1);
315 if(corner.Z == 1) p.Z += 1;
316 else assert(corner.Z == -1);
318 return getSmoothLight(p, data);
322 Converts from day + night color values (0..255)
323 and a given daynight_ratio to the final SColor shown on screen.
325 static void finalColorBlend(video::SColor& result,
326 u8 day, u8 night, u32 daynight_ratio)
328 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
332 b += (day - night) / 13;
333 rg -= (day - night) / 23;
335 // Emphase blue a bit in darker places
336 // Each entry of this array represents a range of 8 blue levels
337 static u8 emphase_blue_when_dark[32] = {
338 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
339 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
341 b += emphase_blue_when_dark[b / 8];
342 b = irr::core::clamp (b, 0, 255);
344 // Artificial light is yellow-ish
345 static u8 emphase_yellow_when_artificial[16] = {
346 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
348 rg += emphase_yellow_when_artificial[night/16];
349 rg = irr::core::clamp (rg, 0, 255);
357 Mesh generation helpers
361 vertex_dirs: v3s16[4]
363 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
366 If looked from outside the node towards the face, the corners are:
372 if(dir == v3s16(0,0,1))
374 // If looking towards z+, this is the face that is behind
375 // the center point, facing towards z+.
376 vertex_dirs[0] = v3s16(-1,-1, 1);
377 vertex_dirs[1] = v3s16( 1,-1, 1);
378 vertex_dirs[2] = v3s16( 1, 1, 1);
379 vertex_dirs[3] = v3s16(-1, 1, 1);
381 else if(dir == v3s16(0,0,-1))
384 vertex_dirs[0] = v3s16( 1,-1,-1);
385 vertex_dirs[1] = v3s16(-1,-1,-1);
386 vertex_dirs[2] = v3s16(-1, 1,-1);
387 vertex_dirs[3] = v3s16( 1, 1,-1);
389 else if(dir == v3s16(1,0,0))
392 vertex_dirs[0] = v3s16( 1,-1, 1);
393 vertex_dirs[1] = v3s16( 1,-1,-1);
394 vertex_dirs[2] = v3s16( 1, 1,-1);
395 vertex_dirs[3] = v3s16( 1, 1, 1);
397 else if(dir == v3s16(-1,0,0))
400 vertex_dirs[0] = v3s16(-1,-1,-1);
401 vertex_dirs[1] = v3s16(-1,-1, 1);
402 vertex_dirs[2] = v3s16(-1, 1, 1);
403 vertex_dirs[3] = v3s16(-1, 1,-1);
405 else if(dir == v3s16(0,1,0))
407 // faces towards Y+ (assume Z- as "down" in texture)
408 vertex_dirs[0] = v3s16( 1, 1,-1);
409 vertex_dirs[1] = v3s16(-1, 1,-1);
410 vertex_dirs[2] = v3s16(-1, 1, 1);
411 vertex_dirs[3] = v3s16( 1, 1, 1);
413 else if(dir == v3s16(0,-1,0))
415 // faces towards Y- (assume Z+ as "down" in texture)
416 vertex_dirs[0] = v3s16( 1,-1, 1);
417 vertex_dirs[1] = v3s16(-1,-1, 1);
418 vertex_dirs[2] = v3s16(-1,-1,-1);
419 vertex_dirs[3] = v3s16( 1,-1,-1);
426 video::S3DVertex vertices[4]; // Precalculated vertices
429 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
430 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
434 // Position is at the center of the cube.
443 v3s16 vertex_dirs[4];
444 getNodeVertexDirs(dir, vertex_dirs);
448 switch (tile.rotation)
454 vertex_dirs[0] = vertex_dirs[3];
455 vertex_dirs[3] = vertex_dirs[2];
456 vertex_dirs[2] = vertex_dirs[1];
466 vertex_dirs[0] = vertex_dirs[2];
469 vertex_dirs[1] = vertex_dirs[3];
480 vertex_dirs[0] = vertex_dirs[1];
481 vertex_dirs[1] = vertex_dirs[2];
482 vertex_dirs[2] = vertex_dirs[3];
492 vertex_dirs[0] = vertex_dirs[3];
493 vertex_dirs[3] = vertex_dirs[2];
494 vertex_dirs[2] = vertex_dirs[1];
506 vertex_dirs[0] = vertex_dirs[1];
507 vertex_dirs[1] = vertex_dirs[2];
508 vertex_dirs[2] = vertex_dirs[3];
520 vertex_dirs[0] = vertex_dirs[3];
521 vertex_dirs[3] = vertex_dirs[2];
522 vertex_dirs[2] = vertex_dirs[1];
534 vertex_dirs[0] = vertex_dirs[1];
535 vertex_dirs[1] = vertex_dirs[2];
536 vertex_dirs[2] = vertex_dirs[3];
558 for(u16 i=0; i<4; i++)
561 BS/2*vertex_dirs[i].X,
562 BS/2*vertex_dirs[i].Y,
563 BS/2*vertex_dirs[i].Z
567 for(u16 i=0; i<4; i++)
569 vertex_pos[i].X *= scale.X;
570 vertex_pos[i].Y *= scale.Y;
571 vertex_pos[i].Z *= scale.Z;
572 vertex_pos[i] += pos;
576 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
577 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
578 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
580 v3f normal(dir.X, dir.Y, dir.Z);
582 u8 alpha = tile.alpha;
584 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
585 MapBlock_LightColor(alpha, li0, light_source),
586 core::vector2d<f32>(x0+w*abs_scale, y0+h));
587 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
588 MapBlock_LightColor(alpha, li1, light_source),
589 core::vector2d<f32>(x0, y0+h));
590 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
591 MapBlock_LightColor(alpha, li2, light_source),
592 core::vector2d<f32>(x0, y0));
593 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
594 MapBlock_LightColor(alpha, li3, light_source),
595 core::vector2d<f32>(x0+w*abs_scale, y0));
598 dest.push_back(face);
602 Nodes make a face if contents differ and solidness differs.
605 1: Face uses m1's content
606 2: Face uses m2's content
607 equivalent: Whether the blocks share the same face (eg. water and glass)
609 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
611 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
612 INodeDefManager *ndef)
616 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
619 bool contents_differ = (m1 != m2);
621 const ContentFeatures &f1 = ndef->get(m1);
622 const ContentFeatures &f2 = ndef->get(m2);
624 // Contents don't differ for different forms of same liquid
625 if(f1.sameLiquid(f2))
626 contents_differ = false;
628 u8 c1 = f1.solidness;
629 u8 c2 = f2.solidness;
631 bool solidness_differs = (c1 != c2);
632 bool makes_face = contents_differ && solidness_differs;
634 if(makes_face == false)
638 c1 = f1.visual_solidness;
640 c2 = f2.visual_solidness;
644 // If same solidness, liquid takes precense
658 Gets nth node tile (0 <= n <= 5).
660 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
662 INodeDefManager *ndef = data->m_gamedef->ndef();
663 TileSpec spec = ndef->get(mn).tiles[tileindex];
664 // Apply temporary crack
665 if(p == data->m_crack_pos_relative)
667 spec.material_flags |= MATERIAL_FLAG_CRACK;
673 Gets node tile given a face direction.
675 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
677 INodeDefManager *ndef = data->m_gamedef->ndef();
679 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
680 // (0,0,1), (0,0,-1) or (0,0,0)
681 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
683 // Convert direction to single integer for table lookup
688 // 4 = invalid, treat as (0,0,0)
692 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
694 // Get rotation for things like chests
695 u8 facedir = mn.getFaceDir(ndef);
698 static const u16 dir_to_tile[24 * 16] =
700 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
701 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
702 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
703 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
704 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
706 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
707 0,0, 4,3 , 2,0 , 0,1 , 0,0, 1,1 , 3,2 , 5,1 ,
708 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
709 0,0, 5,3 , 3,0 , 0,3 , 0,0, 1,3 , 2,2 , 4,1 ,
711 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
712 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
713 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
714 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
716 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
717 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
718 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
719 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
721 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
722 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
723 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
724 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
726 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
727 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
728 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
729 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
732 u16 tile_index=facedir*16 + dir_i;
733 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
734 spec.rotation=dir_to_tile[tile_index + 1];
735 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
739 static void getTileInfo(
747 v3s16 &face_dir_corrected,
753 VoxelManipulator &vmanip = data->m_vmanip;
754 INodeDefManager *ndef = data->m_gamedef->ndef();
755 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
757 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
759 // Don't even try to get n1 if n0 is already CONTENT_IGNORE
760 if (n0.getContent() == CONTENT_IGNORE ) {
764 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
767 bool equivalent = false;
768 u8 mf = face_contents(n0.getContent(), n1.getContent(),
781 tile = getNodeTile(n0, p, face_dir, data);
783 face_dir_corrected = face_dir;
784 light_source = ndef->get(n0).light_source;
788 tile = getNodeTile(n1, p + face_dir, -face_dir, data);
789 p_corrected = p + face_dir;
790 face_dir_corrected = -face_dir;
791 light_source = ndef->get(n1).light_source;
794 // eg. water and glass
796 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
798 if(data->m_smooth_lighting == false)
800 lights[0] = lights[1] = lights[2] = lights[3] =
801 getFaceLight(n0, n1, face_dir, ndef);
805 v3s16 vertex_dirs[4];
806 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
807 for(u16 i=0; i<4; i++)
809 lights[i] = getSmoothLight(
810 blockpos_nodes + p_corrected,
811 vertex_dirs[i], data);
820 translate_dir: unit vector with only one of x, y or z
821 face_dir: unit vector with only one of x, y or z
823 static void updateFastFaceRow(
830 std::vector<FastFace> &dest)
834 u16 continuous_tiles_count = 0;
836 bool makes_face = false;
838 v3s16 face_dir_corrected;
839 u16 lights[4] = {0,0,0,0};
842 getTileInfo(data, p, face_dir,
843 makes_face, p_corrected, face_dir_corrected,
844 lights, tile, light_source);
846 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
848 // If tiling can be done, this is set to false in the next step
849 bool next_is_different = true;
853 bool next_makes_face = false;
854 v3s16 next_p_corrected;
855 v3s16 next_face_dir_corrected;
856 u16 next_lights[4] = {0,0,0,0};
858 u8 next_light_source = 0;
860 // If at last position, there is nothing to compare to and
861 // the face must be drawn anyway
862 if(j != MAP_BLOCKSIZE - 1)
864 p_next = p + translate_dir;
866 getTileInfo(data, p_next, face_dir,
867 next_makes_face, next_p_corrected,
868 next_face_dir_corrected, next_lights,
869 next_tile, next_light_source);
871 if(next_makes_face == makes_face
872 && next_p_corrected == p_corrected + translate_dir
873 && next_face_dir_corrected == face_dir_corrected
874 && next_lights[0] == lights[0]
875 && next_lights[1] == lights[1]
876 && next_lights[2] == lights[2]
877 && next_lights[3] == lights[3]
879 && tile.rotation == 0
880 && next_light_source == light_source)
882 next_is_different = false;
886 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
887 next_makes_face != makes_face ? 1 : 0);
888 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
889 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
890 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
891 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
892 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
893 (next_lights[0] != lights[0] ||
894 next_lights[0] != lights[0] ||
895 next_lights[0] != lights[0] ||
896 next_lights[0] != lights[0]) ? 1 : 0);
897 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
898 !(next_tile == tile) ? 1 : 0);
901 /*g_profiler->add("Meshgen: Total faces checked", 1);
903 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
906 g_profiler->add("Meshgen: diff: last position", 1);*/
909 continuous_tiles_count++;
911 if(next_is_different)
914 Create a face if there should be one
918 // Floating point conversion of the position vector
919 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
920 // Center point of face (kind of)
921 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
922 if(continuous_tiles_count != 1)
923 sp += translate_dir_f;
926 if(translate_dir.X != 0)
928 scale.X = continuous_tiles_count;
930 if(translate_dir.Y != 0)
932 scale.Y = continuous_tiles_count;
934 if(translate_dir.Z != 0)
936 scale.Z = continuous_tiles_count;
939 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
940 sp, face_dir_corrected, scale, light_source,
943 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
944 for(int i=1; i<continuous_tiles_count; i++){
945 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
949 continuous_tiles_count = 0;
951 makes_face = next_makes_face;
952 p_corrected = next_p_corrected;
953 face_dir_corrected = next_face_dir_corrected;
954 lights[0] = next_lights[0];
955 lights[1] = next_lights[1];
956 lights[2] = next_lights[2];
957 lights[3] = next_lights[3];
959 light_source = next_light_source;
966 static void updateAllFastFaceRows(MeshMakeData *data,
967 std::vector<FastFace> &dest)
970 Go through every y,z and get top(y+) faces in rows of x+
972 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
973 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
974 updateFastFaceRow(data,
978 v3s16(0,1,0), //face dir
985 Go through every x,y and get right(x+) faces in rows of z+
987 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
988 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
989 updateFastFaceRow(data,
993 v3s16(1,0,0), //face dir
1000 Go through every y,z and get back(z+) faces in rows of x+
1002 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
1003 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
1004 updateFastFaceRow(data,
1008 v3s16(0,0,1), //face dir
1019 MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
1020 m_mesh(new scene::SMesh()),
1021 m_gamedef(data->m_gamedef),
1022 m_animation_force_timer(0), // force initial animation
1024 m_crack_materials(),
1025 m_last_daynight_ratio((u32) -1),
1028 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1029 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1030 //TimeTaker timer1("MapBlockMesh()");
1032 std::vector<FastFace> fastfaces_new;
1035 We are including the faces of the trailing edges of the block.
1036 This means that when something changes, the caller must
1037 also update the meshes of the blocks at the leading edges.
1039 NOTE: This is the slowest part of this method.
1042 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1043 //TimeTaker timer2("updateAllFastFaceRows()");
1044 updateAllFastFaceRows(data, fastfaces_new);
1049 Convert FastFaces to MeshCollector
1052 MeshCollector collector;
1055 // avg 0ms (100ms spikes when loading textures the first time)
1056 // (NOTE: probably outdated)
1057 //TimeTaker timer2("MeshCollector building");
1059 for(u32 i=0; i<fastfaces_new.size(); i++)
1061 FastFace &f = fastfaces_new[i];
1063 const u16 indices[] = {0,1,2,2,3,0};
1064 const u16 indices_alternate[] = {0,1,3,2,3,1};
1066 if(f.tile.texture == NULL)
1069 const u16 *indices_p = indices;
1072 Revert triangles for nicer looking gradient if vertices
1073 1 and 3 have same color or 0 and 2 have different color.
1074 getRed() is the day color.
1076 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1077 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1078 indices_p = indices_alternate;
1080 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1085 Add special graphics:
1092 mapblock_mesh_generate_special(data, collector);
1096 Convert MeshCollector to SMesh
1098 ITextureSource *tsrc = m_gamedef->tsrc();
1099 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1101 bool enable_shaders = g_settings->getBool("enable_shaders");
1102 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1103 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1105 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1107 PreMeshBuffer &p = collector.prebuffers[i];
1108 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1109 <<", p.indices.size()="<<p.indices.size()
1112 // Generate animation data
1114 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1116 // Find the texture name plus ^[crack:N:
1117 std::ostringstream os(std::ios::binary);
1118 os<<tsrc->getTextureName(p.tile.texture_id)<<"^[crack";
1119 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1120 os<<"o"; // use ^[cracko
1121 os<<":"<<(u32)p.tile.animation_frame_count<<":";
1122 m_crack_materials.insert(std::make_pair(i, os.str()));
1123 // Replace tile texture with the cracked one
1124 p.tile.texture = tsrc->getTexture(
1126 &p.tile.texture_id);
1128 // - Texture animation
1129 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1131 // Add to MapBlockMesh in order to animate these tiles
1132 m_animation_tiles[i] = p.tile;
1133 m_animation_frames[i] = 0;
1134 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1135 // Get starting position from noise
1136 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1137 data->m_blockpos.X, data->m_blockpos.Y,
1138 data->m_blockpos.Z, 0));
1140 // Play all synchronized
1141 m_animation_frame_offsets[i] = 0;
1143 // Replace tile texture with the first animation frame
1144 std::ostringstream os(std::ios::binary);
1145 os<<tsrc->getTextureName(p.tile.texture_id);
1146 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1147 p.tile.texture = tsrc->getTexture(
1149 &p.tile.texture_id);
1152 for(u32 j = 0; j < p.vertices.size(); j++)
1154 video::SColor &vc = p.vertices[j].Color;
1155 if(p.vertices[j].Normal.Y > 0.5) {
1156 applyContrast (vc, 1.2);
1157 } else if (p.vertices[j].Normal.Y < -0.5) {
1158 applyContrast (vc, 0.3);
1159 } else if (p.vertices[j].Normal.X > 0.5) {
1160 applyContrast (vc, 0.5);
1161 } else if (p.vertices[j].Normal.X < -0.5) {
1162 applyContrast (vc, 0.5);
1163 } else if (p.vertices[j].Normal.Z > 0.5) {
1164 applyContrast (vc, 0.8);
1165 } else if (p.vertices[j].Normal.Z < -0.5) {
1166 applyContrast (vc, 0.8);
1170 // - Classic lighting (shaders handle this by themselves)
1171 // Set initial real color and store for later updates
1172 u8 day = vc.getRed();
1173 u8 night = vc.getGreen();
1174 finalColorBlend(vc, day, night, 1000);
1176 m_daynight_diffs[i][j] = std::make_pair(day, night);
1181 video::SMaterial material;
1182 material.setFlag(video::EMF_LIGHTING, false);
1183 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1184 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1185 material.setFlag(video::EMF_FOG_ENABLE, true);
1186 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1187 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1188 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1189 material.setTexture(0, p.tile.texture);
1191 if (enable_shaders) {
1192 material.MaterialType = shdrsrc->getShaderInfo(p.tile.shader_id).material;
1193 p.tile.applyMaterialOptionsWithShaders(material);
1194 material.setTexture(2, tsrc->getTexture("disable_img.png"));
1195 if (enable_bumpmapping || enable_parallax_occlusion) {
1196 if (tsrc->isKnownSourceImage("override_normal.png")){
1197 material.setTexture(1, tsrc->getTexture("override_normal.png"));
1198 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1200 std::string fname_base = tsrc->getTextureName(p.tile.texture_id);
1201 std::string normal_ext = "_normal.png";
1202 size_t pos = fname_base.find(".");
1203 std::string fname_normal = fname_base.substr(0, pos) + normal_ext;
1205 if (tsrc->isKnownSourceImage(fname_normal)) {
1206 // look for image extension and replace it
1208 while ((i = fname_base.find(".", i)) != std::string::npos) {
1209 fname_base.replace(i, 4, normal_ext);
1210 i += normal_ext.length();
1212 material.setTexture(1, tsrc->getTexture(fname_base));
1213 material.setTexture(2, tsrc->getTexture("enable_img.png"));
1218 p.tile.applyMaterialOptions(material);
1220 // Create meshbuffer
1222 // This is a "Standard MeshBuffer",
1223 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1224 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1226 buf->Material = material;
1228 m_mesh->addMeshBuffer(buf);
1231 buf->append(&p.vertices[0], p.vertices.size(),
1232 &p.indices[0], p.indices.size());
1235 m_camera_offset = camera_offset;
1238 Do some stuff to the mesh
1241 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE - camera_offset, BS));
1246 // Usually 1-700 faces and 1-7 materials
1247 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1248 <<"and uses "<<m_mesh->getMeshBufferCount()
1249 <<" materials (meshbuffers)"<<std::endl;
1252 // Use VBO for mesh (this just would set this for ever buffer)
1253 // This will lead to infinite memory usage because or irrlicht.
1254 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1257 NOTE: If that is enabled, some kind of a queue to the main
1258 thread should be made which would call irrlicht to delete
1259 the hardware buffer and then delete the mesh
1263 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1265 // Check if animation is required for this mesh
1267 !m_crack_materials.empty() ||
1268 !m_daynight_diffs.empty() ||
1269 !m_animation_tiles.empty();
1272 MapBlockMesh::~MapBlockMesh()
1278 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1280 bool enable_shaders = g_settings->getBool("enable_shaders");
1281 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1282 bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion");
1284 if(!m_has_animation)
1286 m_animation_force_timer = 100000;
1290 m_animation_force_timer = myrand_range(5, 100);
1293 if(crack != m_last_crack)
1295 for(std::map<u32, std::string>::iterator
1296 i = m_crack_materials.begin();
1297 i != m_crack_materials.end(); i++)
1299 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1300 std::string basename = i->second;
1302 // Create new texture name from original
1303 ITextureSource *tsrc = m_gamedef->getTextureSource();
1304 std::ostringstream os;
1305 os<<basename<<crack;
1306 u32 new_texture_id = 0;
1307 video::ITexture *new_texture =
1308 tsrc->getTexture(os.str(), &new_texture_id);
1309 buf->getMaterial().setTexture(0, new_texture);
1311 // If the current material is also animated,
1312 // update animation info
1313 std::map<u32, TileSpec>::iterator anim_iter =
1314 m_animation_tiles.find(i->first);
1315 if(anim_iter != m_animation_tiles.end()){
1316 TileSpec &tile = anim_iter->second;
1317 tile.texture = new_texture;
1318 tile.texture_id = new_texture_id;
1319 // force animation update
1320 m_animation_frames[i->first] = -1;
1324 m_last_crack = crack;
1327 // Texture animation
1328 for(std::map<u32, TileSpec>::iterator
1329 i = m_animation_tiles.begin();
1330 i != m_animation_tiles.end(); i++)
1332 const TileSpec &tile = i->second;
1333 // Figure out current frame
1334 int frameoffset = m_animation_frame_offsets[i->first];
1335 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1336 + frameoffset) % tile.animation_frame_count;
1337 // If frame doesn't change, skip
1338 if(frame == m_animation_frames[i->first])
1341 m_animation_frames[i->first] = frame;
1343 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1344 ITextureSource *tsrc = m_gamedef->getTextureSource();
1345 IShaderSource *shdrsrc = m_gamedef->getShaderSource();
1347 // Create new texture name from original
1348 std::ostringstream os(std::ios::binary);
1349 os<<tsrc->getTextureName(tile.texture_id);
1350 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1352 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1353 if (enable_shaders){
1354 buf->getMaterial().setTexture(2, tsrc->getTexture("disable_img.png"));
1355 buf->getMaterial().MaterialType = shdrsrc->getShaderInfo(tile.shader_id).material;
1356 if (enable_bumpmapping || enable_parallax_occlusion){
1357 if (tsrc->isKnownSourceImage("override_normal.png")){
1358 buf->getMaterial().setTexture(1, tsrc->getTexture("override_normal.png"));
1359 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1361 std::string fname_base,fname_normal;
1362 fname_base = tsrc->getTextureName(tile.texture_id);
1364 pos = fname_base.find(".");
1365 fname_normal = fname_base.substr (0, pos);
1366 fname_normal += "_normal.png";
1367 if (tsrc->isKnownSourceImage(fname_normal)){
1369 os<<fname_normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1370 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
1371 buf->getMaterial().setTexture(2, tsrc->getTexture("enable_img.png"));
1378 // Day-night transition
1379 if(daynight_ratio != m_last_daynight_ratio)
1381 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1382 i = m_daynight_diffs.begin();
1383 i != m_daynight_diffs.end(); i++)
1385 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1386 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1387 for(std::map<u32, std::pair<u8, u8 > >::iterator
1388 j = i->second.begin();
1389 j != i->second.end(); j++)
1391 u32 vertexIndex = j->first;
1392 u8 day = j->second.first;
1393 u8 night = j->second.second;
1394 finalColorBlend(vertices[vertexIndex].Color,
1395 day, night, daynight_ratio);
1398 m_last_daynight_ratio = daynight_ratio;
1404 void MapBlockMesh::updateCameraOffset(v3s16 camera_offset)
1406 if (camera_offset != m_camera_offset) {
1407 translateMesh(m_mesh, intToFloat(m_camera_offset-camera_offset, BS));
1408 m_camera_offset = camera_offset;
1416 void MeshCollector::append(const TileSpec &tile,
1417 const video::S3DVertex *vertices, u32 numVertices,
1418 const u16 *indices, u32 numIndices)
1420 if(numIndices > 65535)
1422 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1426 PreMeshBuffer *p = NULL;
1427 for(u32 i=0; i<prebuffers.size(); i++)
1429 PreMeshBuffer &pp = prebuffers[i];
1432 if(pp.indices.size() + numIndices > 65535)
1443 prebuffers.push_back(pp);
1444 p = &prebuffers[prebuffers.size()-1];
1447 u32 vertex_count = p->vertices.size();
1448 for(u32 i=0; i<numIndices; i++)
1450 u32 j = indices[i] + vertex_count;
1451 p->indices.push_back(j);
1453 for(u32 i=0; i<numVertices; i++)
1455 p->vertices.push_back(vertices[i]);