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, 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),
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),
484 core::vector2d<f32>(x0, y0+h));
485 face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
486 MapBlock_LightColor(alpha, li2),
487 core::vector2d<f32>(x0, y0));
488 face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
489 MapBlock_LightColor(alpha, li3),
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,
664 VoxelManipulator &vmanip = data->m_vmanip;
665 INodeDefManager *ndef = data->m_gamedef->ndef();
666 v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
668 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
669 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
670 TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
671 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
674 bool equivalent = false;
675 u8 mf = face_contents(n0.getContent(), n1.getContent(),
690 face_dir_corrected = face_dir;
695 p_corrected = p + face_dir;
696 face_dir_corrected = -face_dir;
699 // eg. water and glass
701 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
703 if(data->m_smooth_lighting == false)
705 lights[0] = lights[1] = lights[2] = lights[3] =
706 getFaceLight(n0, n1, face_dir, data);
710 v3s16 vertex_dirs[4];
711 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
712 for(u16 i=0; i<4; i++)
714 lights[i] = getSmoothLight(
715 blockpos_nodes + p_corrected,
716 vertex_dirs[i], data);
725 translate_dir: unit vector with only one of x, y or z
726 face_dir: unit vector with only one of x, y or z
728 static void updateFastFaceRow(
735 core::array<FastFace> &dest)
739 u16 continuous_tiles_count = 0;
741 bool makes_face = false;
743 v3s16 face_dir_corrected;
744 u16 lights[4] = {0,0,0,0};
746 getTileInfo(data, p, face_dir,
747 makes_face, p_corrected, face_dir_corrected,
750 for(u16 j=0; j<MAP_BLOCKSIZE; j++)
752 // If tiling can be done, this is set to false in the next step
753 bool next_is_different = true;
757 bool next_makes_face = false;
758 v3s16 next_p_corrected;
759 v3s16 next_face_dir_corrected;
760 u16 next_lights[4] = {0,0,0,0};
763 // If at last position, there is nothing to compare to and
764 // the face must be drawn anyway
765 if(j != MAP_BLOCKSIZE - 1)
767 p_next = p + translate_dir;
769 getTileInfo(data, p_next, face_dir,
770 next_makes_face, next_p_corrected,
771 next_face_dir_corrected, next_lights,
774 if(next_makes_face == makes_face
775 && next_p_corrected == p_corrected + translate_dir
776 && next_face_dir_corrected == face_dir_corrected
777 && next_lights[0] == lights[0]
778 && next_lights[1] == lights[1]
779 && next_lights[2] == lights[2]
780 && next_lights[3] == lights[3]
781 && next_tile == tile)
783 next_is_different = false;
787 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
788 next_makes_face != makes_face ? 1 : 0);
789 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
790 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
791 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
792 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
793 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
794 (next_lights[0] != lights[0] ||
795 next_lights[0] != lights[0] ||
796 next_lights[0] != lights[0] ||
797 next_lights[0] != lights[0]) ? 1 : 0);
798 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
799 !(next_tile == tile) ? 1 : 0);
802 /*g_profiler->add("Meshgen: Total faces checked", 1);
804 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
807 g_profiler->add("Meshgen: diff: last position", 1);*/
810 continuous_tiles_count++;
812 // This is set to true if the texture doesn't allow more tiling
813 bool end_of_texture = false;
815 If there is no texture, it can be tiled infinitely.
816 If tiled==0, it means the texture can be tiled infinitely.
817 Otherwise check tiled agains continuous_tiles_count.
819 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
821 if(tile.texture.tiled <= continuous_tiles_count)
822 end_of_texture = true;
825 // Do this to disable tiling textures
826 //end_of_texture = true; //DEBUG
828 if(next_is_different || end_of_texture)
831 Create a face if there should be one
835 // Floating point conversion of the position vector
836 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
837 // Center point of face (kind of)
838 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
839 if(continuous_tiles_count != 1)
840 sp += translate_dir_f;
843 if(translate_dir.X != 0)
845 scale.X = continuous_tiles_count;
847 if(translate_dir.Y != 0)
849 scale.Y = continuous_tiles_count;
851 if(translate_dir.Z != 0)
853 scale.Z = continuous_tiles_count;
856 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
857 sp, face_dir_corrected, scale,
860 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
861 for(int i=1; i<continuous_tiles_count; i++){
862 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
866 continuous_tiles_count = 0;
868 makes_face = next_makes_face;
869 p_corrected = next_p_corrected;
870 face_dir_corrected = next_face_dir_corrected;
871 lights[0] = next_lights[0];
872 lights[1] = next_lights[1];
873 lights[2] = next_lights[2];
874 lights[3] = next_lights[3];
882 static void updateAllFastFaceRows(MeshMakeData *data,
883 core::array<FastFace> &dest)
886 Go through every y,z and get top(y+) faces in rows of x+
888 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
889 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
890 updateFastFaceRow(data,
894 v3s16(0,1,0), //face dir
901 Go through every x,y and get right(x+) faces in rows of z+
903 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
904 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
905 updateFastFaceRow(data,
909 v3s16(1,0,0), //face dir
916 Go through every y,z and get back(z+) faces in rows of x+
918 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
919 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
920 updateFastFaceRow(data,
924 v3s16(0,0,1), //face dir
935 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
936 m_mesh(new scene::SMesh()),
937 m_gamedef(data->m_gamedef),
938 m_animation_force_timer(0), // force initial animation
941 m_last_daynight_ratio((u32) -1),
944 // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
945 // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
946 //TimeTaker timer1("MapBlockMesh()");
948 core::array<FastFace> fastfaces_new;
951 We are including the faces of the trailing edges of the block.
952 This means that when something changes, the caller must
953 also update the meshes of the blocks at the leading edges.
955 NOTE: This is the slowest part of this method.
958 // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
959 //TimeTaker timer2("updateAllFastFaceRows()");
960 updateAllFastFaceRows(data, fastfaces_new);
965 Convert FastFaces to MeshCollector
968 MeshCollector collector;
971 // avg 0ms (100ms spikes when loading textures the first time)
972 // (NOTE: probably outdated)
973 //TimeTaker timer2("MeshCollector building");
975 for(u32 i=0; i<fastfaces_new.size(); i++)
977 FastFace &f = fastfaces_new[i];
979 const u16 indices[] = {0,1,2,2,3,0};
980 const u16 indices_alternate[] = {0,1,3,2,3,1};
982 if(f.tile.texture.atlas == NULL)
985 const u16 *indices_p = indices;
988 Revert triangles for nicer looking gradient if vertices
989 1 and 3 have same color or 0 and 2 have different color.
990 getRed() is the day color.
992 if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
993 || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
994 indices_p = indices_alternate;
996 collector.append(f.tile, f.vertices, 4, indices_p, 6);
1001 Add special graphics:
1008 mapblock_mesh_generate_special(data, collector);
1012 Convert MeshCollector to SMesh
1013 Also store animation info
1015 bool enable_shaders = (g_settings->getS32("enable_shaders") > 0);
1016 video::E_MATERIAL_TYPE shadermat1 = m_gamedef->getShaderSource()->
1017 getShader("test_shader_1").material;
1018 video::E_MATERIAL_TYPE shadermat2 = m_gamedef->getShaderSource()->
1019 getShader("test_shader_2").material;
1020 for(u32 i = 0; i < collector.prebuffers.size(); i++)
1022 PreMeshBuffer &p = collector.prebuffers[i];
1023 /*dstream<<"p.vertices.size()="<<p.vertices.size()
1024 <<", p.indices.size()="<<p.indices.size()
1027 // Generate animation data
1029 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
1031 ITextureSource *tsrc = data->m_gamedef->tsrc();
1032 std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
1033 if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
1034 crack_basename += "^[cracko";
1036 crack_basename += "^[crack";
1037 m_crack_materials.insert(std::make_pair(i, crack_basename));
1039 // - Texture animation
1040 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
1042 ITextureSource *tsrc = data->m_gamedef->tsrc();
1043 // Add to MapBlockMesh in order to animate these tiles
1044 m_animation_tiles[i] = p.tile;
1045 m_animation_frames[i] = 0;
1046 if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1047 // Get starting position from noise
1048 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1049 data->m_blockpos.X, data->m_blockpos.Y,
1050 data->m_blockpos.Z, 0));
1052 // Play all synchronized
1053 m_animation_frame_offsets[i] = 0;
1055 // Replace tile texture with the first animation frame
1056 std::ostringstream os(std::ios::binary);
1057 os<<tsrc->getTextureName(p.tile.texture.id);
1058 os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1059 p.tile.texture = tsrc->getTexture(os.str());
1062 for(u32 j = 0; j < p.vertices.size(); j++)
1064 video::SColor &vc = p.vertices[j].Color;
1065 u8 day = vc.getRed();
1066 u8 night = vc.getGreen();
1067 finalColorBlend(vc, day, night, 1000);
1069 m_daynight_diffs[i][j] = std::make_pair(day, night);
1074 video::SMaterial material;
1075 material.setFlag(video::EMF_LIGHTING, false);
1076 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1077 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1078 material.setFlag(video::EMF_FOG_ENABLE, true);
1079 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1080 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1081 material.MaterialType
1082 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1083 material.setTexture(0, p.tile.texture.atlas);
1084 p.tile.applyMaterialOptions(material);
1087 if(material.MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF)
1088 material.MaterialType = shadermat1;
1089 if(material.MaterialType == video::EMT_TRANSPARENT_VERTEX_ALPHA)
1090 material.MaterialType = shadermat2;
1093 // Create meshbuffer
1095 // This is a "Standard MeshBuffer",
1096 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1097 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1099 buf->Material = material;
1101 m_mesh->addMeshBuffer(buf);
1104 buf->append(p.vertices.pointer(), p.vertices.size(),
1105 p.indices.pointer(), p.indices.size());
1109 Do some stuff to the mesh
1112 translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1113 m_mesh->recalculateBoundingBox(); // translateMesh already does this
1118 // Usually 1-700 faces and 1-7 materials
1119 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1120 <<"and uses "<<m_mesh->getMeshBufferCount()
1121 <<" materials (meshbuffers)"<<std::endl;
1124 // Use VBO for mesh (this just would set this for ever buffer)
1125 // This will lead to infinite memory usage because or irrlicht.
1126 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1129 NOTE: If that is enabled, some kind of a queue to the main
1130 thread should be made which would call irrlicht to delete
1131 the hardware buffer and then delete the mesh
1135 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1137 // Check if animation is required for this mesh
1139 !m_crack_materials.empty() ||
1140 !m_daynight_diffs.empty() ||
1141 !m_animation_tiles.empty();
1144 MapBlockMesh::~MapBlockMesh()
1150 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1152 if(!m_has_animation)
1154 m_animation_force_timer = 100000;
1158 m_animation_force_timer = myrand_range(5, 100);
1161 if(crack != m_last_crack)
1163 for(std::map<u32, std::string>::iterator
1164 i = m_crack_materials.begin();
1165 i != m_crack_materials.end(); i++)
1167 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1168 std::string basename = i->second;
1170 // Create new texture name from original
1171 ITextureSource *tsrc = m_gamedef->getTextureSource();
1172 std::ostringstream os;
1173 os<<basename<<crack;
1174 AtlasPointer ap = tsrc->getTexture(os.str());
1175 buf->getMaterial().setTexture(0, ap.atlas);
1178 m_last_crack = crack;
1181 // Texture animation
1182 for(std::map<u32, TileSpec>::iterator
1183 i = m_animation_tiles.begin();
1184 i != m_animation_tiles.end(); i++)
1186 const TileSpec &tile = i->second;
1187 // Figure out current frame
1188 int frameoffset = m_animation_frame_offsets[i->first];
1189 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1190 + frameoffset) % tile.animation_frame_count;
1191 // If frame doesn't change, skip
1192 if(frame == m_animation_frames[i->first])
1195 m_animation_frames[i->first] = frame;
1197 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1198 ITextureSource *tsrc = m_gamedef->getTextureSource();
1200 // Create new texture name from original
1201 std::ostringstream os(std::ios::binary);
1202 os<<tsrc->getTextureName(tile.texture.id);
1203 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1205 AtlasPointer ap = tsrc->getTexture(os.str());
1206 buf->getMaterial().setTexture(0, ap.atlas);
1209 // Day-night transition
1210 if(daynight_ratio != m_last_daynight_ratio)
1212 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1213 i = m_daynight_diffs.begin();
1214 i != m_daynight_diffs.end(); i++)
1216 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1217 video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1218 for(std::map<u32, std::pair<u8, u8 > >::iterator
1219 j = i->second.begin();
1220 j != i->second.end(); j++)
1222 u32 vertexIndex = j->first;
1223 u8 day = j->second.first;
1224 u8 night = j->second.second;
1225 finalColorBlend(vertices[vertexIndex].Color,
1226 day, night, daynight_ratio);
1229 m_last_daynight_ratio = daynight_ratio;
1239 void MeshCollector::append(const TileSpec &tile,
1240 const video::S3DVertex *vertices, u32 numVertices,
1241 const u16 *indices, u32 numIndices)
1243 PreMeshBuffer *p = NULL;
1244 for(u32 i=0; i<prebuffers.size(); i++)
1246 PreMeshBuffer &pp = prebuffers[i];
1258 prebuffers.push_back(pp);
1259 p = &prebuffers[prebuffers.size()-1];
1262 u32 vertex_count = p->vertices.size();
1263 for(u32 i=0; i<numIndices; i++)
1265 u32 j = indices[i] + vertex_count;
1268 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1269 // NOTE: Fix is to just add an another MeshBuffer
1271 p->indices.push_back(j);
1273 for(u32 i=0; i<numVertices; i++)
1275 p->vertices.push_back(vertices[i]);