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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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_settings and g_texturesource
30 #include "content_mapblock.h"
31 #include "mineral.h" // For mineral_block_texture
33 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
35 m_daynight_ratio = daynight_ratio;
36 m_blockpos = block->getPos();
38 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
41 There is no harm not copying the TempMods of the neighbors
42 because they are already copied to this block
45 block->copyTempMods(m_temp_mods);
51 // Allocate this block + neighbors
53 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
54 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
57 //TimeTaker timer("copy central block data");
61 block->copyTo(m_vmanip);
64 //TimeTaker timer("copy neighbor block data");
68 Copy neighbors. This is lightning fast.
69 Copying only the borders would be *very* slow.
73 Map *map = block->getParent();
75 for(u16 i=0; i<6; i++)
77 const v3s16 &dir = g_6dirs[i];
78 v3s16 bp = m_blockpos + dir;
79 MapBlock *b = map->getBlockNoCreateNoEx(bp);
89 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
92 If looked from outside the node towards the face, the corners are:
98 if(dir == v3s16(0,0,1))
100 // If looking towards z+, this is the face that is behind
101 // the center point, facing towards z+.
102 vertex_dirs[0] = v3s16(-1,-1, 1);
103 vertex_dirs[1] = v3s16( 1,-1, 1);
104 vertex_dirs[2] = v3s16( 1, 1, 1);
105 vertex_dirs[3] = v3s16(-1, 1, 1);
107 else if(dir == v3s16(0,0,-1))
110 vertex_dirs[0] = v3s16( 1,-1,-1);
111 vertex_dirs[1] = v3s16(-1,-1,-1);
112 vertex_dirs[2] = v3s16(-1, 1,-1);
113 vertex_dirs[3] = v3s16( 1, 1,-1);
115 else if(dir == v3s16(1,0,0))
118 vertex_dirs[0] = v3s16( 1,-1, 1);
119 vertex_dirs[1] = v3s16( 1,-1,-1);
120 vertex_dirs[2] = v3s16( 1, 1,-1);
121 vertex_dirs[3] = v3s16( 1, 1, 1);
123 else if(dir == v3s16(-1,0,0))
126 vertex_dirs[0] = v3s16(-1,-1,-1);
127 vertex_dirs[1] = v3s16(-1,-1, 1);
128 vertex_dirs[2] = v3s16(-1, 1, 1);
129 vertex_dirs[3] = v3s16(-1, 1,-1);
131 else if(dir == v3s16(0,1,0))
133 // faces towards Y+ (assume Z- as "down" in texture)
134 vertex_dirs[0] = v3s16( 1, 1,-1);
135 vertex_dirs[1] = v3s16(-1, 1,-1);
136 vertex_dirs[2] = v3s16(-1, 1, 1);
137 vertex_dirs[3] = v3s16( 1, 1, 1);
139 else if(dir == v3s16(0,-1,0))
141 // faces towards Y- (assume Z+ as "down" in texture)
142 vertex_dirs[0] = v3s16( 1,-1, 1);
143 vertex_dirs[1] = v3s16(-1,-1, 1);
144 vertex_dirs[2] = v3s16(-1,-1,-1);
145 vertex_dirs[3] = v3s16( 1,-1,-1);
149 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
152 return video::SColor(alpha,light,light,light);
154 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
155 /*return video::SColor(alpha,light,light,MYMAX(0,
156 pow((float)light/255.0, 0.8)*255.0));*/
158 // Emphase blue a bit in darker places
162 return video::SColor(alpha,light,light,light);
164 return video::SColor(alpha,light,light,MYMAX(0,
165 pow((float)light/lim, power)*lim));
172 video::S3DVertex vertices[4]; // Precalculated vertices
175 static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
176 v3s16 dir, v3f scale, v3f posRelative_f,
177 core::array<FastFace> &dest)
181 // Position is at the center of the cube.
186 v3s16 vertex_dirs[4];
187 getNodeVertexDirs(dir, vertex_dirs);
188 for(u16 i=0; i<4; i++)
191 BS/2*vertex_dirs[i].X,
192 BS/2*vertex_dirs[i].Y,
193 BS/2*vertex_dirs[i].Z
197 for(u16 i=0; i<4; i++)
199 vertex_pos[i].X *= scale.X;
200 vertex_pos[i].Y *= scale.Y;
201 vertex_pos[i].Z *= scale.Z;
202 vertex_pos[i] += pos + posRelative_f;
206 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
207 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
208 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
210 v3f zerovector = v3f(0,0,0);
212 u8 alpha = tile.alpha;
214 if(tile.id == TILE_WATER)
215 alpha = WATER_ALPHA;*/
217 float x0 = tile.texture.pos.X;
218 float y0 = tile.texture.pos.Y;
219 float w = tile.texture.size.X;
220 float h = tile.texture.size.Y;
222 /*video::SColor c = MapBlock_LightColor(alpha, li);
224 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
225 core::vector2d<f32>(x0+w*abs_scale, y0+h));
226 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
227 core::vector2d<f32>(x0, y0+h));
228 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
229 core::vector2d<f32>(x0, y0));
230 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
231 core::vector2d<f32>(x0+w*abs_scale, y0));*/
233 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
234 MapBlock_LightColor(alpha, li0),
235 core::vector2d<f32>(x0+w*abs_scale, y0+h));
236 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
237 MapBlock_LightColor(alpha, li1),
238 core::vector2d<f32>(x0, y0+h));
239 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
240 MapBlock_LightColor(alpha, li2),
241 core::vector2d<f32>(x0, y0));
242 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
243 MapBlock_LightColor(alpha, li3),
244 core::vector2d<f32>(x0+w*abs_scale, y0));
248 //f->tile = TILE_STONE;
250 dest.push_back(face);
253 static TileSpec getTile(const MapNode &node, v3s16 dir,
254 ITextureSource *tsrc, INodeDefManager *nodemgr)
256 const ContentFeatures &f = nodemgr->get(node);
258 if(f.param_type == CPT_FACEDIR_SIMPLE)
259 dir = facedir_rotate(node.param1, dir);
265 if(dir == v3s16(0,0,0))
267 else if(dir == v3s16(0,1,0))
269 else if(dir == v3s16(0,-1,0))
271 else if(dir == v3s16(1,0,0))
273 else if(dir == v3s16(-1,0,0))
275 else if(dir == v3s16(0,0,1))
277 else if(dir == v3s16(0,0,-1))
284 spec = f.tiles[dir_i];
287 If it contains some mineral, change texture id
289 if(f.param_type == CPT_MINERAL && tsrc)
291 u8 mineral = node.getMineral(nodemgr);
292 std::string mineral_texture_name = mineral_block_texture(mineral);
293 if(mineral_texture_name != "")
295 u32 orig_id = spec.texture.id;
296 std::string texture_name = tsrc->getTextureName(orig_id);
297 //texture_name += "^blit:";
299 texture_name += mineral_texture_name;
300 u32 new_id = tsrc->getTextureId(texture_name);
301 spec.texture = tsrc->getTexture(new_id);
309 Gets node tile from any place relative to block.
310 Returns TILE_NODE if doesn't exist or should not be drawn.
312 static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
313 NodeModMap &temp_mods, ITextureSource *tsrc, INodeDefManager *ndef)
316 spec = getTile(mn, face_dir, tsrc, ndef);
319 Check temporary modifications on this node
321 /*core::map<v3s16, NodeMod>::Node *n;
322 n = m_temp_mods.find(p);
326 struct NodeMod mod = n->getValue();*/
328 if(temp_mods.get(p, &mod))
330 if(mod.type == NODEMOD_CHANGECONTENT)
332 MapNode mn2(mod.param);
333 spec = getTile(mn2, face_dir, tsrc, ndef);
335 if(mod.type == NODEMOD_CRACK)
338 Get texture id, translate it to name, append stuff to
342 // Get original texture name
343 u32 orig_id = spec.texture.id;
344 std::string orig_name = tsrc->getTextureName(orig_id);
346 // Create new texture name
347 std::ostringstream os;
348 os<<orig_name<<"^[crack"<<mod.param;
351 u32 new_id = tsrc->getTextureId(os.str());
353 /*dstream<<"MapBlock::getNodeTile(): Switching from "
354 <<orig_name<<" to "<<os.str()<<" ("
355 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
357 spec.texture = tsrc->getTexture(new_id);
364 static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
367 Check temporary modifications on this node
369 /*core::map<v3s16, NodeMod>::Node *n;
370 n = m_temp_mods.find(p);
374 struct NodeMod mod = n->getValue();*/
376 if(temp_mods.get(p, &mod))
378 if(mod.type == NODEMOD_CHANGECONTENT)
383 if(mod.type == NODEMOD_CRACK)
386 Content doesn't change.
388 face_contents works just like it should, because
389 there should not be faces between differently cracked
392 If a semi-transparent node is cracked in front an
393 another one, it really doesn't matter whether there
394 is a cracked face drawn in between or not.
399 return mn.getContent();
413 // Calculate lighting at the XYZ- corner of p
414 static u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio,
415 INodeDefManager *ndef)
417 u16 ambient_occlusion = 0;
420 for(u32 i=0; i<8; i++)
422 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
423 if(ndef->get(n).param_type == CPT_LIGHT
424 // Fast-style leaves look better this way
425 && ndef->get(n).solidness != 2)
427 light += decode_light(n.getLightBlend(daynight_ratio, ndef));
432 if(n.getContent() != CONTENT_IGNORE)
440 light /= light_count;
442 if(ambient_occlusion > 4)
444 ambient_occlusion -= 4;
445 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
451 // Calculate lighting at the given corner of p
452 static u8 getSmoothLight(v3s16 p, v3s16 corner,
453 VoxelManipulator &vmanip, u32 daynight_ratio, INodeDefManager *ndef)
455 if(corner.X == 1) p.X += 1;
456 else assert(corner.X == -1);
457 if(corner.Y == 1) p.Y += 1;
458 else assert(corner.Y == -1);
459 if(corner.Z == 1) p.Z += 1;
460 else assert(corner.Z == -1);
462 return getSmoothLight(p, vmanip, daynight_ratio, ndef);
465 static void getTileInfo(
467 v3s16 blockpos_nodes,
471 VoxelManipulator &vmanip,
472 NodeModMap &temp_mods,
473 bool smooth_lighting,
478 v3s16 &face_dir_corrected,
483 ITextureSource *tsrc = gamedef->tsrc();
484 INodeDefManager *ndef = gamedef->ndef();
486 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
487 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
488 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods, tsrc, ndef);
489 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods, tsrc, ndef);
492 content_t content0 = getNodeContent(p, n0, temp_mods);
493 content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
494 bool equivalent = false;
495 u8 mf = face_contents(content0, content1, &equivalent, ndef);
509 face_dir_corrected = face_dir;
514 p_corrected = p + face_dir;
515 face_dir_corrected = -face_dir;
518 // eg. water and glass
520 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
522 if(smooth_lighting == false)
524 lights[0] = lights[1] = lights[2] = lights[3] =
525 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir, ndef));
529 v3s16 vertex_dirs[4];
530 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
531 for(u16 i=0; i<4; i++)
533 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
534 vertex_dirs[i], vmanip, daynight_ratio, ndef);
543 translate_dir: unit vector with only one of x, y or z
544 face_dir: unit vector with only one of x, y or z
546 static void updateFastFaceRow(
555 core::array<FastFace> &dest,
556 NodeModMap &temp_mods,
557 VoxelManipulator &vmanip,
558 v3s16 blockpos_nodes,
559 bool smooth_lighting,
564 u16 continuous_tiles_count = 0;
566 bool makes_face = false;
568 v3s16 face_dir_corrected;
569 u8 lights[4] = {0,0,0,0};
571 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
572 vmanip, temp_mods, smooth_lighting, gamedef,
573 makes_face, p_corrected, face_dir_corrected, lights, tile);
575 for(u16 j=0; j<length; j++)
577 // If tiling can be done, this is set to false in the next step
578 bool next_is_different = true;
582 bool next_makes_face = false;
583 v3s16 next_p_corrected;
584 v3s16 next_face_dir_corrected;
585 u8 next_lights[4] = {0,0,0,0};
588 // If at last position, there is nothing to compare to and
589 // the face must be drawn anyway
592 p_next = p + translate_dir;
594 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
595 vmanip, temp_mods, smooth_lighting, gamedef,
596 next_makes_face, next_p_corrected,
597 next_face_dir_corrected, next_lights,
600 if(next_makes_face == makes_face
601 && next_p_corrected == p_corrected + translate_dir
602 && next_face_dir_corrected == face_dir_corrected
603 && next_lights[0] == lights[0]
604 && next_lights[1] == lights[1]
605 && next_lights[2] == lights[2]
606 && next_lights[3] == lights[3]
607 && next_tile == tile)
609 next_is_different = false;
613 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
614 next_makes_face != makes_face ? 1 : 0);
615 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
616 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
617 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
618 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
619 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
620 (next_lights[0] != lights[0] ||
621 next_lights[0] != lights[0] ||
622 next_lights[0] != lights[0] ||
623 next_lights[0] != lights[0]) ? 1 : 0);
624 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
625 !(next_tile == tile) ? 1 : 0);
628 /*g_profiler->add("Meshgen: Total faces checked", 1);
630 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
633 g_profiler->add("Meshgen: diff: last position", 1);*/
636 continuous_tiles_count++;
638 // This is set to true if the texture doesn't allow more tiling
639 bool end_of_texture = false;
641 If there is no texture, it can be tiled infinitely.
642 If tiled==0, it means the texture can be tiled infinitely.
643 Otherwise check tiled agains continuous_tiles_count.
645 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
647 if(tile.texture.tiled <= continuous_tiles_count)
648 end_of_texture = true;
651 // Do this to disable tiling textures
652 //end_of_texture = true; //DEBUG
654 if(next_is_different || end_of_texture)
657 Create a face if there should be one
661 // Floating point conversion of the position vector
662 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
663 // Center point of face (kind of)
664 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
665 if(continuous_tiles_count != 1)
666 sp += translate_dir_f;
669 if(translate_dir.X != 0)
671 scale.X = continuous_tiles_count;
673 if(translate_dir.Y != 0)
675 scale.Y = continuous_tiles_count;
677 if(translate_dir.Z != 0)
679 scale.Z = continuous_tiles_count;
682 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
683 sp, face_dir_corrected, scale,
684 posRelative_f, dest);
686 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
687 for(int i=1; i<continuous_tiles_count; i++){
688 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
692 continuous_tiles_count = 0;
694 makes_face = next_makes_face;
695 p_corrected = next_p_corrected;
696 face_dir_corrected = next_face_dir_corrected;
697 lights[0] = next_lights[0];
698 lights[1] = next_lights[1];
699 lights[2] = next_lights[2];
700 lights[3] = next_lights[3];
708 scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
710 // 4-21ms for MAP_BLOCKSIZE=16
711 // 24-155ms for MAP_BLOCKSIZE=32
712 //TimeTaker timer1("makeMapBlockMesh()");
714 core::array<FastFace> fastfaces_new;
716 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
718 // floating point conversion
719 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
724 //bool new_style_water = g_settings->getBool("new_style_water");
725 //bool new_style_leaves = g_settings->getBool("new_style_leaves");
726 bool smooth_lighting = g_settings->getBool("smooth_lighting");
729 We are including the faces of the trailing edges of the block.
730 This means that when something changes, the caller must
731 also update the meshes of the blocks at the leading edges.
733 NOTE: This is the slowest part of this method.
737 // 4-23ms for MAP_BLOCKSIZE=16
738 //TimeTaker timer2("updateMesh() collect");
741 Go through every y,z and get top(y+) faces in rows of x+
743 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
744 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
745 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
746 v3s16(0,y,z), MAP_BLOCKSIZE,
749 v3s16(0,1,0), //face dir
760 Go through every x,y and get right(x+) faces in rows of z+
762 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
763 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
764 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
765 v3s16(x,y,0), MAP_BLOCKSIZE,
779 Go through every y,z and get back(z+) faces in rows of x+
781 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
782 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
783 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
784 v3s16(0,y,z), MAP_BLOCKSIZE,
802 Convert FastFaces to SMesh
805 MeshCollector collector;
807 if(fastfaces_new.size() > 0)
809 // avg 0ms (100ms spikes when loading textures the first time)
810 //TimeTaker timer2("updateMesh() mesh building");
812 video::SMaterial material;
813 material.setFlag(video::EMF_LIGHTING, false);
814 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
815 material.setFlag(video::EMF_BILINEAR_FILTER, false);
816 material.setFlag(video::EMF_FOG_ENABLE, true);
817 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
818 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
819 material.MaterialType
820 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
822 for(u32 i=0; i<fastfaces_new.size(); i++)
824 FastFace &f = fastfaces_new[i];
826 const u16 indices[] = {0,1,2,2,3,0};
827 const u16 indices_alternate[] = {0,1,3,2,3,1};
829 video::ITexture *texture = f.tile.texture.atlas;
833 material.setTexture(0, texture);
835 f.tile.applyMaterialOptions(material);
837 const u16 *indices_p = indices;
840 Revert triangles for nicer looking gradient if vertices
841 1 and 3 have same color or 0 and 2 have different color.
843 if(f.vertices[0].Color != f.vertices[2].Color
844 || f.vertices[1].Color == f.vertices[3].Color)
845 indices_p = indices_alternate;
847 collector.append(material, f.vertices, 4, indices_p, 6);
852 Add special graphics:
859 mapblock_mesh_generate_special(data, collector, gamedef);
862 Add stuff from collector to mesh
865 scene::SMesh *mesh_new = NULL;
866 mesh_new = new scene::SMesh();
868 collector.fillMesh(mesh_new);
871 Do some stuff to the mesh
874 mesh_new->recalculateBoundingBox();
877 Delete new mesh if it is empty
880 if(mesh_new->getMeshBufferCount() == 0)
889 // Usually 1-700 faces and 1-7 materials
890 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
891 <<"and uses "<<mesh_new->getMeshBufferCount()
892 <<" materials (meshbuffers)"<<std::endl;
895 // Use VBO for mesh (this just would set this for ever buffer)
896 // This will lead to infinite memory usage because or irrlicht.
897 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
900 NOTE: If that is enabled, some kind of a queue to the main
901 thread should be made which would call irrlicht to delete
902 the hardware buffer and then delete the mesh
908 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;