3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "mapblock_mesh.h"
24 #include "main.h" // for g_profiler
29 #include "content_mapblock.h"
33 #include "util/directiontables.h"
35 float srgb_linear_multiply(float f, float m, float max)
37 f = f * f; // SRGB -> Linear
39 f = sqrt(f); // Linear -> SRGB
49 MeshMakeData::MeshMakeData(IGameDef *gamedef):
51 m_blockpos(-1337,-1337,-1337),
52 m_crack_pos_relative(-1337, -1337, -1337),
53 m_smooth_lighting(false),
57 void MeshMakeData::fill(MapBlock *block)
59 m_blockpos = block->getPos();
61 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
67 // Allocate this block + neighbors
69 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
70 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
73 //TimeTaker timer("copy central block data");
77 block->copyTo(m_vmanip);
80 //TimeTaker timer("copy neighbor block data");
84 Copy neighbors. This is lightning fast.
85 Copying only the borders would be *very* slow.
89 Map *map = block->getParent();
91 for(u16 i=0; i<26; i++)
93 const v3s16 &dir = g_26dirs[i];
94 v3s16 bp = m_blockpos + dir;
95 MapBlock *b = map->getBlockNoCreateNoEx(bp);
102 void MeshMakeData::fillSingleNode(MapNode *node)
104 m_blockpos = v3s16(0,0,0);
106 v3s16 blockpos_nodes = v3s16(0,0,0);
107 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
108 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
109 s32 volume = area.getVolume();
110 s32 our_node_index = area.index(1,1,1);
112 // Allocate this block + neighbors
114 m_vmanip.addArea(area);
117 MapNode *data = new MapNode[volume];
118 for(s32 i = 0; i < volume; i++)
120 if(i == our_node_index)
126 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
129 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
133 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
136 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
139 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
141 m_smooth_lighting = smooth_lighting;
145 Light and vertex color functions
149 Calculate non-smooth lighting at interior of node.
152 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
155 INodeDefManager *ndef = data->m_gamedef->ndef();
156 u8 light = n.getLight(bank, ndef);
160 light = undiminish_light(light);
165 light = diminish_light(light);
169 return decode_light(light);
173 Calculate non-smooth lighting at interior of node.
176 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
178 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
179 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
180 return day | (night << 8);
184 Calculate non-smooth lighting at face of node.
187 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
188 v3s16 face_dir, MeshMakeData *data)
190 INodeDefManager *ndef = data->m_gamedef->ndef();
193 u8 l1 = n.getLight(bank, ndef);
194 u8 l2 = n2.getLight(bank, ndef);
200 // Boost light level for light sources
201 u8 light_source = MYMAX(ndef->get(n).light_source,
202 ndef->get(n2).light_source);
203 //if(light_source >= light)
204 //return decode_light(undiminish_light(light_source));
205 if(light_source > light)
206 //return decode_light(light_source);
207 light = light_source;
209 // Make some nice difference to different sides
211 // This makes light come from a corner
212 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
213 light = diminish_light(diminish_light(light));
214 else if(face_dir.X == -1 || face_dir.Z == -1)
215 light = diminish_light(light);*/
217 // All neighboring faces have different shade (like in minecraft)
218 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
219 light = diminish_light(diminish_light(light));
220 else if(face_dir.Z == 1 || face_dir.Z == -1)
221 light = diminish_light(light);
223 return decode_light(light);
227 Calculate non-smooth lighting at face of node.
230 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
232 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
233 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
234 return day | (night << 8);
238 Calculate smooth lighting at the XYZ- corner of p.
241 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
243 static v3s16 dirs8[8] = {
254 INodeDefManager *ndef = data->m_gamedef->ndef();
256 u16 ambient_occlusion = 0;
259 u8 light_source_max = 0;
260 for(u32 i=0; i<8; i++)
262 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
263 const ContentFeatures &f = ndef->get(n);
264 if(f.light_source > light_source_max)
265 light_source_max = f.light_source;
266 // Check f.solidness because fast-style leaves look
268 if(f.param_type == CPT_LIGHT && f.solidness != 2)
270 light += decode_light(n.getLight(bank, ndef));
273 else if(n.getContent() != CONTENT_IGNORE)
282 light /= light_count;
284 // Boost brightness around light sources
285 if(decode_light(light_source_max) >= light)
286 //return decode_light(undiminish_light(light_source_max));
287 return decode_light(light_source_max);
289 if(ambient_occlusion > 4)
291 //ambient_occlusion -= 4;
292 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
293 float light_amount = (8 - ambient_occlusion) / 4.0;
294 float light_f = (float)light / 255.0;
295 light_f = pow(light_f, 2.2f); // gamma -> linear space
296 light_f = light_f * light_amount;
297 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
300 light = 255.0 * light_f + 0.5;
307 Calculate smooth lighting at the XYZ- corner of p.
310 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
312 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
313 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
314 return day | (night << 8);
318 Calculate smooth lighting at the given corner of p.
321 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
323 if(corner.X == 1) p.X += 1;
324 else assert(corner.X == -1);
325 if(corner.Y == 1) p.Y += 1;
326 else assert(corner.Y == -1);
327 if(corner.Z == 1) p.Z += 1;
328 else assert(corner.Z == -1);
330 return getSmoothLight(p, data);
334 Converts from day + night color values (0..255)
335 and a given daynight_ratio to the final SColor shown on screen.
337 static void finalColorBlend(video::SColor& result,
338 u8 day, u8 night, u32 daynight_ratio)
340 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
344 b += (day - night) / 13;
345 rg -= (day - night) / 23;
347 // Emphase blue a bit in darker places
348 // Each entry of this array represents a range of 8 blue levels
349 static u8 emphase_blue_when_dark[32] = {
350 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
357 b += emphase_blue_when_dark[b / 8];
359 // Artificial light is yellow-ish
360 static u8 emphase_yellow_when_artificial[16] = {
361 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
363 rg += emphase_yellow_when_artificial[night/16];
375 Mesh generation helpers
379 vertex_dirs: v3s16[4]
381 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
384 If looked from outside the node towards the face, the corners are:
390 if(dir == v3s16(0,0,1))
392 // If looking towards z+, this is the face that is behind
393 // the center point, facing towards z+.
394 vertex_dirs[0] = v3s16(-1,-1, 1);
395 vertex_dirs[1] = v3s16( 1,-1, 1);
396 vertex_dirs[2] = v3s16( 1, 1, 1);
397 vertex_dirs[3] = v3s16(-1, 1, 1);
399 else if(dir == v3s16(0,0,-1))
402 vertex_dirs[0] = v3s16( 1,-1,-1);
403 vertex_dirs[1] = v3s16(-1,-1,-1);
404 vertex_dirs[2] = v3s16(-1, 1,-1);
405 vertex_dirs[3] = v3s16( 1, 1,-1);
407 else if(dir == v3s16(1,0,0))
410 vertex_dirs[0] = v3s16( 1,-1, 1);
411 vertex_dirs[1] = v3s16( 1,-1,-1);
412 vertex_dirs[2] = v3s16( 1, 1,-1);
413 vertex_dirs[3] = v3s16( 1, 1, 1);
415 else if(dir == v3s16(-1,0,0))
418 vertex_dirs[0] = v3s16(-1,-1,-1);
419 vertex_dirs[1] = v3s16(-1,-1, 1);
420 vertex_dirs[2] = v3s16(-1, 1, 1);
421 vertex_dirs[3] = v3s16(-1, 1,-1);
423 else if(dir == v3s16(0,1,0))
425 // faces towards Y+ (assume Z- as "down" in texture)
426 vertex_dirs[0] = v3s16( 1, 1,-1);
427 vertex_dirs[1] = v3s16(-1, 1,-1);
428 vertex_dirs[2] = v3s16(-1, 1, 1);
429 vertex_dirs[3] = v3s16( 1, 1, 1);
431 else if(dir == v3s16(0,-1,0))
433 // faces towards Y- (assume Z+ as "down" in texture)
434 vertex_dirs[0] = v3s16( 1,-1, 1);
435 vertex_dirs[1] = v3s16(-1,-1, 1);
436 vertex_dirs[2] = v3s16(-1,-1,-1);
437 vertex_dirs[3] = v3s16( 1,-1,-1);
444 video::S3DVertex vertices[4]; // Precalculated vertices
447 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
448 v3f p, v3s16 dir, v3f scale, u8 light_source, std::vector<FastFace> &dest)
452 // Position is at the center of the cube.
461 v3s16 vertex_dirs[4];
462 getNodeVertexDirs(dir, vertex_dirs);
464 switch (tile.rotation)
470 vertex_dirs[0] = vertex_dirs[3];
471 vertex_dirs[3] = vertex_dirs[2];
472 vertex_dirs[2] = vertex_dirs[1];
477 vertex_dirs[0] = vertex_dirs[2];
480 vertex_dirs[1] = vertex_dirs[3];
485 vertex_dirs[0] = vertex_dirs[1];
486 vertex_dirs[1] = vertex_dirs[2];
487 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];
501 vertex_dirs[0] = vertex_dirs[1];
502 vertex_dirs[1] = vertex_dirs[2];
503 vertex_dirs[2] = vertex_dirs[3];
510 vertex_dirs[0] = vertex_dirs[3];
511 vertex_dirs[3] = vertex_dirs[2];
512 vertex_dirs[2] = vertex_dirs[1];
519 vertex_dirs[0] = vertex_dirs[1];
520 vertex_dirs[1] = vertex_dirs[2];
521 vertex_dirs[2] = vertex_dirs[3];
537 for(u16 i=0; i<4; i++)
540 BS/2*vertex_dirs[i].X,
541 BS/2*vertex_dirs[i].Y,
542 BS/2*vertex_dirs[i].Z
546 for(u16 i=0; i<4; i++)
548 vertex_pos[i].X *= scale.X;
549 vertex_pos[i].Y *= scale.Y;
550 vertex_pos[i].Z *= scale.Z;
551 vertex_pos[i] += pos;
555 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
556 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
557 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
559 v3f normal(dir.X, dir.Y, dir.Z);
561 u8 alpha = tile.alpha;
563 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
564 MapBlock_LightColor(alpha, li0, light_source),
565 core::vector2d<f32>(x0+w*abs_scale, y0+h));
566 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
567 MapBlock_LightColor(alpha, li1, light_source),
568 core::vector2d<f32>(x0, y0+h));
569 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
570 MapBlock_LightColor(alpha, li2, light_source),
571 core::vector2d<f32>(x0, y0));
572 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
573 MapBlock_LightColor(alpha, li3, light_source),
574 core::vector2d<f32>(x0+w*abs_scale, y0));
578 dest.push_back(face);
582 Nodes make a face if contents differ and solidness differs.
585 1: Face uses m1's content
586 2: Face uses m2's content
587 equivalent: Whether the blocks share the same face (eg. water and glass)
589 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
591 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
592 INodeDefManager *ndef)
596 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
599 bool contents_differ = (m1 != m2);
601 const ContentFeatures &f1 = ndef->get(m1);
602 const ContentFeatures &f2 = ndef->get(m2);
604 // Contents don't differ for different forms of same liquid
605 if(f1.sameLiquid(f2))
606 contents_differ = false;
608 u8 c1 = f1.solidness;
609 u8 c2 = f2.solidness;
611 bool solidness_differs = (c1 != c2);
612 bool makes_face = contents_differ && solidness_differs;
614 if(makes_face == false)
618 c1 = f1.visual_solidness;
620 c2 = f2.visual_solidness;
624 // If same solidness, liquid takes precense
638 Gets nth node tile (0 <= n <= 5).
640 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
642 INodeDefManager *ndef = data->m_gamedef->ndef();
643 TileSpec spec = ndef->get(mn).tiles[tileindex];
644 // Apply temporary crack
645 if(p == data->m_crack_pos_relative)
647 spec.material_flags |= MATERIAL_FLAG_CRACK;
653 Gets node tile given a face direction.
655 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
657 INodeDefManager *ndef = data->m_gamedef->ndef();
659 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
660 // (0,0,1), (0,0,-1) or (0,0,0)
661 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
663 // Convert direction to single integer for table lookup
668 // 4 = invalid, treat as (0,0,0)
672 u8 dir_i = ((dir.X + 2 * dir.Y + 3 * dir.Z) & 7)*2;
674 // Get rotation for things like chests
675 u8 facedir = mn.getFaceDir(ndef);
676 assert(facedir <= 23);
677 static const u16 dir_to_tile[24 * 16] =
679 // 0 +X +Y +Z -Z -Y -X -> value=tile,rotation
680 0,0, 2,0 , 0,0 , 4,0 , 0,0, 5,0 , 1,0 , 3,0 , // rotate around y+ 0 - 3
681 0,0, 4,0 , 0,3 , 3,0 , 0,0, 2,0 , 1,1 , 5,0 ,
682 0,0, 3,0 , 0,2 , 5,0 , 0,0, 4,0 , 1,2 , 2,0 ,
683 0,0, 5,0 , 0,1 , 2,0 , 0,0, 3,0 , 1,3 , 4,0 ,
685 0,0, 2,3 , 5,0 , 0,2 , 0,0, 1,0 , 4,2 , 3,1 , // rotate around z+ 4 - 7
686 0,0, 4,3 , 2,0 , 0,3 , 0,0, 1,1 , 3,2 , 5,1 ,
687 0,0, 3,3 , 4,0 , 0,0 , 0,0, 1,2 , 5,2 , 2,1 ,
688 0,0, 5,3 , 3,0 , 0,1 , 0,0, 1,3 , 2,2 , 4,1 ,
690 0,0, 2,1 , 4,2 , 1,2 , 0,0, 0,0 , 5,0 , 3,3 , // rotate around z- 8 - 11
691 0,0, 4,1 , 3,2 , 1,3 , 0,0, 0,3 , 2,0 , 5,3 ,
692 0,0, 3,1 , 5,2 , 1,0 , 0,0, 0,2 , 4,0 , 2,3 ,
693 0,0, 5,1 , 2,2 , 1,1 , 0,0, 0,1 , 3,0 , 4,3 ,
695 0,0, 0,3 , 3,3 , 4,1 , 0,0, 5,3 , 2,3 , 1,3 , // rotate around x+ 12 - 15
696 0,0, 0,2 , 5,3 , 3,1 , 0,0, 2,3 , 4,3 , 1,0 ,
697 0,0, 0,1 , 2,3 , 5,1 , 0,0, 4,3 , 3,3 , 1,1 ,
698 0,0, 0,0 , 4,3 , 2,1 , 0,0, 3,3 , 5,3 , 1,2 ,
700 0,0, 1,1 , 2,1 , 4,3 , 0,0, 5,1 , 3,1 , 0,1 , // rotate around x- 16 - 19
701 0,0, 1,2 , 4,1 , 3,3 , 0,0, 2,1 , 5,1 , 0,0 ,
702 0,0, 1,3 , 3,1 , 5,3 , 0,0, 4,1 , 2,1 , 0,3 ,
703 0,0, 1,0 , 5,1 , 2,3 , 0,0, 3,1 , 4,1 , 0,2 ,
705 0,0, 3,2 , 1,2 , 4,2 , 0,0, 5,2 , 0,2 , 2,2 , // rotate around y- 20 - 23
706 0,0, 5,2 , 1,3 , 3,2 , 0,0, 2,2 , 0,1 , 4,2 ,
707 0,0, 2,2 , 1,0 , 5,2 , 0,0, 4,2 , 0,0 , 3,2 ,
708 0,0, 4,2 , 1,1 , 2,2 , 0,0, 3,2 , 0,3 , 5,2
711 u16 tile_index=facedir*16 + dir_i;
712 TileSpec spec = getNodeTileN(mn, p, dir_to_tile[tile_index], data);
713 spec.rotation=dir_to_tile[tile_index + 1];
714 spec.texture = data->m_gamedef->tsrc()->getTexture(spec.texture_id);
718 static void getTileInfo(
726 v3s16 &face_dir_corrected,
732 VoxelManipulator &vmanip = data->m_vmanip;
733 INodeDefManager *ndef = data->m_gamedef->ndef();
734 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
736 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
737 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
738 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
739 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
742 bool equivalent = false;
743 u8 mf = face_contents(n0.getContent(), n1.getContent(),
758 face_dir_corrected = face_dir;
759 light_source = ndef->get(n0).light_source;
764 p_corrected = p + face_dir;
765 face_dir_corrected = -face_dir;
766 light_source = ndef->get(n1).light_source;
769 // eg. water and glass
771 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
773 if(data->m_smooth_lighting == false)
775 lights[0] = lights[1] = lights[2] = lights[3] =
776 getFaceLight(n0, n1, face_dir, data);
780 v3s16 vertex_dirs[4];
781 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
782 for(u16 i=0; i<4; i++)
784 lights[i] = getSmoothLight(
785 blockpos_nodes + p_corrected,
786 vertex_dirs[i], data);
795 translate_dir: unit vector with only one of x, y or z
796 face_dir: unit vector with only one of x, y or z
798 static void updateFastFaceRow(
805 std::vector<FastFace> &dest)
809 u16 continuous_tiles_count = 0;
811 bool makes_face = false;
813 v3s16 face_dir_corrected;
814 u16 lights[4] = {0,0,0,0};
817 getTileInfo(data, p, face_dir,
818 makes_face, p_corrected, face_dir_corrected,
819 lights, tile, light_source);
821 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
823 // If tiling can be done, this is set to false in the next step
824 bool next_is_different = true;
828 bool next_makes_face = false;
829 v3s16 next_p_corrected;
830 v3s16 next_face_dir_corrected;
831 u16 next_lights[4] = {0,0,0,0};
833 u8 next_light_source = 0;
835 // If at last position, there is nothing to compare to and
836 // the face must be drawn anyway
837 if(j != MAP_BLOCKSIZE - 1)
839 p_next = p + translate_dir;
841 getTileInfo(data, p_next, face_dir,
842 next_makes_face, next_p_corrected,
843 next_face_dir_corrected, next_lights,
844 next_tile, next_light_source);
846 if(next_makes_face == makes_face
847 && next_p_corrected == p_corrected + translate_dir
848 && next_face_dir_corrected == face_dir_corrected
849 && next_lights[0] == lights[0]
850 && next_lights[1] == lights[1]
851 && next_lights[2] == lights[2]
852 && next_lights[3] == lights[3]
854 && tile.rotation == 0
855 && next_light_source == light_source)
857 next_is_different = false;
861 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
862 next_makes_face != makes_face ? 1 : 0);
863 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
864 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
865 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
866 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
867 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
868 (next_lights[0] != lights[0] ||
869 next_lights[0] != lights[0] ||
870 next_lights[0] != lights[0] ||
871 next_lights[0] != lights[0]) ? 1 : 0);
872 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
873 !(next_tile == tile) ? 1 : 0);
876 /*g_profiler->add("Meshgen: Total faces checked", 1);
878 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
881 g_profiler->add("Meshgen: diff: last position", 1);*/
884 continuous_tiles_count++;
886 if(next_is_different)
889 Create a face if there should be one
893 // Floating point conversion of the position vector
894 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
895 // Center point of face (kind of)
896 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
897 if(continuous_tiles_count != 1)
898 sp += translate_dir_f;
901 if(translate_dir.X != 0)
903 scale.X = continuous_tiles_count;
905 if(translate_dir.Y != 0)
907 scale.Y = continuous_tiles_count;
909 if(translate_dir.Z != 0)
911 scale.Z = continuous_tiles_count;
914 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
915 sp, face_dir_corrected, scale, light_source,
918 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
919 for(int i=1; i<continuous_tiles_count; i++){
920 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
924 continuous_tiles_count = 0;
926 makes_face = next_makes_face;
927 p_corrected = next_p_corrected;
928 face_dir_corrected = next_face_dir_corrected;
929 lights[0] = next_lights[0];
930 lights[1] = next_lights[1];
931 lights[2] = next_lights[2];
932 lights[3] = next_lights[3];
934 light_source = next_light_source;
941 static void updateAllFastFaceRows(MeshMakeData *data,
942 std::vector<FastFace> &dest)
945 Go through every y,z and get top(y+) faces in rows of x+
947 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
948 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
949 updateFastFaceRow(data,
953 v3s16(0,1,0), //face dir
960 Go through every x,y and get right(x+) faces in rows of z+
962 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
963 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
964 updateFastFaceRow(data,
968 v3s16(1,0,0), //face dir
975 Go through every y,z and get back(z+) faces in rows of x+
977 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
978 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
979 updateFastFaceRow(data,
983 v3s16(0,0,1), //face dir
994 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
995 m_mesh(new scene::SMesh()),
996 m_gamedef(data->m_gamedef),
997 m_animation_force_timer(0), // force initial animation
1000 m_last_daynight_ratio((u32) -1),
1003 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1004 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
1005 //TimeTaker timer1("MapBlockMesh()");
1007 std::vector<FastFace> fastfaces_new;
1010 We are including the faces of the trailing edges of the block.
1011 This means that when something changes, the caller must
1012 also update the meshes of the blocks at the leading edges.
1014 NOTE: This is the slowest part of this method.
1017 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
1018 //TimeTaker timer2("updateAllFastFaceRows()");
1019 updateAllFastFaceRows(data, fastfaces_new);
1024 Convert FastFaces to MeshCollector
1027 MeshCollector collector;
1030 // avg 0ms (100ms spikes when loading textures the first time)
1031 // (NOTE: probably outdated)
1032 //TimeTaker timer2("MeshCollector building");
1034 for(u32 i=0; i<fastfaces_new.size(); i++)
1036 FastFace &f = fastfaces_new[i];
1038 const u16 indices[] = {0,1,2,2,3,0};
1039 const u16 indices_alternate[] = {0,1,3,2,3,1};
1041 if(f.tile.texture == NULL)
1044 const u16 *indices_p = indices;
1047 Revert triangles for nicer looking gradient if vertices
1048 1 and 3 have same color or 0 and 2 have different color.
1049 getRed() is the day color.
1051 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1052 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1053 indices_p = indices_alternate;
1055 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1060 Add special graphics:
1067 mapblock_mesh_generate_special(data, collector);
1071 Convert MeshCollector to SMesh
1072 Also store animation info
1074 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1075 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1076 video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
1077 getShader("test_shader_1").material;
1078 video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
1079 getShader("test_shader_2").material;
1080 video::E_MATERIAL_TYPE shadermat3 = m_gamedef->getShaderSource()->
1081 getShader("test_shader_3").material;
1082 video::E_MATERIAL_TYPE bumpmaps1 = m_gamedef->getShaderSource()->
1083 getShader("bumpmaps_solids").material;
1084 video::E_MATERIAL_TYPE bumpmaps2 = m_gamedef->getShaderSource()->
1085 getShader("bumpmaps_liquids").material;
1087 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1089 PreMeshBuffer &p = collector.prebuffers[i];
1090 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1091 <<", p.indices.size()="<<p.indices.size()
1094 // Generate animation data
1096 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1098 ITextureSource *tsrc = data->m_gamedef->tsrc();
1099 std::string crack_basename = tsrc->getTextureName(p.tile.texture_id);
1100 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1101 crack_basename += "^[cracko";
1103 crack_basename += "^[crack";
1104 m_crack_materials.insert(std::make_pair(i, crack_basename));
1106 // - Texture animation
1107 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1109 ITextureSource *tsrc = data->m_gamedef->tsrc();
1110 // Add to MapBlockMesh in order to animate these tiles
1111 m_animation_tiles[i] = p.tile;
1112 m_animation_frames[i] = 0;
1113 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1114 // Get starting position from noise
1115 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1116 data->m_blockpos.X, data->m_blockpos.Y,
1117 data->m_blockpos.Z, 0));
1119 // Play all synchronized
1120 m_animation_frame_offsets[i] = 0;
1122 // Replace tile texture with the first animation frame
1123 std::ostringstream os(std::ios::binary);
1124 os<<tsrc->getTextureName(p.tile.texture_id);
1125 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1126 p.tile.texture = tsrc->getTexture(
1128 &p.tile.texture_id);
1130 // - Classic lighting (shaders handle this by themselves)
1133 for(u32 j = 0; j < p.vertices.size(); j++)
1135 video::SColor &vc = p.vertices[j].Color;
1136 // Set initial real color and store for later updates
1137 u8 day = vc.getRed();
1138 u8 night = vc.getGreen();
1139 finalColorBlend(vc, day, night, 1000);
1141 m_daynight_diffs[i][j] = std::make_pair(day, night);
1142 // Brighten topside (no shaders)
1143 if(p.vertices[j].Normal.Y > 0.5)
1145 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1146 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1147 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1153 video::SMaterial material;
1154 material.setFlag(video::EMF_LIGHTING, false);
1155 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1156 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1157 material.setFlag(video::EMF_FOG_ENABLE, true);
1158 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1159 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1160 material.MaterialType
1161 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1162 material.setTexture(0, p.tile.texture);
1165 if (enable_bumpmapping)
1167 ITextureSource *tsrc = data->m_gamedef->tsrc();
1168 std::string basename,normal,replace;
1169 replace = "_normal.png";
1170 basename = tsrc->getTextureName(p.tile.texture_id);
1171 unsigned pos = basename.find(".");
1172 normal = basename.substr (0, pos) + replace;
1173 if (tsrc->isKnownSourceImage(normal))
1175 // look for image extension and replace it
1176 for(std::string::size_type i = 0; (i = basename.find(".", i)) != std::string::npos;)
1178 basename.replace(i, 4, replace);
1179 i += replace.length();
1181 material.setTexture(1, tsrc->getTexture(basename));
1182 p.tile.applyMaterialOptionsWithShaders(material, bumpmaps1,bumpmaps2, shadermat3);
1185 p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2, shadermat3);
1188 p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2, shadermat3);
1191 p.tile.applyMaterialOptions(material);
1193 // Create meshbuffer
1195 // This is a "Standard MeshBuffer",
1196 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1197 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1199 buf->Material = material;
1201 m_mesh->addMeshBuffer(buf);
1204 buf->append(&p.vertices[0], p.vertices.size(),
1205 &p.indices[0], p.indices.size());
1209 Do some stuff to the mesh
1212 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1217 // Usually 1-700 faces and 1-7 materials
1218 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1219 <<"and uses "<<m_mesh->getMeshBufferCount()
1220 <<" materials (meshbuffers)"<<std::endl;
1223 // Use VBO for mesh (this just would set this for ever buffer)
1224 // This will lead to infinite memory usage because or irrlicht.
1225 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1228 NOTE: If that is enabled, some kind of a queue to the main
1229 thread should be made which would call irrlicht to delete
1230 the hardware buffer and then delete the mesh
1234 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1236 // Check if animation is required for this mesh
1238 !m_crack_materials.empty() ||
1239 !m_daynight_diffs.empty() ||
1240 !m_animation_tiles.empty();
1243 MapBlockMesh::~MapBlockMesh()
1249 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1251 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1252 bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping");
1254 if(!m_has_animation)
1256 m_animation_force_timer = 100000;
1260 m_animation_force_timer = myrand_range(5, 100);
1263 if(crack != m_last_crack)
1265 for(std::map<u32, std::string>::iterator
1266 i = m_crack_materials.begin();
1267 i != m_crack_materials.end(); i++)
1269 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1270 std::string basename = i->second;
1272 // Create new texture name from original
1273 ITextureSource *tsrc = m_gamedef->getTextureSource();
1274 std::ostringstream os;
1275 os<<basename<<crack;
1276 buf->getMaterial().setTexture(0,
1277 tsrc->getTexture(os.str()));
1280 m_last_crack = crack;
1283 // Texture animation
1284 for(std::map<u32, TileSpec>::iterator
1285 i = m_animation_tiles.begin();
1286 i != m_animation_tiles.end(); i++)
1288 const TileSpec &tile = i->second;
1289 // Figure out current frame
1290 int frameoffset = m_animation_frame_offsets[i->first];
1291 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1292 + frameoffset) % tile.animation_frame_count;
1293 // If frame doesn't change, skip
1294 if(frame == m_animation_frames[i->first])
1297 m_animation_frames[i->first] = frame;
1299 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1300 ITextureSource *tsrc = m_gamedef->getTextureSource();
1302 // Create new texture name from original
1303 std::ostringstream os(std::ios::binary);
1304 os<<tsrc->getTextureName(tile.texture_id);
1305 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1307 buf->getMaterial().setTexture(0, tsrc->getTexture(os.str()));
1308 if (enable_shaders && enable_bumpmapping)
1310 std::string basename,normal;
1311 basename = tsrc->getTextureName(tile.texture_id);
1313 pos = basename.find(".");
1314 normal = basename.substr (0, pos);
1315 normal += "_normal.png";
1317 os<<normal<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1318 if (tsrc->isKnownSourceImage(normal))
1319 buf->getMaterial().setTexture(1, tsrc->getTexture(os.str()));
1323 // Day-night transition
1324 if(daynight_ratio != m_last_daynight_ratio)
1326 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1327 i = m_daynight_diffs.begin();
1328 i != m_daynight_diffs.end(); i++)
1330 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1331 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1332 for(std::map<u32, std::pair<u8, u8 > >::iterator
1333 j = i->second.begin();
1334 j != i->second.end(); j++)
1336 u32 vertexIndex = j->first;
1337 u8 day = j->second.first;
1338 u8 night = j->second.second;
1339 finalColorBlend(vertices[vertexIndex].Color,
1340 day, night, daynight_ratio);
1341 // Brighten topside (no shaders)
1342 if(vertices[vertexIndex].Normal.Y > 0.5)
1344 video::SColor &vc = vertices[vertexIndex].Color;
1345 vc.setRed (srgb_linear_multiply(vc.getRed(), 1.3, 255.0));
1346 vc.setGreen(srgb_linear_multiply(vc.getGreen(), 1.3, 255.0));
1347 vc.setBlue (srgb_linear_multiply(vc.getBlue(), 1.3, 255.0));
1351 m_last_daynight_ratio = daynight_ratio;
1361 void MeshCollector::append(const TileSpec &tile,
1362 const video::S3DVertex *vertices, u32 numVertices,
1363 const u16 *indices, u32 numIndices)
1365 if(numIndices > 65535)
1367 dstream<<"FIXME: MeshCollector::append() called with numIndices="<<numIndices<<" (limit 65535)"<<std::endl;
1371 PreMeshBuffer *p = NULL;
1372 for(u32 i=0; i<prebuffers.size(); i++)
1374 PreMeshBuffer &pp = prebuffers[i];
1377 if(pp.indices.size() + numIndices > 65535)
1388 prebuffers.push_back(pp);
1389 p = &prebuffers[prebuffers.size()-1];
1392 u32 vertex_count = p->vertices.size();
1393 for(u32 i=0; i<numIndices; i++)
1395 u32 j = indices[i] + vertex_count;
1396 p->indices.push_back(j);
1398 for(u32 i=0; i<numVertices; i++)
1400 p->vertices.push_back(vertices[i]);