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"
33 #include "util/directiontables.h"
39 MeshMakeData::MeshMakeData(IGameDef *gamedef):
41 m_blockpos(-1337,-1337,-1337),
42 m_crack_pos_relative(-1337, -1337, -1337),
43 m_smooth_lighting(false),
47 void MeshMakeData::fill(MapBlock *block)
49 m_blockpos = block->getPos();
51 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
57 // Allocate this block + neighbors
59 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
60 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
63 //TimeTaker timer("copy central block data");
67 block->copyTo(m_vmanip);
70 //TimeTaker timer("copy neighbor block data");
74 Copy neighbors. This is lightning fast.
75 Copying only the borders would be *very* slow.
79 Map *map = block->getParent();
81 for(u16 i=0; i<26; i++)
83 const v3s16 &dir = g_26dirs[i];
84 v3s16 bp = m_blockpos + dir;
85 MapBlock *b = map->getBlockNoCreateNoEx(bp);
92 void MeshMakeData::fillSingleNode(MapNode *node)
94 m_blockpos = v3s16(0,0,0);
96 v3s16 blockpos_nodes = v3s16(0,0,0);
97 VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
98 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
99 s32 volume = area.getVolume();
100 s32 our_node_index = area.index(1,1,1);
102 // Allocate this block + neighbors
104 m_vmanip.addArea(area);
107 MapNode *data = new MapNode[volume];
108 for(s32 i = 0; i < volume; i++)
110 if(i == our_node_index)
116 data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
119 m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
123 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
126 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
129 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
131 m_smooth_lighting = smooth_lighting;
135 Light and vertex color functions
139 Calculate non-smooth lighting at interior of node.
142 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
145 INodeDefManager *ndef = data->m_gamedef->ndef();
146 u8 light = n.getLight(bank, ndef);
150 light = undiminish_light(light);
155 light = diminish_light(light);
159 return decode_light(light);
163 Calculate non-smooth lighting at interior of node.
166 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
168 u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
169 u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
170 return day | (night << 8);
174 Calculate non-smooth lighting at face of node.
177 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
178 v3s16 face_dir, MeshMakeData *data)
180 INodeDefManager *ndef = data->m_gamedef->ndef();
183 u8 l1 = n.getLight(bank, ndef);
184 u8 l2 = n2.getLight(bank, ndef);
190 // Boost light level for light sources
191 u8 light_source = MYMAX(ndef->get(n).light_source,
192 ndef->get(n2).light_source);
193 //if(light_source >= light)
194 //return decode_light(undiminish_light(light_source));
195 if(light_source > light)
196 //return decode_light(light_source);
197 light = light_source;
199 // Make some nice difference to different sides
201 // This makes light come from a corner
202 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
203 light = diminish_light(diminish_light(light));
204 else if(face_dir.X == -1 || face_dir.Z == -1)
205 light = diminish_light(light);*/
207 // All neighboring faces have different shade (like in minecraft)
208 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
209 light = diminish_light(diminish_light(light));
210 else if(face_dir.Z == 1 || face_dir.Z == -1)
211 light = diminish_light(light);
213 return decode_light(light);
217 Calculate non-smooth lighting at face of node.
220 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
222 u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
223 u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
224 return day | (night << 8);
228 Calculate smooth lighting at the XYZ- corner of p.
231 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
233 static v3s16 dirs8[8] = {
244 INodeDefManager *ndef = data->m_gamedef->ndef();
246 u16 ambient_occlusion = 0;
249 u8 light_source_max = 0;
250 for(u32 i=0; i<8; i++)
252 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
253 const ContentFeatures &f = ndef->get(n);
254 if(f.light_source > light_source_max)
255 light_source_max = f.light_source;
256 // Check f.solidness because fast-style leaves look
258 if(f.param_type == CPT_LIGHT && f.solidness != 2)
260 light += decode_light(n.getLight(bank, ndef));
263 else if(n.getContent() != CONTENT_IGNORE)
272 light /= light_count;
274 // Boost brightness around light sources
275 if(decode_light(light_source_max) >= light)
276 //return decode_light(undiminish_light(light_source_max));
277 return decode_light(light_source_max);
279 if(ambient_occlusion > 4)
281 //ambient_occlusion -= 4;
282 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
283 float light_amount = (8 - ambient_occlusion) / 4.0;
284 float light_f = (float)light / 255.0;
285 light_f = pow(light_f, 2.2f); // gamma -> linear space
286 light_f = light_f * light_amount;
287 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
290 light = 255.0 * light_f + 0.5;
297 Calculate smooth lighting at the XYZ- corner of p.
300 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
302 u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
303 u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
304 return day | (night << 8);
308 Calculate smooth lighting at the given corner of p.
311 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
313 if(corner.X == 1) p.X += 1;
314 else assert(corner.X == -1);
315 if(corner.Y == 1) p.Y += 1;
316 else assert(corner.Y == -1);
317 if(corner.Z == 1) p.Z += 1;
318 else assert(corner.Z == -1);
320 return getSmoothLight(p, data);
324 Converts from day + night color values (0..255)
325 and a given daynight_ratio to the final SColor shown on screen.
327 static void finalColorBlend(video::SColor& result,
328 u8 day, u8 night, u32 daynight_ratio)
330 s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
334 b += (day - night) / 13;
335 rg -= (day - night) / 23;
337 // Emphase blue a bit in darker places
338 // Each entry of this array represents a range of 8 blue levels
339 static u8 emphase_blue_when_dark[32] = {
340 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
341 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
347 b += emphase_blue_when_dark[b / 8];
349 // Artificial light is yellow-ish
350 static u8 emphase_yellow_when_artificial[16] = {
351 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
353 rg += emphase_yellow_when_artificial[night/16];
365 Mesh generation helpers
369 vertex_dirs: v3s16[4]
371 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
374 If looked from outside the node towards the face, the corners are:
380 if(dir == v3s16(0,0,1))
382 // If looking towards z+, this is the face that is behind
383 // the center point, facing towards z+.
384 vertex_dirs[0] = v3s16(-1,-1, 1);
385 vertex_dirs[1] = v3s16( 1,-1, 1);
386 vertex_dirs[2] = v3s16( 1, 1, 1);
387 vertex_dirs[3] = v3s16(-1, 1, 1);
389 else if(dir == v3s16(0,0,-1))
392 vertex_dirs[0] = v3s16( 1,-1,-1);
393 vertex_dirs[1] = v3s16(-1,-1,-1);
394 vertex_dirs[2] = v3s16(-1, 1,-1);
395 vertex_dirs[3] = v3s16( 1, 1,-1);
397 else if(dir == v3s16(1,0,0))
400 vertex_dirs[0] = v3s16( 1,-1, 1);
401 vertex_dirs[1] = v3s16( 1,-1,-1);
402 vertex_dirs[2] = v3s16( 1, 1,-1);
403 vertex_dirs[3] = v3s16( 1, 1, 1);
405 else if(dir == v3s16(-1,0,0))
408 vertex_dirs[0] = v3s16(-1,-1,-1);
409 vertex_dirs[1] = v3s16(-1,-1, 1);
410 vertex_dirs[2] = v3s16(-1, 1, 1);
411 vertex_dirs[3] = v3s16(-1, 1,-1);
413 else if(dir == v3s16(0,1,0))
415 // faces towards Y+ (assume Z- as "down" in texture)
416 vertex_dirs[0] = v3s16( 1, 1,-1);
417 vertex_dirs[1] = v3s16(-1, 1,-1);
418 vertex_dirs[2] = v3s16(-1, 1, 1);
419 vertex_dirs[3] = v3s16( 1, 1, 1);
421 else if(dir == v3s16(0,-1,0))
423 // faces towards Y- (assume Z+ as "down" in texture)
424 vertex_dirs[0] = v3s16( 1,-1, 1);
425 vertex_dirs[1] = v3s16(-1,-1, 1);
426 vertex_dirs[2] = v3s16(-1,-1,-1);
427 vertex_dirs[3] = v3s16( 1,-1,-1);
434 video::S3DVertex vertices[4]; // Precalculated vertices
437 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
438 v3f p, v3s16 dir, v3f scale, u8 light_source, core::array<FastFace> &dest)
442 // Position is at the center of the cube.
446 v3s16 vertex_dirs[4];
447 getNodeVertexDirs(dir, vertex_dirs);
448 for(u16 i=0; i<4; i++)
451 BS/2*vertex_dirs[i].X,
452 BS/2*vertex_dirs[i].Y,
453 BS/2*vertex_dirs[i].Z
457 for(u16 i=0; i<4; i++)
459 vertex_pos[i].X *= scale.X;
460 vertex_pos[i].Y *= scale.Y;
461 vertex_pos[i].Z *= scale.Z;
462 vertex_pos[i] += pos;
466 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
467 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
468 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
470 v3f normal(dir.X, dir.Y, dir.Z);
472 u8 alpha = tile.alpha;
474 float x0 = tile.texture.pos.X;
475 float y0 = tile.texture.pos.Y;
476 float w = tile.texture.size.X;
477 float h = tile.texture.size.Y;
479 face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
480 MapBlock_LightColor(alpha, li0, light_source),
481 core::vector2d<f32>(x0+w*abs_scale, y0+h));
482 face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
483 MapBlock_LightColor(alpha, li1, light_source),
484 core::vector2d<f32>(x0, y0+h));
485 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
486 MapBlock_LightColor(alpha, li2, light_source),
487 core::vector2d<f32>(x0, y0));
488 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
489 MapBlock_LightColor(alpha, li3, light_source),
490 core::vector2d<f32>(x0+w*abs_scale, y0));
494 dest.push_back(face);
498 Nodes make a face if contents differ and solidness differs.
501 1: Face uses m1's content
502 2: Face uses m2's content
503 equivalent: Whether the blocks share the same face (eg. water and glass)
505 TODO: Add 3: Both faces drawn with backface culling, remove equivalent
507 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
508 INodeDefManager *ndef)
512 if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
515 bool contents_differ = (m1 != m2);
517 const ContentFeatures &f1 = ndef->get(m1);
518 const ContentFeatures &f2 = ndef->get(m2);
520 // Contents don't differ for different forms of same liquid
521 if(f1.sameLiquid(f2))
522 contents_differ = false;
524 u8 c1 = f1.solidness;
525 u8 c2 = f2.solidness;
527 bool solidness_differs = (c1 != c2);
528 bool makes_face = contents_differ && solidness_differs;
530 if(makes_face == false)
534 c1 = f1.visual_solidness;
536 c2 = f2.visual_solidness;
540 // If same solidness, liquid takes precense
554 Gets nth node tile (0 <= n <= 5).
556 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
558 INodeDefManager *ndef = data->m_gamedef->ndef();
559 TileSpec spec = ndef->get(mn).tiles[tileindex];
560 // Apply temporary crack
561 if(p == data->m_crack_pos_relative)
563 spec.material_flags |= MATERIAL_FLAG_CRACK;
564 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
566 // If animated, replace tile texture with one without texture atlas
567 if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
569 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
575 Gets node tile given a face direction.
577 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
579 INodeDefManager *ndef = data->m_gamedef->ndef();
581 // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
582 // (0,0,1), (0,0,-1) or (0,0,0)
583 assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
585 // Convert direction to single integer for table lookup
590 // 4 = invalid, treat as (0,0,0)
594 u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
596 // Get rotation for things like chests
597 u8 facedir = mn.getFaceDir(ndef);
598 assert(facedir <= 3);
600 static const u8 dir_to_tile[4 * 8] =
602 // 0 +X +Y +Z 0 -Z -Y -X
603 0, 2, 0, 4, 0, 5, 1, 3, // facedir = 0
604 0, 4, 0, 3, 0, 2, 1, 5, // facedir = 1
605 0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
606 0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
608 u8 tileindex = dir_to_tile[facedir*8 + dir_i];
610 // If not rotated or is side tile, we're done
611 if(facedir == 0 || (tileindex != 0 && tileindex != 1))
612 return getNodeTileN(mn, p, tileindex, data);
614 // This is the top or bottom tile, and it shall be rotated; thus rotate it
615 TileSpec spec = getNodeTileN(mn, p, tileindex, data);
617 if(facedir == 1){ // -90
618 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
619 name += "^[transformR270";
620 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
622 else if(facedir == 2){ // 180
623 spec.texture.pos += spec.texture.size;
624 spec.texture.size *= -1;
626 else if(facedir == 3){ // 90
627 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
628 name += "^[transformR90";
629 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
632 else if(tileindex == 1){
633 if(facedir == 1){ // -90
634 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
635 name += "^[transformR90";
636 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
638 else if(facedir == 2){ // 180
639 spec.texture.pos += spec.texture.size;
640 spec.texture.size *= -1;
642 else if(facedir == 3){ // 90
643 std::string name = data->m_gamedef->tsrc()->getTextureName(spec.texture.id);
644 name += "^[transformR270";
645 spec.texture = data->m_gamedef->tsrc()->getTexture(name);
651 static void getTileInfo(
659 v3s16 &face_dir_corrected,
665 VoxelManipulator &vmanip = data->m_vmanip;
666 INodeDefManager *ndef = data->m_gamedef->ndef();
667 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
669 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
670 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
671 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
672 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
675 bool equivalent = false;
676 u8 mf = face_contents(n0.getContent(), n1.getContent(),
691 face_dir_corrected = face_dir;
692 light_source = ndef->get(n0).light_source;
697 p_corrected = p + face_dir;
698 face_dir_corrected = -face_dir;
699 light_source = ndef->get(n1).light_source;
702 // eg. water and glass
704 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
706 if(data->m_smooth_lighting == false)
708 lights[0] = lights[1] = lights[2] = lights[3] =
709 getFaceLight(n0, n1, face_dir, data);
713 v3s16 vertex_dirs[4];
714 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
715 for(u16 i=0; i<4; i++)
717 lights[i] = getSmoothLight(
718 blockpos_nodes + p_corrected,
719 vertex_dirs[i], data);
728 translate_dir: unit vector with only one of x, y or z
729 face_dir: unit vector with only one of x, y or z
731 static void updateFastFaceRow(
738 core::array<FastFace> &dest)
742 u16 continuous_tiles_count = 0;
744 bool makes_face = false;
746 v3s16 face_dir_corrected;
747 u16 lights[4] = {0,0,0,0};
750 getTileInfo(data, p, face_dir,
751 makes_face, p_corrected, face_dir_corrected,
752 lights, tile, light_source);
754 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
756 // If tiling can be done, this is set to false in the next step
757 bool next_is_different = true;
761 bool next_makes_face = false;
762 v3s16 next_p_corrected;
763 v3s16 next_face_dir_corrected;
764 u16 next_lights[4] = {0,0,0,0};
766 u8 next_light_source = 0;
768 // If at last position, there is nothing to compare to and
769 // the face must be drawn anyway
770 if(j != MAP_BLOCKSIZE - 1)
772 p_next = p + translate_dir;
774 getTileInfo(data, p_next, face_dir,
775 next_makes_face, next_p_corrected,
776 next_face_dir_corrected, next_lights,
777 next_tile, next_light_source);
779 if(next_makes_face == makes_face
780 && next_p_corrected == p_corrected + translate_dir
781 && next_face_dir_corrected == face_dir_corrected
782 && next_lights[0] == lights[0]
783 && next_lights[1] == lights[1]
784 && next_lights[2] == lights[2]
785 && next_lights[3] == lights[3]
787 && next_light_source == light_source)
789 next_is_different = false;
793 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
794 next_makes_face != makes_face ? 1 : 0);
795 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
796 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
797 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
798 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
799 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
800 (next_lights[0] != lights[0] ||
801 next_lights[0] != lights[0] ||
802 next_lights[0] != lights[0] ||
803 next_lights[0] != lights[0]) ? 1 : 0);
804 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
805 !(next_tile == tile) ? 1 : 0);
808 /*g_profiler->add("Meshgen: Total faces checked", 1);
810 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
813 g_profiler->add("Meshgen: diff: last position", 1);*/
816 continuous_tiles_count++;
818 // This is set to true if the texture doesn't allow more tiling
819 bool end_of_texture = false;
821 If there is no texture, it can be tiled infinitely.
822 If tiled==0, it means the texture can be tiled infinitely.
823 Otherwise check tiled agains continuous_tiles_count.
825 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
827 if(tile.texture.tiled <= continuous_tiles_count)
828 end_of_texture = true;
831 // Do this to disable tiling textures
832 //end_of_texture = true; //DEBUG
834 if(next_is_different || end_of_texture)
837 Create a face if there should be one
841 // Floating point conversion of the position vector
842 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
843 // Center point of face (kind of)
844 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
845 if(continuous_tiles_count != 1)
846 sp += translate_dir_f;
849 if(translate_dir.X != 0)
851 scale.X = continuous_tiles_count;
853 if(translate_dir.Y != 0)
855 scale.Y = continuous_tiles_count;
857 if(translate_dir.Z != 0)
859 scale.Z = continuous_tiles_count;
862 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
863 sp, face_dir_corrected, scale, light_source,
866 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
867 for(int i=1; i<continuous_tiles_count; i++){
868 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
872 continuous_tiles_count = 0;
874 makes_face = next_makes_face;
875 p_corrected = next_p_corrected;
876 face_dir_corrected = next_face_dir_corrected;
877 lights[0] = next_lights[0];
878 lights[1] = next_lights[1];
879 lights[2] = next_lights[2];
880 lights[3] = next_lights[3];
882 light_source = next_light_source;
889 static void updateAllFastFaceRows(MeshMakeData *data,
890 core::array<FastFace> &dest)
893 Go through every y,z and get top(y+) faces in rows of x+
895 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
896 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
897 updateFastFaceRow(data,
901 v3s16(0,1,0), //face dir
908 Go through every x,y and get right(x+) faces in rows of z+
910 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
911 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
912 updateFastFaceRow(data,
916 v3s16(1,0,0), //face dir
923 Go through every y,z and get back(z+) faces in rows of x+
925 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
926 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
927 updateFastFaceRow(data,
931 v3s16(0,0,1), //face dir
942 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
943 m_mesh(new scene::SMesh()),
944 m_gamedef(data->m_gamedef),
945 m_animation_force_timer(0), // force initial animation
948 m_last_daynight_ratio((u32) -1),
951 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
952 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
953 //TimeTaker timer1("MapBlockMesh()");
955 core::array<FastFace> fastfaces_new;
958 We are including the faces of the trailing edges of the block.
959 This means that when something changes, the caller must
960 also update the meshes of the blocks at the leading edges.
962 NOTE: This is the slowest part of this method.
965 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
966 //TimeTaker timer2("updateAllFastFaceRows()");
967 updateAllFastFaceRows(data, fastfaces_new);
972 Convert FastFaces to MeshCollector
975 MeshCollector collector;
978 // avg 0ms (100ms spikes when loading textures the first time)
979 // (NOTE: probably outdated)
980 //TimeTaker timer2("MeshCollector building");
982 for(u32 i=0; i<fastfaces_new.size(); i++)
984 FastFace &f = fastfaces_new[i];
986 const u16 indices[] = {0,1,2,2,3,0};
987 const u16 indices_alternate[] = {0,1,3,2,3,1};
989 if(f.tile.texture.atlas == NULL)
992 const u16 *indices_p = indices;
995 Revert triangles for nicer looking gradient if vertices
996 1 and 3 have same color or 0 and 2 have different color.
997 getRed() is the day color.
999 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
1000 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
1001 indices_p = indices_alternate;
1003 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1008 Add special graphics:
1015 mapblock_mesh_generate_special(data, collector);
1019 Convert MeshCollector to SMesh
1020 Also store animation info
1022 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1023 video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
1024 getShader("test_shader_1").material;
1025 video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
1026 getShader("test_shader_2").material;
1027 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1029 PreMeshBuffer &p = collector.prebuffers[i];
1030 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1031 <<", p.indices.size()="<<p.indices.size()
1034 // Generate animation data
1036 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1038 ITextureSource *tsrc = data->m_gamedef->tsrc();
1039 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
1040 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1041 crack_basename += "^[cracko";
1043 crack_basename += "^[crack";
1044 m_crack_materials.insert(std::make_pair(i, crack_basename));
1046 // - Texture animation
1047 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1049 ITextureSource *tsrc = data->m_gamedef->tsrc();
1050 // Add to MapBlockMesh in order to animate these tiles
1051 m_animation_tiles[i] = p.tile;
1052 m_animation_frames[i] = 0;
1053 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1054 // Get starting position from noise
1055 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1056 data->m_blockpos.X, data->m_blockpos.Y,
1057 data->m_blockpos.Z, 0));
1059 // Play all synchronized
1060 m_animation_frame_offsets[i] = 0;
1062 // Replace tile texture with the first animation frame
1063 std::ostringstream os(std::ios::binary);
1064 os<<tsrc->getTextureName(p.tile.texture.id);
1065 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1066 p.tile.texture = tsrc->getTexture(os.str());
1068 // - Classic lighting (shaders handle this by themselves)
1071 for(u32 j = 0; j < p.vertices.size(); j++)
1073 video::SColor &vc = p.vertices[j].Color;
1074 u8 day = vc.getRed();
1075 u8 night = vc.getGreen();
1076 finalColorBlend(vc, day, night, 1000);
1078 m_daynight_diffs[i][j] = std::make_pair(day, night);
1083 video::SMaterial material;
1084 material.setFlag(video::EMF_LIGHTING, false);
1085 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1086 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1087 material.setFlag(video::EMF_FOG_ENABLE, true);
1088 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1089 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1090 material.MaterialType
1091 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1092 material.setTexture(0, p.tile.texture.atlas);
1094 p.tile.applyMaterialOptionsWithShaders(material, shadermat1, shadermat2);
1096 p.tile.applyMaterialOptions(material);
1098 // Create meshbuffer
1100 // This is a "Standard MeshBuffer",
1101 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1102 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1104 buf->Material = material;
1106 m_mesh->addMeshBuffer(buf);
1109 buf->append(p.vertices.pointer(), p.vertices.size(),
1110 p.indices.pointer(), p.indices.size());
1114 Do some stuff to the mesh
1117 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1118 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1123 // Usually 1-700 faces and 1-7 materials
1124 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1125 <<"and uses "<<m_mesh->getMeshBufferCount()
1126 <<" materials (meshbuffers)"<<std::endl;
1129 // Use VBO for mesh (this just would set this for ever buffer)
1130 // This will lead to infinite memory usage because or irrlicht.
1131 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1134 NOTE: If that is enabled, some kind of a queue to the main
1135 thread should be made which would call irrlicht to delete
1136 the hardware buffer and then delete the mesh
1140 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1142 // Check if animation is required for this mesh
1144 !m_crack_materials.empty() ||
1145 !m_daynight_diffs.empty() ||
1146 !m_animation_tiles.empty();
1149 MapBlockMesh::~MapBlockMesh()
1155 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1157 if(!m_has_animation)
1159 m_animation_force_timer = 100000;
1163 m_animation_force_timer = myrand_range(5, 100);
1166 if(crack != m_last_crack)
1168 for(std::map<u32, std::string>::iterator
1169 i = m_crack_materials.begin();
1170 i != m_crack_materials.end(); i++)
1172 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1173 std::string basename = i->second;
1175 // Create new texture name from original
1176 ITextureSource *tsrc = m_gamedef->getTextureSource();
1177 std::ostringstream os;
1178 os<<basename<<crack;
1179 AtlasPointer ap = tsrc->getTexture(os.str());
1180 buf->getMaterial().setTexture(0, ap.atlas);
1183 m_last_crack = crack;
1186 // Texture animation
1187 for(std::map<u32, TileSpec>::iterator
1188 i = m_animation_tiles.begin();
1189 i != m_animation_tiles.end(); i++)
1191 const TileSpec &tile = i->second;
1192 // Figure out current frame
1193 int frameoffset = m_animation_frame_offsets[i->first];
1194 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1195 + frameoffset) % tile.animation_frame_count;
1196 // If frame doesn't change, skip
1197 if(frame == m_animation_frames[i->first])
1200 m_animation_frames[i->first] = frame;
1202 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1203 ITextureSource *tsrc = m_gamedef->getTextureSource();
1205 // Create new texture name from original
1206 std::ostringstream os(std::ios::binary);
1207 os<<tsrc->getTextureName(tile.texture.id);
1208 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1210 AtlasPointer ap = tsrc->getTexture(os.str());
1211 buf->getMaterial().setTexture(0, ap.atlas);
1214 // Day-night transition
1215 if(daynight_ratio != m_last_daynight_ratio)
1217 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1218 i = m_daynight_diffs.begin();
1219 i != m_daynight_diffs.end(); i++)
1221 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1222 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1223 for(std::map<u32, std::pair<u8, u8 > >::iterator
1224 j = i->second.begin();
1225 j != i->second.end(); j++)
1227 u32 vertexIndex = j->first;
1228 u8 day = j->second.first;
1229 u8 night = j->second.second;
1230 finalColorBlend(vertices[vertexIndex].Color,
1231 day, night, daynight_ratio);
1234 m_last_daynight_ratio = daynight_ratio;
1244 void MeshCollector::append(const TileSpec &tile,
1245 const video::S3DVertex *vertices, u32 numVertices,
1246 const u16 *indices, u32 numIndices)
1248 PreMeshBuffer *p = NULL;
1249 for(u32 i=0; i<prebuffers.size(); i++)
1251 PreMeshBuffer &pp = prebuffers[i];
1263 prebuffers.push_back(pp);
1264 p = &prebuffers[prebuffers.size()-1];
1267 u32 vertex_count = p->vertices.size();
1268 for(u32 i=0; i<numIndices; i++)
1270 u32 j = indices[i] + vertex_count;
1273 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1274 // NOTE: Fix is to just add an another MeshBuffer
1276 p->indices.push_back(j);
1278 for(u32 i=0; i<numVertices; i++)
1280 p->vertices.push_back(vertices[i]);