3 Copyright (C) 2010-2011 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"
36 MeshMakeData::MeshMakeData(IGameDef *gamedef):
38 m_blockpos(-1337,-1337,-1337),
39 m_crack_pos_relative(-1337, -1337, -1337),
40 m_smooth_lighting(false),
44 void MeshMakeData::fill(MapBlock *block)
46 m_blockpos = block->getPos();
48 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
54 // Allocate this block + neighbors
56 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
57 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
60 //TimeTaker timer("copy central block data");
64 block->copyTo(m_vmanip);
67 //TimeTaker timer("copy neighbor block data");
71 Copy neighbors. This is lightning fast.
72 Copying only the borders would be *very* slow.
76 Map *map = block->getParent();
78 for(u16 i=0; i<6; i++)
80 const v3s16 &dir = g_6dirs[i];
81 v3s16 bp = m_blockpos + dir;
82 MapBlock *b = map->getBlockNoCreateNoEx(bp);
89 void MeshMakeData::fillSingleNode(MapNode *node)
91 m_blockpos = v3s16(0,0,0);
93 v3s16 blockpos_nodes = v3s16(0,0,0);
94 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
95 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
96 s32 volume = area.getVolume();
97 s32 our_node_index = area.index(1,1,1);
99 // Allocate this block + neighbors
101 m_vmanip.addArea(area);
104 MapNode *data = new MapNode[volume];
105 for(s32 i = 0; i < volume; i++)
107 if(i == our_node_index)
113 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
116 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
120 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
123 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
126 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
128 m_smooth_lighting = smooth_lighting;
132 Light and vertex color functions
136 Calculate non-smooth lighting at interior of node.
139 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
142 INodeDefManager *ndef = data->m_gamedef->ndef();
143 u8 light = n.getLight(bank, ndef);
147 light = undiminish_light(light);
152 light = diminish_light(light);
156 return decode_light(light);
160 Calculate non-smooth lighting at interior of node.
163 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
165 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
166 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
167 return day | (night << 8);
171 Calculate non-smooth lighting at face of node.
174 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
175 v3s16 face_dir, MeshMakeData *data)
177 INodeDefManager *ndef = data->m_gamedef->ndef();
180 u8 l1 = n.getLight(bank, ndef);
181 u8 l2 = n2.getLight(bank, ndef);
187 // Boost light level for light sources
188 u8 light_source = MYMAX(ndef->get(n).light_source,
189 ndef->get(n2).light_source);
190 //if(light_source >= light)
191 //return decode_light(undiminish_light(light_source));
192 if(light_source > light)
193 //return decode_light(light_source);
194 light = light_source;
196 // Make some nice difference to different sides
198 // This makes light come from a corner
199 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
200 light = diminish_light(diminish_light(light));
201 else if(face_dir.X == -1 || face_dir.Z == -1)
202 light = diminish_light(light);*/
204 // All neighboring faces have different shade (like in minecraft)
205 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
206 light = diminish_light(diminish_light(light));
207 else if(face_dir.Z == 1 || face_dir.Z == -1)
208 light = diminish_light(light);
210 return decode_light(light);
214 Calculate non-smooth lighting at face of node.
217 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
219 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
220 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
221 return day | (night << 8);
225 Calculate smooth lighting at the XYZ- corner of p.
228 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
230 static v3s16 dirs8[8] = {
241 INodeDefManager *ndef = data->m_gamedef->ndef();
243 u16 ambient_occlusion = 0;
246 u8 light_source_max = 0;
247 for(u32 i=0; i<8; i++)
249 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
250 const ContentFeatures &f = ndef->get(n);
251 if(f.light_source > light_source_max)
252 light_source_max = f.light_source;
253 // Check f.solidness because fast-style leaves look
255 if(f.param_type == CPT_LIGHT && f.solidness != 2)
257 light += decode_light(n.getLight(bank, ndef));
260 else if(n.getContent() != CONTENT_IGNORE)
269 light /= light_count;
271 // Boost brightness around light sources
272 if(decode_light(light_source_max) >= light)
273 //return decode_light(undiminish_light(light_source_max));
274 return decode_light(light_source_max);
276 if(ambient_occlusion > 4)
278 //ambient_occlusion -= 4;
279 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
280 float light_amount = (8 - ambient_occlusion) / 4.0;
281 float light_f = (float)light / 255.0;
282 light_f = pow(light_f, 2.2f); // gamma -> linear space
283 light_f = light_f * light_amount;
284 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
287 light = 255.0 * light_f + 0.5;
294 Calculate smooth lighting at the XYZ- corner of p.
297 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
299 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
300 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
301 return day | (night << 8);
305 Calculate smooth lighting at the given corner of p.
308 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
310 if(corner.X == 1) p.X += 1;
311 else assert(corner.X == -1);
312 if(corner.Y == 1) p.Y += 1;
313 else assert(corner.Y == -1);
314 if(corner.Z == 1) p.Z += 1;
315 else assert(corner.Z == -1);
317 return getSmoothLight(p, data);
321 Converts from day + night color values (0..255)
322 and a given daynight_ratio to the final SColor shown on screen.
324 static void finalColorBlend(video::SColor& result,
325 u8 day, u8 night, u32 daynight_ratio)
327 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
331 b += (day - night) / 13;
332 rg -= (day - night) / 23;
334 // Emphase blue a bit in darker places
335 // Each entry of this array represents a range of 8 blue levels
336 static u8 emphase_blue_when_dark[32] = {
337 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
338 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
344 b += emphase_blue_when_dark[b / 8];
346 // Artificial light is yellow-ish
347 static u8 emphase_yellow_when_artificial[16] = {
348 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
350 rg += emphase_yellow_when_artificial[night/16];
362 Mesh generation helpers
366 vertex_dirs: v3s16[4]
368 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
371 If looked from outside the node towards the face, the corners are:
377 if(dir == v3s16(0,0,1))
379 // If looking towards z+, this is the face that is behind
380 // the center point, facing towards z+.
381 vertex_dirs[0] = v3s16(-1,-1, 1);
382 vertex_dirs[1] = v3s16( 1,-1, 1);
383 vertex_dirs[2] = v3s16( 1, 1, 1);
384 vertex_dirs[3] = v3s16(-1, 1, 1);
386 else if(dir == v3s16(0,0,-1))
389 vertex_dirs[0] = v3s16( 1,-1,-1);
390 vertex_dirs[1] = v3s16(-1,-1,-1);
391 vertex_dirs[2] = v3s16(-1, 1,-1);
392 vertex_dirs[3] = v3s16( 1, 1,-1);
394 else if(dir == v3s16(1,0,0))
397 vertex_dirs[0] = v3s16( 1,-1, 1);
398 vertex_dirs[1] = v3s16( 1,-1,-1);
399 vertex_dirs[2] = v3s16( 1, 1,-1);
400 vertex_dirs[3] = v3s16( 1, 1, 1);
402 else if(dir == v3s16(-1,0,0))
405 vertex_dirs[0] = v3s16(-1,-1,-1);
406 vertex_dirs[1] = v3s16(-1,-1, 1);
407 vertex_dirs[2] = v3s16(-1, 1, 1);
408 vertex_dirs[3] = v3s16(-1, 1,-1);
410 else if(dir == v3s16(0,1,0))
412 // faces towards Y+ (assume Z- as "down" in texture)
413 vertex_dirs[0] = v3s16( 1, 1,-1);
414 vertex_dirs[1] = v3s16(-1, 1,-1);
415 vertex_dirs[2] = v3s16(-1, 1, 1);
416 vertex_dirs[3] = v3s16( 1, 1, 1);
418 else if(dir == v3s16(0,-1,0))
420 // faces towards Y- (assume Z+ as "down" in texture)
421 vertex_dirs[0] = v3s16( 1,-1, 1);
422 vertex_dirs[1] = v3s16(-1,-1, 1);
423 vertex_dirs[2] = v3s16(-1,-1,-1);
424 vertex_dirs[3] = v3s16( 1,-1,-1);
431 video::S3DVertex vertices[4]; // Precalculated vertices
434 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
435 v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
439 // Position is at the center of the cube.
443 v3s16 vertex_dirs[4];
444 getNodeVertexDirs(dir, vertex_dirs);
445 for(u16 i=0; i<4; i++)
448 BS/2*vertex_dirs[i].X,
449 BS/2*vertex_dirs[i].Y,
450 BS/2*vertex_dirs[i].Z
454 for(u16 i=0; i<4; i++)
456 vertex_pos[i].X *= scale.X;
457 vertex_pos[i].Y *= scale.Y;
458 vertex_pos[i].Z *= scale.Z;
459 vertex_pos[i] += pos;
463 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
464 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
465 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
467 v3f normal(dir.X, dir.Y, dir.Z);
469 u8 alpha = tile.alpha;
471 float x0 = tile.texture.pos.X;
472 float y0 = tile.texture.pos.Y;
473 float w = tile.texture.size.X;
474 float h = tile.texture.size.Y;
476 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
477 MapBlock_LightColor(alpha, li0),
478 core::vector2d<f32>(x0+w*abs_scale, y0+h));
479 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
480 MapBlock_LightColor(alpha, li1),
481 core::vector2d<f32>(x0, y0+h));
482 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
483 MapBlock_LightColor(alpha, li2),
484 core::vector2d<f32>(x0, y0));
485 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
486 MapBlock_LightColor(alpha, li3),
487 core::vector2d<f32>(x0+w*abs_scale, y0));
491 dest.push_back(face);
495 Nodes make a face if contents differ and solidness differs.
498 1: Face uses m1's content
499 2: Face uses m2's content
500 equivalent: Whether the blocks share the same face (eg. water and glass)
502 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
504 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
505 INodeDefManager *ndef)
509 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
512 bool contents_differ = (m1 != m2);
514 const ContentFeatures &f1 = ndef->get(m1);
515 const ContentFeatures &f2 = ndef->get(m2);
517 // Contents don't differ for different forms of same liquid
518 if(f1.sameLiquid(f2))
519 contents_differ = false;
521 u8 c1 = f1.solidness;
522 u8 c2 = f2.solidness;
524 bool solidness_differs = (c1 != c2);
525 bool makes_face = contents_differ && solidness_differs;
527 if(makes_face == false)
531 c1 = f1.visual_solidness;
533 c2 = f2.visual_solidness;
537 // If same solidness, liquid takes precense
551 Gets nth node tile (0 <= n <= 5).
553 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
555 INodeDefManager *ndef = data->m_gamedef->ndef();
556 TileSpec spec = ndef->get(mn).tiles[tileindex];
557 // Apply temporary crack
558 if(p == data->m_crack_pos_relative)
560 spec.material_flags |= MATERIAL_FLAG_CRACK;
561 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
563 // If animated, replace tile texture with one without texture atlas
564 if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
566 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
572 Gets node tile given a face direction.
574 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
576 INodeDefManager *ndef = data->m_gamedef->ndef();
578 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
579 // (0,0,1), (0,0,-1) or (0,0,0)
580 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
582 // Convert direction to single integer for table lookup
587 // 4 = invalid, treat as (0,0,0)
591 u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
593 // Get rotation for things like chests
594 u8 facedir = mn.getFaceDir(ndef);
595 assert(facedir <= 3);
597 static const u8 dir_to_tile[4 * 8] =
599 // 0 +X +Y +Z 0 -Z -Y -X
600 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
601 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
602 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
603 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
605 u8 tileindex = dir_to_tile[facedir*8 + dir_i];
606 return getNodeTileN(mn, p, tileindex, data);
609 static void getTileInfo(
617 v3s16 &face_dir_corrected,
622 VoxelManipulator &vmanip = data->m_vmanip;
623 INodeDefManager *ndef = data->m_gamedef->ndef();
624 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
626 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
627 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
628 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
629 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
632 bool equivalent = false;
633 u8 mf = face_contents(n0.getContent(), n1.getContent(),
648 face_dir_corrected = face_dir;
653 p_corrected = p + face_dir;
654 face_dir_corrected = -face_dir;
657 // eg. water and glass
659 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
661 if(data->m_smooth_lighting == false)
663 lights[0] = lights[1] = lights[2] = lights[3] =
664 getFaceLight(n0, n1, face_dir, data);
668 v3s16 vertex_dirs[4];
669 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
670 for(u16 i=0; i<4; i++)
672 lights[i] = getSmoothLight(
673 blockpos_nodes + p_corrected,
674 vertex_dirs[i], data);
683 translate_dir: unit vector with only one of x, y or z
684 face_dir: unit vector with only one of x, y or z
686 static void updateFastFaceRow(
693 core::array<FastFace> &dest)
697 u16 continuous_tiles_count = 0;
699 bool makes_face = false;
701 v3s16 face_dir_corrected;
702 u16 lights[4] = {0,0,0,0};
704 getTileInfo(data, p, face_dir,
705 makes_face, p_corrected, face_dir_corrected,
708 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
710 // If tiling can be done, this is set to false in the next step
711 bool next_is_different = true;
715 bool next_makes_face = false;
716 v3s16 next_p_corrected;
717 v3s16 next_face_dir_corrected;
718 u16 next_lights[4] = {0,0,0,0};
721 // If at last position, there is nothing to compare to and
722 // the face must be drawn anyway
723 if(j != MAP_BLOCKSIZE - 1)
725 p_next = p + translate_dir;
727 getTileInfo(data, p_next, face_dir,
728 next_makes_face, next_p_corrected,
729 next_face_dir_corrected, next_lights,
732 if(next_makes_face == makes_face
733 && next_p_corrected == p_corrected + translate_dir
734 && next_face_dir_corrected == face_dir_corrected
735 && next_lights[0] == lights[0]
736 && next_lights[1] == lights[1]
737 && next_lights[2] == lights[2]
738 && next_lights[3] == lights[3]
739 && next_tile == tile)
741 next_is_different = false;
745 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
746 next_makes_face != makes_face ? 1 : 0);
747 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
748 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
749 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
750 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
751 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
752 (next_lights[0] != lights[0] ||
753 next_lights[0] != lights[0] ||
754 next_lights[0] != lights[0] ||
755 next_lights[0] != lights[0]) ? 1 : 0);
756 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
757 !(next_tile == tile) ? 1 : 0);
760 /*g_profiler->add("Meshgen: Total faces checked", 1);
762 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
765 g_profiler->add("Meshgen: diff: last position", 1);*/
768 continuous_tiles_count++;
770 // This is set to true if the texture doesn't allow more tiling
771 bool end_of_texture = false;
773 If there is no texture, it can be tiled infinitely.
774 If tiled==0, it means the texture can be tiled infinitely.
775 Otherwise check tiled agains continuous_tiles_count.
777 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
779 if(tile.texture.tiled <= continuous_tiles_count)
780 end_of_texture = true;
783 // Do this to disable tiling textures
784 //end_of_texture = true; //DEBUG
786 if(next_is_different || end_of_texture)
789 Create a face if there should be one
793 // Floating point conversion of the position vector
794 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
795 // Center point of face (kind of)
796 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
797 if(continuous_tiles_count != 1)
798 sp += translate_dir_f;
801 if(translate_dir.X != 0)
803 scale.X = continuous_tiles_count;
805 if(translate_dir.Y != 0)
807 scale.Y = continuous_tiles_count;
809 if(translate_dir.Z != 0)
811 scale.Z = continuous_tiles_count;
814 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
815 sp, face_dir_corrected, scale,
818 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
819 for(int i=1; i<continuous_tiles_count; i++){
820 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
824 continuous_tiles_count = 0;
826 makes_face = next_makes_face;
827 p_corrected = next_p_corrected;
828 face_dir_corrected = next_face_dir_corrected;
829 lights[0] = next_lights[0];
830 lights[1] = next_lights[1];
831 lights[2] = next_lights[2];
832 lights[3] = next_lights[3];
840 static void updateAllFastFaceRows(MeshMakeData *data,
841 core::array<FastFace> &dest)
844 Go through every y,z and get top(y+) faces in rows of x+
846 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
847 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
848 updateFastFaceRow(data,
852 v3s16(0,1,0), //face dir
859 Go through every x,y and get right(x+) faces in rows of z+
861 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
862 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
863 updateFastFaceRow(data,
867 v3s16(1,0,0), //face dir
874 Go through every y,z and get back(z+) faces in rows of x+
876 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
877 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
878 updateFastFaceRow(data,
882 v3s16(0,0,1), //face dir
893 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
894 m_mesh(new scene::SMesh()),
895 m_gamedef(data->m_gamedef),
896 m_animation_force_timer(0), // force initial animation
899 m_last_daynight_ratio((u32) -1),
902 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
903 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
904 //TimeTaker timer1("MapBlockMesh()");
906 core::array<FastFace> fastfaces_new;
909 We are including the faces of the trailing edges of the block.
910 This means that when something changes, the caller must
911 also update the meshes of the blocks at the leading edges.
913 NOTE: This is the slowest part of this method.
916 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
917 //TimeTaker timer2("updateAllFastFaceRows()");
918 updateAllFastFaceRows(data, fastfaces_new);
923 Convert FastFaces to MeshCollector
926 MeshCollector collector;
929 // avg 0ms (100ms spikes when loading textures the first time)
930 // (NOTE: probably outdated)
931 //TimeTaker timer2("MeshCollector building");
933 for(u32 i=0; i<fastfaces_new.size(); i++)
935 FastFace &f = fastfaces_new[i];
937 const u16 indices[] = {0,1,2,2,3,0};
938 const u16 indices_alternate[] = {0,1,3,2,3,1};
940 if(f.tile.texture.atlas == NULL)
943 const u16 *indices_p = indices;
946 Revert triangles for nicer looking gradient if vertices
947 1 and 3 have same color or 0 and 2 have different color.
948 getRed() is the day color.
950 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
951 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
952 indices_p = indices_alternate;
954 collector.append(f.tile, f.vertices, 4, indices_p, 6);
959 Add special graphics:
966 mapblock_mesh_generate_special(data, collector);
970 Convert MeshCollector to SMesh
971 Also store animation info
973 for(u32 i = 0; i < collector.prebuffers.size(); i++)
975 PreMeshBuffer &p = collector.prebuffers[i];
976 /*dstream<<"p.vertices.size()="<<p.vertices.size()
977 <<", p.indices.size()="<<p.indices.size()
980 // Generate animation data
982 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
984 ITextureSource *tsrc = data->m_gamedef->tsrc();
985 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
986 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
987 crack_basename += "^[cracko";
989 crack_basename += "^[crack";
990 m_crack_materials.insert(std::make_pair(i, crack_basename));
992 // - Texture animation
993 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
995 ITextureSource *tsrc = data->m_gamedef->tsrc();
996 // Add to MapBlockMesh in order to animate these tiles
997 m_animation_tiles[i] = p.tile;
998 m_animation_frames[i] = 0;
999 // Get starting position from noise
1000 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1001 data->m_blockpos.X, data->m_blockpos.Y,
1002 data->m_blockpos.Z, 0));
1003 // Replace tile texture with the first animation frame
1004 std::ostringstream os(std::ios::binary);
1005 os<<tsrc->getTextureName(p.tile.texture.id);
1006 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1007 p.tile.texture = tsrc->getTexture(os.str());
1010 for(u32 j = 0; j < p.vertices.size(); j++)
1012 video::SColor &vc = p.vertices[j].Color;
1013 u8 day = vc.getRed();
1014 u8 night = vc.getGreen();
1015 finalColorBlend(vc, day, night, 1000);
1017 m_daynight_diffs[i][j] = std::make_pair(day, night);
1022 video::SMaterial material;
1023 material.setFlag(video::EMF_LIGHTING, false);
1024 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1025 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1026 material.setFlag(video::EMF_FOG_ENABLE, true);
1027 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1028 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1029 material.MaterialType
1030 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1031 material.setTexture(0, p.tile.texture.atlas);
1032 p.tile.applyMaterialOptions(material);
1034 // Create meshbuffer
1036 // This is a "Standard MeshBuffer",
1037 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1038 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1040 buf->Material = material;
1042 m_mesh->addMeshBuffer(buf);
1045 buf->append(p.vertices.pointer(), p.vertices.size(),
1046 p.indices.pointer(), p.indices.size());
1050 Do some stuff to the mesh
1053 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1054 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1059 // Usually 1-700 faces and 1-7 materials
1060 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1061 <<"and uses "<<m_mesh->getMeshBufferCount()
1062 <<" materials (meshbuffers)"<<std::endl;
1065 // Use VBO for mesh (this just would set this for ever buffer)
1066 // This will lead to infinite memory usage because or irrlicht.
1067 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1070 NOTE: If that is enabled, some kind of a queue to the main
1071 thread should be made which would call irrlicht to delete
1072 the hardware buffer and then delete the mesh
1076 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1078 // Check if animation is required for this mesh
1080 !m_crack_materials.empty() ||
1081 !m_daynight_diffs.empty() ||
1082 !m_animation_tiles.empty();
1085 MapBlockMesh::~MapBlockMesh()
1091 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1093 if(!m_has_animation)
1095 m_animation_force_timer = 100000;
1099 m_animation_force_timer = myrand_range(5, 100);
1102 if(crack != m_last_crack)
1104 for(std::map<u32, std::string>::iterator
1105 i = m_crack_materials.begin();
1106 i != m_crack_materials.end(); i++)
1108 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1109 std::string basename = i->second;
1111 // Create new texture name from original
1112 ITextureSource *tsrc = m_gamedef->getTextureSource();
1113 std::ostringstream os;
1114 os<<basename<<crack;
1115 AtlasPointer ap = tsrc->getTexture(os.str());
1116 buf->getMaterial().setTexture(0, ap.atlas);
1119 m_last_crack = crack;
1122 // Texture animation
1123 for(std::map<u32, TileSpec>::iterator
1124 i = m_animation_tiles.begin();
1125 i != m_animation_tiles.end(); i++)
1127 const TileSpec &tile = i->second;
1128 // Figure out current frame
1129 int frameoffset = m_animation_frame_offsets[i->first];
1130 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1131 + frameoffset) % tile.animation_frame_count;
1132 // If frame doesn't change, skip
1133 if(frame == m_animation_frames[i->first])
1136 m_animation_frames[i->first] = frame;
1138 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1139 ITextureSource *tsrc = m_gamedef->getTextureSource();
1141 // Create new texture name from original
1142 std::ostringstream os(std::ios::binary);
1143 os<<tsrc->getTextureName(tile.texture.id);
1144 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1146 AtlasPointer ap = tsrc->getTexture(os.str());
1147 buf->getMaterial().setTexture(0, ap.atlas);
1150 // Day-night transition
1151 if(daynight_ratio != m_last_daynight_ratio)
1153 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1154 i = m_daynight_diffs.begin();
1155 i != m_daynight_diffs.end(); i++)
1157 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1158 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1159 for(std::map<u32, std::pair<u8, u8 > >::iterator
1160 j = i->second.begin();
1161 j != i->second.end(); j++)
1163 u32 vertexIndex = j->first;
1164 u8 day = j->second.first;
1165 u8 night = j->second.second;
1166 finalColorBlend(vertices[vertexIndex].Color,
1167 day, night, daynight_ratio);
1170 m_last_daynight_ratio = daynight_ratio;
1180 void MeshCollector::append(const TileSpec &tile,
1181 const video::S3DVertex *vertices, u32 numVertices,
1182 const u16 *indices, u32 numIndices)
1184 PreMeshBuffer *p = NULL;
1185 for(u32 i=0; i<prebuffers.size(); i++)
1187 PreMeshBuffer &pp = prebuffers[i];
1199 prebuffers.push_back(pp);
1200 p = &prebuffers[prebuffers.size()-1];
1203 u32 vertex_count = p->vertices.size();
1204 for(u32 i=0; i<numIndices; i++)
1206 u32 j = indices[i] + vertex_count;
1209 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1210 // NOTE: Fix is to just add an another MeshBuffer
1212 p->indices.push_back(j);
1214 for(u32 i=0; i<numVertices; i++)
1216 p->vertices.push_back(vertices[i]);