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"
32 #include "util/directiontables.h"
38 MeshMakeData::MeshMakeData(IGameDef *gamedef):
40 m_blockpos(-1337,-1337,-1337),
41 m_crack_pos_relative(-1337, -1337, -1337),
42 m_smooth_lighting(false),
46 void MeshMakeData::fill(MapBlock *block)
48 m_blockpos = block->getPos();
50 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
56 // Allocate this block + neighbors
58 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
59 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
62 //TimeTaker timer("copy central block data");
66 block->copyTo(m_vmanip);
69 //TimeTaker timer("copy neighbor block data");
73 Copy neighbors. This is lightning fast.
74 Copying only the borders would be *very* slow.
78 Map *map = block->getParent();
80 for(u16 i=0; i<6; i++)
82 const v3s16 &dir = g_6dirs[i];
83 v3s16 bp = m_blockpos + dir;
84 MapBlock *b = map->getBlockNoCreateNoEx(bp);
91 void MeshMakeData::fillSingleNode(MapNode *node)
93 m_blockpos = v3s16(0,0,0);
95 v3s16 blockpos_nodes = v3s16(0,0,0);
96 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
97 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
98 s32 volume = area.getVolume();
99 s32 our_node_index = area.index(1,1,1);
101 // Allocate this block + neighbors
103 m_vmanip.addArea(area);
106 MapNode *data = new MapNode[volume];
107 for(s32 i = 0; i < volume; i++)
109 if(i == our_node_index)
115 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
118 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
122 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
125 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
128 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
130 m_smooth_lighting = smooth_lighting;
134 Light and vertex color functions
138 Calculate non-smooth lighting at interior of node.
141 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
144 INodeDefManager *ndef = data->m_gamedef->ndef();
145 u8 light = n.getLight(bank, ndef);
149 light = undiminish_light(light);
154 light = diminish_light(light);
158 return decode_light(light);
162 Calculate non-smooth lighting at interior of node.
165 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
167 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
168 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
169 return day | (night << 8);
173 Calculate non-smooth lighting at face of node.
176 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
177 v3s16 face_dir, MeshMakeData *data)
179 INodeDefManager *ndef = data->m_gamedef->ndef();
182 u8 l1 = n.getLight(bank, ndef);
183 u8 l2 = n2.getLight(bank, ndef);
189 // Boost light level for light sources
190 u8 light_source = MYMAX(ndef->get(n).light_source,
191 ndef->get(n2).light_source);
192 //if(light_source >= light)
193 //return decode_light(undiminish_light(light_source));
194 if(light_source > light)
195 //return decode_light(light_source);
196 light = light_source;
198 // Make some nice difference to different sides
200 // This makes light come from a corner
201 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
202 light = diminish_light(diminish_light(light));
203 else if(face_dir.X == -1 || face_dir.Z == -1)
204 light = diminish_light(light);*/
206 // All neighboring faces have different shade (like in minecraft)
207 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
208 light = diminish_light(diminish_light(light));
209 else if(face_dir.Z == 1 || face_dir.Z == -1)
210 light = diminish_light(light);
212 return decode_light(light);
216 Calculate non-smooth lighting at face of node.
219 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
221 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
222 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
223 return day | (night << 8);
227 Calculate smooth lighting at the XYZ- corner of p.
230 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
232 static v3s16 dirs8[8] = {
243 INodeDefManager *ndef = data->m_gamedef->ndef();
245 u16 ambient_occlusion = 0;
248 u8 light_source_max = 0;
249 for(u32 i=0; i<8; i++)
251 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
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));
262 else if(n.getContent() != CONTENT_IGNORE)
271 light /= light_count;
273 // Boost brightness around light sources
274 if(decode_light(light_source_max) >= light)
275 //return decode_light(undiminish_light(light_source_max));
276 return decode_light(light_source_max);
278 if(ambient_occlusion > 4)
280 //ambient_occlusion -= 4;
281 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
282 float light_amount = (8 - ambient_occlusion) / 4.0;
283 float light_f = (float)light / 255.0;
284 light_f = pow(light_f, 2.2f); // gamma -> linear space
285 light_f = light_f * light_amount;
286 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
289 light = 255.0 * light_f + 0.5;
296 Calculate smooth lighting at the XYZ- corner of p.
299 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
301 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
302 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
303 return day | (night << 8);
307 Calculate smooth lighting at the given corner of p.
310 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
312 if(corner.X == 1) p.X += 1;
313 else assert(corner.X == -1);
314 if(corner.Y == 1) p.Y += 1;
315 else assert(corner.Y == -1);
316 if(corner.Z == 1) p.Z += 1;
317 else assert(corner.Z == -1);
319 return getSmoothLight(p, data);
323 Converts from day + night color values (0..255)
324 and a given daynight_ratio to the final SColor shown on screen.
326 static void finalColorBlend(video::SColor& result,
327 u8 day, u8 night, u32 daynight_ratio)
329 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
333 b += (day - night) / 13;
334 rg -= (day - night) / 23;
336 // Emphase blue a bit in darker places
337 // Each entry of this array represents a range of 8 blue levels
338 static u8 emphase_blue_when_dark[32] = {
339 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
340 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
346 b += emphase_blue_when_dark[b / 8];
348 // Artificial light is yellow-ish
349 static u8 emphase_yellow_when_artificial[16] = {
350 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
352 rg += emphase_yellow_when_artificial[night/16];
364 Mesh generation helpers
368 vertex_dirs: v3s16[4]
370 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
373 If looked from outside the node towards the face, the corners are:
379 if(dir == v3s16(0,0,1))
381 // If looking towards z+, this is the face that is behind
382 // the center point, facing towards z+.
383 vertex_dirs[0] = v3s16(-1,-1, 1);
384 vertex_dirs[1] = v3s16( 1,-1, 1);
385 vertex_dirs[2] = v3s16( 1, 1, 1);
386 vertex_dirs[3] = v3s16(-1, 1, 1);
388 else if(dir == v3s16(0,0,-1))
391 vertex_dirs[0] = v3s16( 1,-1,-1);
392 vertex_dirs[1] = v3s16(-1,-1,-1);
393 vertex_dirs[2] = v3s16(-1, 1,-1);
394 vertex_dirs[3] = v3s16( 1, 1,-1);
396 else if(dir == v3s16(1,0,0))
399 vertex_dirs[0] = v3s16( 1,-1, 1);
400 vertex_dirs[1] = v3s16( 1,-1,-1);
401 vertex_dirs[2] = v3s16( 1, 1,-1);
402 vertex_dirs[3] = v3s16( 1, 1, 1);
404 else if(dir == v3s16(-1,0,0))
407 vertex_dirs[0] = v3s16(-1,-1,-1);
408 vertex_dirs[1] = v3s16(-1,-1, 1);
409 vertex_dirs[2] = v3s16(-1, 1, 1);
410 vertex_dirs[3] = v3s16(-1, 1,-1);
412 else if(dir == v3s16(0,1,0))
414 // faces towards Y+ (assume Z- as "down" in texture)
415 vertex_dirs[0] = v3s16( 1, 1,-1);
416 vertex_dirs[1] = v3s16(-1, 1,-1);
417 vertex_dirs[2] = v3s16(-1, 1, 1);
418 vertex_dirs[3] = v3s16( 1, 1, 1);
420 else if(dir == v3s16(0,-1,0))
422 // faces towards Y- (assume Z+ as "down" in texture)
423 vertex_dirs[0] = v3s16( 1,-1, 1);
424 vertex_dirs[1] = v3s16(-1,-1, 1);
425 vertex_dirs[2] = v3s16(-1,-1,-1);
426 vertex_dirs[3] = v3s16( 1,-1,-1);
433 video::S3DVertex vertices[4]; // Precalculated vertices
436 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
437 v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
441 // Position is at the center of the cube.
445 v3s16 vertex_dirs[4];
446 getNodeVertexDirs(dir, vertex_dirs);
447 for(u16 i=0; i<4; i++)
450 BS/2*vertex_dirs[i].X,
451 BS/2*vertex_dirs[i].Y,
452 BS/2*vertex_dirs[i].Z
456 for(u16 i=0; i<4; i++)
458 vertex_pos[i].X *= scale.X;
459 vertex_pos[i].Y *= scale.Y;
460 vertex_pos[i].Z *= scale.Z;
461 vertex_pos[i] += pos;
465 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
466 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
467 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
469 v3f normal(dir.X, dir.Y, dir.Z);
471 u8 alpha = tile.alpha;
473 float x0 = tile.texture.pos.X;
474 float y0 = tile.texture.pos.Y;
475 float w = tile.texture.size.X;
476 float h = tile.texture.size.Y;
478 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
479 MapBlock_LightColor(alpha, li0),
480 core::vector2d<f32>(x0+w*abs_scale, y0+h));
481 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
482 MapBlock_LightColor(alpha, li1),
483 core::vector2d<f32>(x0, y0+h));
484 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
485 MapBlock_LightColor(alpha, li2),
486 core::vector2d<f32>(x0, y0));
487 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
488 MapBlock_LightColor(alpha, li3),
489 core::vector2d<f32>(x0+w*abs_scale, y0));
493 dest.push_back(face);
497 Nodes make a face if contents differ and solidness differs.
500 1: Face uses m1's content
501 2: Face uses m2's content
502 equivalent: Whether the blocks share the same face (eg. water and glass)
504 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
506 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
507 INodeDefManager *ndef)
511 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
514 bool contents_differ = (m1 != m2);
516 const ContentFeatures &f1 = ndef->get(m1);
517 const ContentFeatures &f2 = ndef->get(m2);
519 // Contents don't differ for different forms of same liquid
520 if(f1.sameLiquid(f2))
521 contents_differ = false;
523 u8 c1 = f1.solidness;
524 u8 c2 = f2.solidness;
526 bool solidness_differs = (c1 != c2);
527 bool makes_face = contents_differ && solidness_differs;
529 if(makes_face == false)
533 c1 = f1.visual_solidness;
535 c2 = f2.visual_solidness;
539 // If same solidness, liquid takes precense
553 Gets nth node tile (0 <= n <= 5).
555 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
557 INodeDefManager *ndef = data->m_gamedef->ndef();
558 TileSpec spec = ndef->get(mn).tiles[tileindex];
559 // Apply temporary crack
560 if(p == data->m_crack_pos_relative)
562 spec.material_flags |= MATERIAL_FLAG_CRACK;
563 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
565 // If animated, replace tile texture with one without texture atlas
566 if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
568 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
574 Gets node tile given a face direction.
576 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
578 INodeDefManager *ndef = data->m_gamedef->ndef();
580 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
581 // (0,0,1), (0,0,-1) or (0,0,0)
582 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
584 // Convert direction to single integer for table lookup
589 // 4 = invalid, treat as (0,0,0)
593 u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
595 // Get rotation for things like chests
596 u8 facedir = mn.getFaceDir(ndef);
597 assert(facedir <= 3);
599 static const u8 dir_to_tile[4 * 8] =
601 // 0 +X +Y +Z 0 -Z -Y -X
602 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
603 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
604 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
605 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
607 u8 tileindex = dir_to_tile[facedir*8 + dir_i];
608 return getNodeTileN(mn, p, tileindex, data);
611 static void getTileInfo(
619 v3s16 &face_dir_corrected,
624 VoxelManipulator &vmanip = data->m_vmanip;
625 INodeDefManager *ndef = data->m_gamedef->ndef();
626 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
628 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
629 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
630 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
631 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
634 bool equivalent = false;
635 u8 mf = face_contents(n0.getContent(), n1.getContent(),
650 face_dir_corrected = face_dir;
655 p_corrected = p + face_dir;
656 face_dir_corrected = -face_dir;
659 // eg. water and glass
661 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
663 if(data->m_smooth_lighting == false)
665 lights[0] = lights[1] = lights[2] = lights[3] =
666 getFaceLight(n0, n1, face_dir, data);
670 v3s16 vertex_dirs[4];
671 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
672 for(u16 i=0; i<4; i++)
674 lights[i] = getSmoothLight(
675 blockpos_nodes + p_corrected,
676 vertex_dirs[i], data);
685 translate_dir: unit vector with only one of x, y or z
686 face_dir: unit vector with only one of x, y or z
688 static void updateFastFaceRow(
695 core::array<FastFace> &dest)
699 u16 continuous_tiles_count = 0;
701 bool makes_face = false;
703 v3s16 face_dir_corrected;
704 u16 lights[4] = {0,0,0,0};
706 getTileInfo(data, p, face_dir,
707 makes_face, p_corrected, face_dir_corrected,
710 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
712 // If tiling can be done, this is set to false in the next step
713 bool next_is_different = true;
717 bool next_makes_face = false;
718 v3s16 next_p_corrected;
719 v3s16 next_face_dir_corrected;
720 u16 next_lights[4] = {0,0,0,0};
723 // If at last position, there is nothing to compare to and
724 // the face must be drawn anyway
725 if(j != MAP_BLOCKSIZE - 1)
727 p_next = p + translate_dir;
729 getTileInfo(data, p_next, face_dir,
730 next_makes_face, next_p_corrected,
731 next_face_dir_corrected, next_lights,
734 if(next_makes_face == makes_face
735 && next_p_corrected == p_corrected + translate_dir
736 && next_face_dir_corrected == face_dir_corrected
737 && next_lights[0] == lights[0]
738 && next_lights[1] == lights[1]
739 && next_lights[2] == lights[2]
740 && next_lights[3] == lights[3]
741 && next_tile == tile)
743 next_is_different = false;
747 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
748 next_makes_face != makes_face ? 1 : 0);
749 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
750 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
751 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
752 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
753 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
754 (next_lights[0] != lights[0] ||
755 next_lights[0] != lights[0] ||
756 next_lights[0] != lights[0] ||
757 next_lights[0] != lights[0]) ? 1 : 0);
758 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
759 !(next_tile == tile) ? 1 : 0);
762 /*g_profiler->add("Meshgen: Total faces checked", 1);
764 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
767 g_profiler->add("Meshgen: diff: last position", 1);*/
770 continuous_tiles_count++;
772 // This is set to true if the texture doesn't allow more tiling
773 bool end_of_texture = false;
775 If there is no texture, it can be tiled infinitely.
776 If tiled==0, it means the texture can be tiled infinitely.
777 Otherwise check tiled agains continuous_tiles_count.
779 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
781 if(tile.texture.tiled <= continuous_tiles_count)
782 end_of_texture = true;
785 // Do this to disable tiling textures
786 //end_of_texture = true; //DEBUG
788 if(next_is_different || end_of_texture)
791 Create a face if there should be one
795 // Floating point conversion of the position vector
796 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
797 // Center point of face (kind of)
798 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
799 if(continuous_tiles_count != 1)
800 sp += translate_dir_f;
803 if(translate_dir.X != 0)
805 scale.X = continuous_tiles_count;
807 if(translate_dir.Y != 0)
809 scale.Y = continuous_tiles_count;
811 if(translate_dir.Z != 0)
813 scale.Z = continuous_tiles_count;
816 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
817 sp, face_dir_corrected, scale,
820 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
821 for(int i=1; i<continuous_tiles_count; i++){
822 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
826 continuous_tiles_count = 0;
828 makes_face = next_makes_face;
829 p_corrected = next_p_corrected;
830 face_dir_corrected = next_face_dir_corrected;
831 lights[0] = next_lights[0];
832 lights[1] = next_lights[1];
833 lights[2] = next_lights[2];
834 lights[3] = next_lights[3];
842 static void updateAllFastFaceRows(MeshMakeData *data,
843 core::array<FastFace> &dest)
846 Go through every y,z and get top(y+) faces in rows of x+
848 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
849 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
850 updateFastFaceRow(data,
854 v3s16(0,1,0), //face dir
861 Go through every x,y and get right(x+) faces in rows of z+
863 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
864 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
865 updateFastFaceRow(data,
869 v3s16(1,0,0), //face dir
876 Go through every y,z and get back(z+) faces in rows of x+
878 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
879 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
880 updateFastFaceRow(data,
884 v3s16(0,0,1), //face dir
895 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
896 m_mesh(new scene::SMesh()),
897 m_gamedef(data->m_gamedef),
898 m_animation_force_timer(0), // force initial animation
901 m_last_daynight_ratio((u32) -1),
904 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
905 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
906 //TimeTaker timer1("MapBlockMesh()");
908 core::array<FastFace> fastfaces_new;
911 We are including the faces of the trailing edges of the block.
912 This means that when something changes, the caller must
913 also update the meshes of the blocks at the leading edges.
915 NOTE: This is the slowest part of this method.
918 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
919 //TimeTaker timer2("updateAllFastFaceRows()");
920 updateAllFastFaceRows(data, fastfaces_new);
925 Convert FastFaces to MeshCollector
928 MeshCollector collector;
931 // avg 0ms (100ms spikes when loading textures the first time)
932 // (NOTE: probably outdated)
933 //TimeTaker timer2("MeshCollector building");
935 for(u32 i=0; i<fastfaces_new.size(); i++)
937 FastFace &f = fastfaces_new[i];
939 const u16 indices[] = {0,1,2,2,3,0};
940 const u16 indices_alternate[] = {0,1,3,2,3,1};
942 if(f.tile.texture.atlas == NULL)
945 const u16 *indices_p = indices;
948 Revert triangles for nicer looking gradient if vertices
949 1 and 3 have same color or 0 and 2 have different color.
950 getRed() is the day color.
952 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
953 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
954 indices_p = indices_alternate;
956 collector.append(f.tile, f.vertices, 4, indices_p, 6);
961 Add special graphics:
968 mapblock_mesh_generate_special(data, collector);
972 Convert MeshCollector to SMesh
973 Also store animation info
975 for(u32 i = 0; i < collector.prebuffers.size(); i++)
977 PreMeshBuffer &p = collector.prebuffers[i];
978 /*dstream<<"p.vertices.size()="<<p.vertices.size()
979 <<", p.indices.size()="<<p.indices.size()
982 // Generate animation data
984 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
986 ITextureSource *tsrc = data->m_gamedef->tsrc();
987 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
988 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
989 crack_basename += "^[cracko";
991 crack_basename += "^[crack";
992 m_crack_materials.insert(std::make_pair(i, crack_basename));
994 // - Texture animation
995 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
997 ITextureSource *tsrc = data->m_gamedef->tsrc();
998 // Add to MapBlockMesh in order to animate these tiles
999 m_animation_tiles[i] = p.tile;
1000 m_animation_frames[i] = 0;
1001 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1002 // Get starting position from noise
1003 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1004 data->m_blockpos.X, data->m_blockpos.Y,
1005 data->m_blockpos.Z, 0));
1007 // Play all synchronized
1008 m_animation_frame_offsets[i] = 0;
1010 // Replace tile texture with the first animation frame
1011 std::ostringstream os(std::ios::binary);
1012 os<<tsrc->getTextureName(p.tile.texture.id);
1013 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1014 p.tile.texture = tsrc->getTexture(os.str());
1017 for(u32 j = 0; j < p.vertices.size(); j++)
1019 video::SColor &vc = p.vertices[j].Color;
1020 u8 day = vc.getRed();
1021 u8 night = vc.getGreen();
1022 finalColorBlend(vc, day, night, 1000);
1024 m_daynight_diffs[i][j] = std::make_pair(day, night);
1029 video::SMaterial material;
1030 material.setFlag(video::EMF_LIGHTING, false);
1031 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1032 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1033 material.setFlag(video::EMF_FOG_ENABLE, true);
1034 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1035 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1036 material.MaterialType
1037 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1038 material.setTexture(0, p.tile.texture.atlas);
1039 p.tile.applyMaterialOptions(material);
1041 // Create meshbuffer
1043 // This is a "Standard MeshBuffer",
1044 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1045 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1047 buf->Material = material;
1049 m_mesh->addMeshBuffer(buf);
1052 buf->append(p.vertices.pointer(), p.vertices.size(),
1053 p.indices.pointer(), p.indices.size());
1057 Do some stuff to the mesh
1060 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1061 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1066 // Usually 1-700 faces and 1-7 materials
1067 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1068 <<"and uses "<<m_mesh->getMeshBufferCount()
1069 <<" materials (meshbuffers)"<<std::endl;
1072 // Use VBO for mesh (this just would set this for ever buffer)
1073 // This will lead to infinite memory usage because or irrlicht.
1074 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1077 NOTE: If that is enabled, some kind of a queue to the main
1078 thread should be made which would call irrlicht to delete
1079 the hardware buffer and then delete the mesh
1083 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1085 // Check if animation is required for this mesh
1087 !m_crack_materials.empty() ||
1088 !m_daynight_diffs.empty() ||
1089 !m_animation_tiles.empty();
1092 MapBlockMesh::~MapBlockMesh()
1098 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1100 if(!m_has_animation)
1102 m_animation_force_timer = 100000;
1106 m_animation_force_timer = myrand_range(5, 100);
1109 if(crack != m_last_crack)
1111 for(std::map<u32, std::string>::iterator
1112 i = m_crack_materials.begin();
1113 i != m_crack_materials.end(); i++)
1115 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1116 std::string basename = i->second;
1118 // Create new texture name from original
1119 ITextureSource *tsrc = m_gamedef->getTextureSource();
1120 std::ostringstream os;
1121 os<<basename<<crack;
1122 AtlasPointer ap = tsrc->getTexture(os.str());
1123 buf->getMaterial().setTexture(0, ap.atlas);
1126 m_last_crack = crack;
1129 // Texture animation
1130 for(std::map<u32, TileSpec>::iterator
1131 i = m_animation_tiles.begin();
1132 i != m_animation_tiles.end(); i++)
1134 const TileSpec &tile = i->second;
1135 // Figure out current frame
1136 int frameoffset = m_animation_frame_offsets[i->first];
1137 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1138 + frameoffset) % tile.animation_frame_count;
1139 // If frame doesn't change, skip
1140 if(frame == m_animation_frames[i->first])
1143 m_animation_frames[i->first] = frame;
1145 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1146 ITextureSource *tsrc = m_gamedef->getTextureSource();
1148 // Create new texture name from original
1149 std::ostringstream os(std::ios::binary);
1150 os<<tsrc->getTextureName(tile.texture.id);
1151 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1153 AtlasPointer ap = tsrc->getTexture(os.str());
1154 buf->getMaterial().setTexture(0, ap.atlas);
1157 // Day-night transition
1158 if(daynight_ratio != m_last_daynight_ratio)
1160 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1161 i = m_daynight_diffs.begin();
1162 i != m_daynight_diffs.end(); i++)
1164 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1165 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1166 for(std::map<u32, std::pair<u8, u8 > >::iterator
1167 j = i->second.begin();
1168 j != i->second.end(); j++)
1170 u32 vertexIndex = j->first;
1171 u8 day = j->second.first;
1172 u8 night = j->second.second;
1173 finalColorBlend(vertices[vertexIndex].Color,
1174 day, night, daynight_ratio);
1177 m_last_daynight_ratio = daynight_ratio;
1187 void MeshCollector::append(const TileSpec &tile,
1188 const video::S3DVertex *vertices, u32 numVertices,
1189 const u16 *indices, u32 numIndices)
1191 PreMeshBuffer *p = NULL;
1192 for(u32 i=0; i<prebuffers.size(); i++)
1194 PreMeshBuffer &pp = prebuffers[i];
1206 prebuffers.push_back(pp);
1207 p = &prebuffers[prebuffers.size()-1];
1210 u32 vertex_count = p->vertices.size();
1211 for(u32 i=0; i<numIndices; i++)
1213 u32 j = indices[i] + vertex_count;
1216 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1217 // NOTE: Fix is to just add an another MeshBuffer
1219 p->indices.push_back(j);
1221 for(u32 i=0; i<numVertices; i++)
1223 p->vertices.push_back(vertices[i]);