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"
37 MeshMakeData::MeshMakeData(IGameDef *gamedef):
39 m_blockpos(-1337,-1337,-1337),
40 m_crack_pos_relative(-1337, -1337, -1337),
41 m_smooth_lighting(false),
45 void MeshMakeData::fill(MapBlock *block)
47 m_blockpos = block->getPos();
49 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
55 // Allocate this block + neighbors
57 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
58 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
61 //TimeTaker timer("copy central block data");
65 block->copyTo(m_vmanip);
68 //TimeTaker timer("copy neighbor block data");
72 Copy neighbors. This is lightning fast.
73 Copying only the borders would be *very* slow.
77 Map *map = block->getParent();
79 for(u16 i=0; i<6; i++)
81 const v3s16 &dir = g_6dirs[i];
82 v3s16 bp = m_blockpos + dir;
83 MapBlock *b = map->getBlockNoCreateNoEx(bp);
90 void MeshMakeData::fillSingleNode(MapNode *node)
92 m_blockpos = v3s16(0,0,0);
94 v3s16 blockpos_nodes = v3s16(0,0,0);
95 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
96 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
97 s32 volume = area.getVolume();
98 s32 our_node_index = area.index(1,1,1);
100 // Allocate this block + neighbors
102 m_vmanip.addArea(area);
105 MapNode *data = new MapNode[volume];
106 for(s32 i = 0; i < volume; i++)
108 if(i == our_node_index)
114 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
117 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
121 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
124 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
127 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
129 m_smooth_lighting = smooth_lighting;
133 Light and vertex color functions
137 Calculate non-smooth lighting at interior of node.
140 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
143 INodeDefManager *ndef = data->m_gamedef->ndef();
144 u8 light = n.getLight(bank, ndef);
148 light = undiminish_light(light);
153 light = diminish_light(light);
157 return decode_light(light);
161 Calculate non-smooth lighting at interior of node.
164 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
166 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
167 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
168 return day | (night << 8);
172 Calculate non-smooth lighting at face of node.
175 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
176 v3s16 face_dir, MeshMakeData *data)
178 INodeDefManager *ndef = data->m_gamedef->ndef();
181 u8 l1 = n.getLight(bank, ndef);
182 u8 l2 = n2.getLight(bank, ndef);
188 // Boost light level for light sources
189 u8 light_source = MYMAX(ndef->get(n).light_source,
190 ndef->get(n2).light_source);
191 //if(light_source >= light)
192 //return decode_light(undiminish_light(light_source));
193 if(light_source > light)
194 //return decode_light(light_source);
195 light = light_source;
197 // Make some nice difference to different sides
199 // This makes light come from a corner
200 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
201 light = diminish_light(diminish_light(light));
202 else if(face_dir.X == -1 || face_dir.Z == -1)
203 light = diminish_light(light);*/
205 // All neighboring faces have different shade (like in minecraft)
206 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
207 light = diminish_light(diminish_light(light));
208 else if(face_dir.Z == 1 || face_dir.Z == -1)
209 light = diminish_light(light);
211 return decode_light(light);
215 Calculate non-smooth lighting at face of node.
218 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
220 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
221 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
222 return day | (night << 8);
226 Calculate smooth lighting at the XYZ- corner of p.
229 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
231 static v3s16 dirs8[8] = {
242 INodeDefManager *ndef = data->m_gamedef->ndef();
244 u16 ambient_occlusion = 0;
247 u8 light_source_max = 0;
248 for(u32 i=0; i<8; i++)
250 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
251 const ContentFeatures &f = ndef->get(n);
252 if(f.light_source > light_source_max)
253 light_source_max = f.light_source;
254 // Check f.solidness because fast-style leaves look
256 if(f.param_type == CPT_LIGHT && f.solidness != 2)
258 light += decode_light(n.getLight(bank, ndef));
261 else if(n.getContent() != CONTENT_IGNORE)
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,
345 b += emphase_blue_when_dark[b / 8];
347 // Artificial light is yellow-ish
348 static u8 emphase_yellow_when_artificial[16] = {
349 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
351 rg += emphase_yellow_when_artificial[night/16];
363 Mesh generation helpers
367 vertex_dirs: v3s16[4]
369 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
372 If looked from outside the node towards the face, the corners are:
378 if(dir == v3s16(0,0,1))
380 // If looking towards z+, this is the face that is behind
381 // the center point, facing towards z+.
382 vertex_dirs[0] = v3s16(-1,-1, 1);
383 vertex_dirs[1] = v3s16( 1,-1, 1);
384 vertex_dirs[2] = v3s16( 1, 1, 1);
385 vertex_dirs[3] = v3s16(-1, 1, 1);
387 else if(dir == v3s16(0,0,-1))
390 vertex_dirs[0] = v3s16( 1,-1,-1);
391 vertex_dirs[1] = v3s16(-1,-1,-1);
392 vertex_dirs[2] = v3s16(-1, 1,-1);
393 vertex_dirs[3] = v3s16( 1, 1,-1);
395 else if(dir == v3s16(1,0,0))
398 vertex_dirs[0] = v3s16( 1,-1, 1);
399 vertex_dirs[1] = v3s16( 1,-1,-1);
400 vertex_dirs[2] = v3s16( 1, 1,-1);
401 vertex_dirs[3] = v3s16( 1, 1, 1);
403 else if(dir == v3s16(-1,0,0))
406 vertex_dirs[0] = v3s16(-1,-1,-1);
407 vertex_dirs[1] = v3s16(-1,-1, 1);
408 vertex_dirs[2] = v3s16(-1, 1, 1);
409 vertex_dirs[3] = v3s16(-1, 1,-1);
411 else if(dir == v3s16(0,1,0))
413 // faces towards Y+ (assume Z- as "down" in texture)
414 vertex_dirs[0] = v3s16( 1, 1,-1);
415 vertex_dirs[1] = v3s16(-1, 1,-1);
416 vertex_dirs[2] = v3s16(-1, 1, 1);
417 vertex_dirs[3] = v3s16( 1, 1, 1);
419 else if(dir == v3s16(0,-1,0))
421 // faces towards Y- (assume Z+ as "down" in texture)
422 vertex_dirs[0] = v3s16( 1,-1, 1);
423 vertex_dirs[1] = v3s16(-1,-1, 1);
424 vertex_dirs[2] = v3s16(-1,-1,-1);
425 vertex_dirs[3] = v3s16( 1,-1,-1);
432 video::S3DVertex vertices[4]; // Precalculated vertices
435 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
436 v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
440 // Position is at the center of the cube.
444 v3s16 vertex_dirs[4];
445 getNodeVertexDirs(dir, vertex_dirs);
446 for(u16 i=0; i<4; i++)
449 BS/2*vertex_dirs[i].X,
450 BS/2*vertex_dirs[i].Y,
451 BS/2*vertex_dirs[i].Z
455 for(u16 i=0; i<4; i++)
457 vertex_pos[i].X *= scale.X;
458 vertex_pos[i].Y *= scale.Y;
459 vertex_pos[i].Z *= scale.Z;
460 vertex_pos[i] += pos;
464 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
465 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
466 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
468 v3f normal(dir.X, dir.Y, dir.Z);
470 u8 alpha = tile.alpha;
472 float x0 = tile.texture.pos.X;
473 float y0 = tile.texture.pos.Y;
474 float w = tile.texture.size.X;
475 float h = tile.texture.size.Y;
477 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
478 MapBlock_LightColor(alpha, li0),
479 core::vector2d<f32>(x0+w*abs_scale, y0+h));
480 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
481 MapBlock_LightColor(alpha, li1),
482 core::vector2d<f32>(x0, y0+h));
483 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
484 MapBlock_LightColor(alpha, li2),
485 core::vector2d<f32>(x0, y0));
486 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
487 MapBlock_LightColor(alpha, li3),
488 core::vector2d<f32>(x0+w*abs_scale, y0));
492 dest.push_back(face);
496 Nodes make a face if contents differ and solidness differs.
499 1: Face uses m1's content
500 2: Face uses m2's content
501 equivalent: Whether the blocks share the same face (eg. water and glass)
503 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
505 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
506 INodeDefManager *ndef)
510 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
513 bool contents_differ = (m1 != m2);
515 const ContentFeatures &f1 = ndef->get(m1);
516 const ContentFeatures &f2 = ndef->get(m2);
518 // Contents don't differ for different forms of same liquid
519 if(f1.sameLiquid(f2))
520 contents_differ = false;
522 u8 c1 = f1.solidness;
523 u8 c2 = f2.solidness;
525 bool solidness_differs = (c1 != c2);
526 bool makes_face = contents_differ && solidness_differs;
528 if(makes_face == false)
532 c1 = f1.visual_solidness;
534 c2 = f2.visual_solidness;
538 // If same solidness, liquid takes precense
552 Gets nth node tile (0 <= n <= 5).
554 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
556 INodeDefManager *ndef = data->m_gamedef->ndef();
557 TileSpec spec = ndef->get(mn).tiles[tileindex];
558 // Apply temporary crack
559 if(p == data->m_crack_pos_relative)
561 spec.material_flags |= MATERIAL_FLAG_CRACK;
562 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
564 // If animated, replace tile texture with one without texture atlas
565 if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
567 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
573 Gets node tile given a face direction.
575 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
577 INodeDefManager *ndef = data->m_gamedef->ndef();
579 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
580 // (0,0,1), (0,0,-1) or (0,0,0)
581 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
583 // Convert direction to single integer for table lookup
588 // 4 = invalid, treat as (0,0,0)
592 u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
594 // Get rotation for things like chests
595 u8 facedir = mn.getFaceDir(ndef);
596 assert(facedir <= 3);
598 static const u8 dir_to_tile[4 * 8] =
600 // 0 +X +Y +Z 0 -Z -Y -X
601 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
602 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
603 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
604 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
606 u8 tileindex = dir_to_tile[facedir*8 + dir_i];
607 return getNodeTileN(mn, p, tileindex, data);
610 static void getTileInfo(
618 v3s16 &face_dir_corrected,
623 VoxelManipulator &vmanip = data->m_vmanip;
624 INodeDefManager *ndef = data->m_gamedef->ndef();
625 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
627 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
628 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
629 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
630 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
633 bool equivalent = false;
634 u8 mf = face_contents(n0.getContent(), n1.getContent(),
649 face_dir_corrected = face_dir;
654 p_corrected = p + face_dir;
655 face_dir_corrected = -face_dir;
658 // eg. water and glass
660 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
662 if(data->m_smooth_lighting == false)
664 lights[0] = lights[1] = lights[2] = lights[3] =
665 getFaceLight(n0, n1, face_dir, data);
669 v3s16 vertex_dirs[4];
670 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
671 for(u16 i=0; i<4; i++)
673 lights[i] = getSmoothLight(
674 blockpos_nodes + p_corrected,
675 vertex_dirs[i], data);
684 translate_dir: unit vector with only one of x, y or z
685 face_dir: unit vector with only one of x, y or z
687 static void updateFastFaceRow(
694 core::array<FastFace> &dest)
698 u16 continuous_tiles_count = 0;
700 bool makes_face = false;
702 v3s16 face_dir_corrected;
703 u16 lights[4] = {0,0,0,0};
705 getTileInfo(data, p, face_dir,
706 makes_face, p_corrected, face_dir_corrected,
709 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
711 // If tiling can be done, this is set to false in the next step
712 bool next_is_different = true;
716 bool next_makes_face = false;
717 v3s16 next_p_corrected;
718 v3s16 next_face_dir_corrected;
719 u16 next_lights[4] = {0,0,0,0};
722 // If at last position, there is nothing to compare to and
723 // the face must be drawn anyway
724 if(j != MAP_BLOCKSIZE - 1)
726 p_next = p + translate_dir;
728 getTileInfo(data, p_next, face_dir,
729 next_makes_face, next_p_corrected,
730 next_face_dir_corrected, next_lights,
733 if(next_makes_face == makes_face
734 && next_p_corrected == p_corrected + translate_dir
735 && next_face_dir_corrected == face_dir_corrected
736 && next_lights[0] == lights[0]
737 && next_lights[1] == lights[1]
738 && next_lights[2] == lights[2]
739 && next_lights[3] == lights[3]
740 && next_tile == tile)
742 next_is_different = false;
746 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
747 next_makes_face != makes_face ? 1 : 0);
748 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
749 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
750 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
751 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
752 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
753 (next_lights[0] != lights[0] ||
754 next_lights[0] != lights[0] ||
755 next_lights[0] != lights[0] ||
756 next_lights[0] != lights[0]) ? 1 : 0);
757 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
758 !(next_tile == tile) ? 1 : 0);
761 /*g_profiler->add("Meshgen: Total faces checked", 1);
763 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
766 g_profiler->add("Meshgen: diff: last position", 1);*/
769 continuous_tiles_count++;
771 // This is set to true if the texture doesn't allow more tiling
772 bool end_of_texture = false;
774 If there is no texture, it can be tiled infinitely.
775 If tiled==0, it means the texture can be tiled infinitely.
776 Otherwise check tiled agains continuous_tiles_count.
778 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
780 if(tile.texture.tiled <= continuous_tiles_count)
781 end_of_texture = true;
784 // Do this to disable tiling textures
785 //end_of_texture = true; //DEBUG
787 if(next_is_different || end_of_texture)
790 Create a face if there should be one
794 // Floating point conversion of the position vector
795 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
796 // Center point of face (kind of)
797 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
798 if(continuous_tiles_count != 1)
799 sp += translate_dir_f;
802 if(translate_dir.X != 0)
804 scale.X = continuous_tiles_count;
806 if(translate_dir.Y != 0)
808 scale.Y = continuous_tiles_count;
810 if(translate_dir.Z != 0)
812 scale.Z = continuous_tiles_count;
815 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
816 sp, face_dir_corrected, scale,
819 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
820 for(int i=1; i<continuous_tiles_count; i++){
821 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
825 continuous_tiles_count = 0;
827 makes_face = next_makes_face;
828 p_corrected = next_p_corrected;
829 face_dir_corrected = next_face_dir_corrected;
830 lights[0] = next_lights[0];
831 lights[1] = next_lights[1];
832 lights[2] = next_lights[2];
833 lights[3] = next_lights[3];
841 static void updateAllFastFaceRows(MeshMakeData *data,
842 core::array<FastFace> &dest)
845 Go through every y,z and get top(y+) faces in rows of x+
847 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
848 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
849 updateFastFaceRow(data,
853 v3s16(0,1,0), //face dir
860 Go through every x,y and get right(x+) faces in rows of z+
862 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
863 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
864 updateFastFaceRow(data,
868 v3s16(1,0,0), //face dir
875 Go through every y,z and get back(z+) faces in rows of x+
877 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
878 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
879 updateFastFaceRow(data,
883 v3s16(0,0,1), //face dir
894 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
895 m_mesh(new scene::SMesh()),
896 m_gamedef(data->m_gamedef),
897 m_animation_force_timer(0), // force initial animation
900 m_last_daynight_ratio((u32) -1),
903 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
904 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
905 //TimeTaker timer1("MapBlockMesh()");
907 core::array<FastFace> fastfaces_new;
910 We are including the faces of the trailing edges of the block.
911 This means that when something changes, the caller must
912 also update the meshes of the blocks at the leading edges.
914 NOTE: This is the slowest part of this method.
917 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
918 //TimeTaker timer2("updateAllFastFaceRows()");
919 updateAllFastFaceRows(data, fastfaces_new);
924 Convert FastFaces to MeshCollector
927 MeshCollector collector;
930 // avg 0ms (100ms spikes when loading textures the first time)
931 // (NOTE: probably outdated)
932 //TimeTaker timer2("MeshCollector building");
934 for(u32 i=0; i<fastfaces_new.size(); i++)
936 FastFace &f = fastfaces_new[i];
938 const u16 indices[] = {0,1,2,2,3,0};
939 const u16 indices_alternate[] = {0,1,3,2,3,1};
941 if(f.tile.texture.atlas == NULL)
944 const u16 *indices_p = indices;
947 Revert triangles for nicer looking gradient if vertices
948 1 and 3 have same color or 0 and 2 have different color.
949 getRed() is the day color.
951 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
952 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
953 indices_p = indices_alternate;
955 collector.append(f.tile, f.vertices, 4, indices_p, 6);
960 Add special graphics:
967 mapblock_mesh_generate_special(data, collector);
971 Convert MeshCollector to SMesh
972 Also store animation info
974 for(u32 i = 0; i < collector.prebuffers.size(); i++)
976 PreMeshBuffer &p = collector.prebuffers[i];
977 /*dstream<<"p.vertices.size()="<<p.vertices.size()
978 <<", p.indices.size()="<<p.indices.size()
981 // Generate animation data
983 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
985 ITextureSource *tsrc = data->m_gamedef->tsrc();
986 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
987 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
988 crack_basename += "^[cracko";
990 crack_basename += "^[crack";
991 m_crack_materials.insert(std::make_pair(i, crack_basename));
993 // - Texture animation
994 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
996 ITextureSource *tsrc = data->m_gamedef->tsrc();
997 // Add to MapBlockMesh in order to animate these tiles
998 m_animation_tiles[i] = p.tile;
999 m_animation_frames[i] = 0;
1000 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1001 // Get starting position from noise
1002 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1003 data->m_blockpos.X, data->m_blockpos.Y,
1004 data->m_blockpos.Z, 0));
1006 // Play all synchronized
1007 m_animation_frame_offsets[i] = 0;
1009 // Replace tile texture with the first animation frame
1010 std::ostringstream os(std::ios::binary);
1011 os<<tsrc->getTextureName(p.tile.texture.id);
1012 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1013 p.tile.texture = tsrc->getTexture(os.str());
1016 for(u32 j = 0; j < p.vertices.size(); j++)
1018 video::SColor &vc = p.vertices[j].Color;
1019 u8 day = vc.getRed();
1020 u8 night = vc.getGreen();
1021 finalColorBlend(vc, day, night, 1000);
1023 m_daynight_diffs[i][j] = std::make_pair(day, night);
1028 video::SMaterial material;
1029 material.setFlag(video::EMF_LIGHTING, false);
1030 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1031 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1032 material.setFlag(video::EMF_FOG_ENABLE, true);
1033 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1034 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1035 material.MaterialType
1036 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1037 material.setTexture(0, p.tile.texture.atlas);
1038 p.tile.applyMaterialOptions(material);
1040 // Create meshbuffer
1042 // This is a "Standard MeshBuffer",
1043 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1044 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1046 buf->Material = material;
1048 m_mesh->addMeshBuffer(buf);
1051 buf->append(p.vertices.pointer(), p.vertices.size(),
1052 p.indices.pointer(), p.indices.size());
1056 Do some stuff to the mesh
1059 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1060 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1065 // Usually 1-700 faces and 1-7 materials
1066 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1067 <<"and uses "<<m_mesh->getMeshBufferCount()
1068 <<" materials (meshbuffers)"<<std::endl;
1071 // Use VBO for mesh (this just would set this for ever buffer)
1072 // This will lead to infinite memory usage because or irrlicht.
1073 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1076 NOTE: If that is enabled, some kind of a queue to the main
1077 thread should be made which would call irrlicht to delete
1078 the hardware buffer and then delete the mesh
1082 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1084 // Check if animation is required for this mesh
1086 !m_crack_materials.empty() ||
1087 !m_daynight_diffs.empty() ||
1088 !m_animation_tiles.empty();
1091 MapBlockMesh::~MapBlockMesh()
1097 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1099 if(!m_has_animation)
1101 m_animation_force_timer = 100000;
1105 m_animation_force_timer = myrand_range(5, 100);
1108 if(crack != m_last_crack)
1110 for(std::map<u32, std::string>::iterator
1111 i = m_crack_materials.begin();
1112 i != m_crack_materials.end(); i++)
1114 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1115 std::string basename = i->second;
1117 // Create new texture name from original
1118 ITextureSource *tsrc = m_gamedef->getTextureSource();
1119 std::ostringstream os;
1120 os<<basename<<crack;
1121 AtlasPointer ap = tsrc->getTexture(os.str());
1122 buf->getMaterial().setTexture(0, ap.atlas);
1125 m_last_crack = crack;
1128 // Texture animation
1129 for(std::map<u32, TileSpec>::iterator
1130 i = m_animation_tiles.begin();
1131 i != m_animation_tiles.end(); i++)
1133 const TileSpec &tile = i->second;
1134 // Figure out current frame
1135 int frameoffset = m_animation_frame_offsets[i->first];
1136 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1137 + frameoffset) % tile.animation_frame_count;
1138 // If frame doesn't change, skip
1139 if(frame == m_animation_frames[i->first])
1142 m_animation_frames[i->first] = frame;
1144 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1145 ITextureSource *tsrc = m_gamedef->getTextureSource();
1147 // Create new texture name from original
1148 std::ostringstream os(std::ios::binary);
1149 os<<tsrc->getTextureName(tile.texture.id);
1150 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1152 AtlasPointer ap = tsrc->getTexture(os.str());
1153 buf->getMaterial().setTexture(0, ap.atlas);
1156 // Day-night transition
1157 if(daynight_ratio != m_last_daynight_ratio)
1159 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1160 i = m_daynight_diffs.begin();
1161 i != m_daynight_diffs.end(); i++)
1163 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1164 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1165 for(std::map<u32, std::pair<u8, u8 > >::iterator
1166 j = i->second.begin();
1167 j != i->second.end(); j++)
1169 u32 vertexIndex = j->first;
1170 u8 day = j->second.first;
1171 u8 night = j->second.second;
1172 finalColorBlend(vertices[vertexIndex].Color,
1173 day, night, daynight_ratio);
1176 m_last_daynight_ratio = daynight_ratio;
1186 void MeshCollector::append(const TileSpec &tile,
1187 const video::S3DVertex *vertices, u32 numVertices,
1188 const u16 *indices, u32 numIndices)
1190 PreMeshBuffer *p = NULL;
1191 for(u32 i=0; i<prebuffers.size(); i++)
1193 PreMeshBuffer &pp = prebuffers[i];
1205 prebuffers.push_back(pp);
1206 p = &prebuffers[prebuffers.size()-1];
1209 u32 vertex_count = p->vertices.size();
1210 for(u32 i=0; i<numIndices; i++)
1212 u32 j = indices[i] + vertex_count;
1215 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1216 // NOTE: Fix is to just add an another MeshBuffer
1218 p->indices.push_back(j);
1220 for(u32 i=0; i<numVertices; i++)
1222 p->vertices.push_back(vertices[i]);