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<26; i++)
82 const v3s16 &dir = g_26dirs[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];
609 // If not rotated or is side tile, we're done
610 if(facedir == 0 || (tileindex != 0 && tileindex != 1))
611 return getNodeTileN(mn, p, tileindex, data);
613 // This is the top or bottom tile, and it shall be rotated; thus rotate it
614 TileSpec spec = getNodeTileN(mn, p, tileindex, data);
616 if(facedir == 1){ // -90
617 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
618 name += "^[transformR270";
619 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
621 else if(facedir == 2){ // 180
622 spec.texture.pos += spec.texture.size;
623 spec.texture.size *= -1;
625 else if(facedir == 3){ // 90
626 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
627 name += "^[transformR90";
628 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
631 else if(tileindex == 1){
632 if(facedir == 1){ // -90
633 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
634 name += "^[transformR90";
635 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
637 else if(facedir == 2){ // 180
638 spec.texture.pos += spec.texture.size;
639 spec.texture.size *= -1;
641 else if(facedir == 3){ // 90
642 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
643 name += "^[transformR270";
644 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
650 static void getTileInfo(
658 v3s16 &face_dir_corrected,
663 VoxelManipulator &vmanip = data->m_vmanip;
664 INodeDefManager *ndef = data->m_gamedef->ndef();
665 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
667 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
668 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
669 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
670 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
673 bool equivalent = false;
674 u8 mf = face_contents(n0.getContent(), n1.getContent(),
689 face_dir_corrected = face_dir;
694 p_corrected = p + face_dir;
695 face_dir_corrected = -face_dir;
698 // eg. water and glass
700 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
702 if(data->m_smooth_lighting == false)
704 lights[0] = lights[1] = lights[2] = lights[3] =
705 getFaceLight(n0, n1, face_dir, data);
709 v3s16 vertex_dirs[4];
710 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
711 for(u16 i=0; i<4; i++)
713 lights[i] = getSmoothLight(
714 blockpos_nodes + p_corrected,
715 vertex_dirs[i], data);
724 translate_dir: unit vector with only one of x, y or z
725 face_dir: unit vector with only one of x, y or z
727 static void updateFastFaceRow(
734 core::array<FastFace> &dest)
738 u16 continuous_tiles_count = 0;
740 bool makes_face = false;
742 v3s16 face_dir_corrected;
743 u16 lights[4] = {0,0,0,0};
745 getTileInfo(data, p, face_dir,
746 makes_face, p_corrected, face_dir_corrected,
749 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
751 // If tiling can be done, this is set to false in the next step
752 bool next_is_different = true;
756 bool next_makes_face = false;
757 v3s16 next_p_corrected;
758 v3s16 next_face_dir_corrected;
759 u16 next_lights[4] = {0,0,0,0};
762 // If at last position, there is nothing to compare to and
763 // the face must be drawn anyway
764 if(j != MAP_BLOCKSIZE - 1)
766 p_next = p + translate_dir;
768 getTileInfo(data, p_next, face_dir,
769 next_makes_face, next_p_corrected,
770 next_face_dir_corrected, next_lights,
773 if(next_makes_face == makes_face
774 && next_p_corrected == p_corrected + translate_dir
775 && next_face_dir_corrected == face_dir_corrected
776 && next_lights[0] == lights[0]
777 && next_lights[1] == lights[1]
778 && next_lights[2] == lights[2]
779 && next_lights[3] == lights[3]
780 && next_tile == tile)
782 next_is_different = false;
786 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
787 next_makes_face != makes_face ? 1 : 0);
788 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
789 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
790 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
791 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
792 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
793 (next_lights[0] != lights[0] ||
794 next_lights[0] != lights[0] ||
795 next_lights[0] != lights[0] ||
796 next_lights[0] != lights[0]) ? 1 : 0);
797 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
798 !(next_tile == tile) ? 1 : 0);
801 /*g_profiler->add("Meshgen: Total faces checked", 1);
803 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
806 g_profiler->add("Meshgen: diff: last position", 1);*/
809 continuous_tiles_count++;
811 // This is set to true if the texture doesn't allow more tiling
812 bool end_of_texture = false;
814 If there is no texture, it can be tiled infinitely.
815 If tiled==0, it means the texture can be tiled infinitely.
816 Otherwise check tiled agains continuous_tiles_count.
818 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
820 if(tile.texture.tiled <= continuous_tiles_count)
821 end_of_texture = true;
824 // Do this to disable tiling textures
825 //end_of_texture = true; //DEBUG
827 if(next_is_different || end_of_texture)
830 Create a face if there should be one
834 // Floating point conversion of the position vector
835 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
836 // Center point of face (kind of)
837 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
838 if(continuous_tiles_count != 1)
839 sp += translate_dir_f;
842 if(translate_dir.X != 0)
844 scale.X = continuous_tiles_count;
846 if(translate_dir.Y != 0)
848 scale.Y = continuous_tiles_count;
850 if(translate_dir.Z != 0)
852 scale.Z = continuous_tiles_count;
855 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
856 sp, face_dir_corrected, scale,
859 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
860 for(int i=1; i<continuous_tiles_count; i++){
861 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
865 continuous_tiles_count = 0;
867 makes_face = next_makes_face;
868 p_corrected = next_p_corrected;
869 face_dir_corrected = next_face_dir_corrected;
870 lights[0] = next_lights[0];
871 lights[1] = next_lights[1];
872 lights[2] = next_lights[2];
873 lights[3] = next_lights[3];
881 static void updateAllFastFaceRows(MeshMakeData *data,
882 core::array<FastFace> &dest)
885 Go through every y,z and get top(y+) faces in rows of x+
887 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
888 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
889 updateFastFaceRow(data,
893 v3s16(0,1,0), //face dir
900 Go through every x,y and get right(x+) faces in rows of z+
902 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
903 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
904 updateFastFaceRow(data,
908 v3s16(1,0,0), //face dir
915 Go through every y,z and get back(z+) faces in rows of x+
917 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
918 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
919 updateFastFaceRow(data,
923 v3s16(0,0,1), //face dir
934 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
935 m_mesh(new scene::SMesh()),
936 m_gamedef(data->m_gamedef),
937 m_animation_force_timer(0), // force initial animation
940 m_last_daynight_ratio((u32) -1),
943 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
944 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
945 //TimeTaker timer1("MapBlockMesh()");
947 core::array<FastFace> fastfaces_new;
950 We are including the faces of the trailing edges of the block.
951 This means that when something changes, the caller must
952 also update the meshes of the blocks at the leading edges.
954 NOTE: This is the slowest part of this method.
957 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
958 //TimeTaker timer2("updateAllFastFaceRows()");
959 updateAllFastFaceRows(data, fastfaces_new);
964 Convert FastFaces to MeshCollector
967 MeshCollector collector;
970 // avg 0ms (100ms spikes when loading textures the first time)
971 // (NOTE: probably outdated)
972 //TimeTaker timer2("MeshCollector building");
974 for(u32 i=0; i<fastfaces_new.size(); i++)
976 FastFace &f = fastfaces_new[i];
978 const u16 indices[] = {0,1,2,2,3,0};
979 const u16 indices_alternate[] = {0,1,3,2,3,1};
981 if(f.tile.texture.atlas == NULL)
984 const u16 *indices_p = indices;
987 Revert triangles for nicer looking gradient if vertices
988 1 and 3 have same color or 0 and 2 have different color.
989 getRed() is the day color.
991 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
992 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
993 indices_p = indices_alternate;
995 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1000 Add special graphics:
1007 mapblock_mesh_generate_special(data, collector);
1011 Convert MeshCollector to SMesh
1012 Also store animation info
1014 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1016 PreMeshBuffer &p = collector.prebuffers[i];
1017 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1018 <<", p.indices.size()="<<p.indices.size()
1021 // Generate animation data
1023 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1025 ITextureSource *tsrc = data->m_gamedef->tsrc();
1026 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
1027 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1028 crack_basename += "^[cracko";
1030 crack_basename += "^[crack";
1031 m_crack_materials.insert(std::make_pair(i, crack_basename));
1033 // - Texture animation
1034 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1036 ITextureSource *tsrc = data->m_gamedef->tsrc();
1037 // Add to MapBlockMesh in order to animate these tiles
1038 m_animation_tiles[i] = p.tile;
1039 m_animation_frames[i] = 0;
1040 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1041 // Get starting position from noise
1042 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1043 data->m_blockpos.X, data->m_blockpos.Y,
1044 data->m_blockpos.Z, 0));
1046 // Play all synchronized
1047 m_animation_frame_offsets[i] = 0;
1049 // Replace tile texture with the first animation frame
1050 std::ostringstream os(std::ios::binary);
1051 os<<tsrc->getTextureName(p.tile.texture.id);
1052 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1053 p.tile.texture = tsrc->getTexture(os.str());
1056 for(u32 j = 0; j < p.vertices.size(); j++)
1058 video::SColor &vc = p.vertices[j].Color;
1059 u8 day = vc.getRed();
1060 u8 night = vc.getGreen();
1061 finalColorBlend(vc, day, night, 1000);
1063 m_daynight_diffs[i][j] = std::make_pair(day, night);
1068 video::SMaterial material;
1069 material.setFlag(video::EMF_LIGHTING, false);
1070 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1071 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1072 material.setFlag(video::EMF_FOG_ENABLE, true);
1073 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1074 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1075 material.MaterialType
1076 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1077 material.setTexture(0, p.tile.texture.atlas);
1078 p.tile.applyMaterialOptions(material);
1080 // Create meshbuffer
1082 // This is a "Standard MeshBuffer",
1083 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1084 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1086 buf->Material = material;
1088 m_mesh->addMeshBuffer(buf);
1091 buf->append(p.vertices.pointer(), p.vertices.size(),
1092 p.indices.pointer(), p.indices.size());
1096 Do some stuff to the mesh
1099 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1100 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1105 // Usually 1-700 faces and 1-7 materials
1106 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1107 <<"and uses "<<m_mesh->getMeshBufferCount()
1108 <<" materials (meshbuffers)"<<std::endl;
1111 // Use VBO for mesh (this just would set this for ever buffer)
1112 // This will lead to infinite memory usage because or irrlicht.
1113 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1116 NOTE: If that is enabled, some kind of a queue to the main
1117 thread should be made which would call irrlicht to delete
1118 the hardware buffer and then delete the mesh
1122 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1124 // Check if animation is required for this mesh
1126 !m_crack_materials.empty() ||
1127 !m_daynight_diffs.empty() ||
1128 !m_animation_tiles.empty();
1131 MapBlockMesh::~MapBlockMesh()
1137 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1139 if(!m_has_animation)
1141 m_animation_force_timer = 100000;
1145 m_animation_force_timer = myrand_range(5, 100);
1148 if(crack != m_last_crack)
1150 for(std::map<u32, std::string>::iterator
1151 i = m_crack_materials.begin();
1152 i != m_crack_materials.end(); i++)
1154 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1155 std::string basename = i->second;
1157 // Create new texture name from original
1158 ITextureSource *tsrc = m_gamedef->getTextureSource();
1159 std::ostringstream os;
1160 os<<basename<<crack;
1161 AtlasPointer ap = tsrc->getTexture(os.str());
1162 buf->getMaterial().setTexture(0, ap.atlas);
1165 m_last_crack = crack;
1168 // Texture animation
1169 for(std::map<u32, TileSpec>::iterator
1170 i = m_animation_tiles.begin();
1171 i != m_animation_tiles.end(); i++)
1173 const TileSpec &tile = i->second;
1174 // Figure out current frame
1175 int frameoffset = m_animation_frame_offsets[i->first];
1176 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1177 + frameoffset) % tile.animation_frame_count;
1178 // If frame doesn't change, skip
1179 if(frame == m_animation_frames[i->first])
1182 m_animation_frames[i->first] = frame;
1184 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1185 ITextureSource *tsrc = m_gamedef->getTextureSource();
1187 // Create new texture name from original
1188 std::ostringstream os(std::ios::binary);
1189 os<<tsrc->getTextureName(tile.texture.id);
1190 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1192 AtlasPointer ap = tsrc->getTexture(os.str());
1193 buf->getMaterial().setTexture(0, ap.atlas);
1196 // Day-night transition
1197 if(daynight_ratio != m_last_daynight_ratio)
1199 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1200 i = m_daynight_diffs.begin();
1201 i != m_daynight_diffs.end(); i++)
1203 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1204 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1205 for(std::map<u32, std::pair<u8, u8 > >::iterator
1206 j = i->second.begin();
1207 j != i->second.end(); j++)
1209 u32 vertexIndex = j->first;
1210 u8 day = j->second.first;
1211 u8 night = j->second.second;
1212 finalColorBlend(vertices[vertexIndex].Color,
1213 day, night, daynight_ratio);
1216 m_last_daynight_ratio = daynight_ratio;
1226 void MeshCollector::append(const TileSpec &tile,
1227 const video::S3DVertex *vertices, u32 numVertices,
1228 const u16 *indices, u32 numIndices)
1230 PreMeshBuffer *p = NULL;
1231 for(u32 i=0; i<prebuffers.size(); i++)
1233 PreMeshBuffer &pp = prebuffers[i];
1245 prebuffers.push_back(pp);
1246 p = &prebuffers[prebuffers.size()-1];
1249 u32 vertex_count = p->vertices.size();
1250 for(u32 i=0; i<numIndices; i++)
1252 u32 j = indices[i] + vertex_count;
1255 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1256 // NOTE: Fix is to just add an another MeshBuffer
1258 p->indices.push_back(j);
1260 for(u32 i=0; i<numVertices; i++)
1262 p->vertices.push_back(vertices[i]);