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
25 #include "content_mapblock.h"
28 #include "mapnode_contentfeatures.h"
31 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
33 m_daynight_ratio = daynight_ratio;
34 m_blockpos = block->getPos();
36 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
39 There is no harm not copying the TempMods of the neighbors
40 because they are already copied to this block
43 block->copyTempMods(m_temp_mods);
49 // Allocate this block + neighbors
51 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
52 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
55 //TimeTaker timer("copy central block data");
59 block->copyTo(m_vmanip);
62 //TimeTaker timer("copy neighbor block data");
66 Copy neighbors. This is lightning fast.
67 Copying only the borders would be *very* slow.
71 Map *map = block->getParent();
73 for(u16 i=0; i<6; i++)
75 const v3s16 &dir = g_6dirs[i];
76 v3s16 bp = m_blockpos + dir;
77 MapBlock *b = map->getBlockNoCreateNoEx(bp);
87 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
90 If looked from outside the node towards the face, the corners are:
96 if(dir == v3s16(0,0,1))
98 // If looking towards z+, this is the face that is behind
99 // the center point, facing towards z+.
100 vertex_dirs[0] = v3s16(-1,-1, 1);
101 vertex_dirs[1] = v3s16( 1,-1, 1);
102 vertex_dirs[2] = v3s16( 1, 1, 1);
103 vertex_dirs[3] = v3s16(-1, 1, 1);
105 else if(dir == v3s16(0,0,-1))
108 vertex_dirs[0] = v3s16( 1,-1,-1);
109 vertex_dirs[1] = v3s16(-1,-1,-1);
110 vertex_dirs[2] = v3s16(-1, 1,-1);
111 vertex_dirs[3] = v3s16( 1, 1,-1);
113 else if(dir == v3s16(1,0,0))
116 vertex_dirs[0] = v3s16( 1,-1, 1);
117 vertex_dirs[1] = v3s16( 1,-1,-1);
118 vertex_dirs[2] = v3s16( 1, 1,-1);
119 vertex_dirs[3] = v3s16( 1, 1, 1);
121 else if(dir == v3s16(-1,0,0))
124 vertex_dirs[0] = v3s16(-1,-1,-1);
125 vertex_dirs[1] = v3s16(-1,-1, 1);
126 vertex_dirs[2] = v3s16(-1, 1, 1);
127 vertex_dirs[3] = v3s16(-1, 1,-1);
129 else if(dir == v3s16(0,1,0))
131 // faces towards Y+ (assume Z- as "down" in texture)
132 vertex_dirs[0] = v3s16( 1, 1,-1);
133 vertex_dirs[1] = v3s16(-1, 1,-1);
134 vertex_dirs[2] = v3s16(-1, 1, 1);
135 vertex_dirs[3] = v3s16( 1, 1, 1);
137 else if(dir == v3s16(0,-1,0))
139 // faces towards Y- (assume Z+ as "down" in texture)
140 vertex_dirs[0] = v3s16( 1,-1, 1);
141 vertex_dirs[1] = v3s16(-1,-1, 1);
142 vertex_dirs[2] = v3s16(-1,-1,-1);
143 vertex_dirs[3] = v3s16( 1,-1,-1);
147 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
150 return video::SColor(alpha,light,light,light);
152 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
153 /*return video::SColor(alpha,light,light,MYMAX(0,
154 pow((float)light/255.0, 0.8)*255.0));*/
156 // Emphase blue a bit in darker places
160 return video::SColor(alpha,light,light,light);
162 return video::SColor(alpha,light,light,MYMAX(0,
163 pow((float)light/lim, power)*lim));
170 video::S3DVertex vertices[4]; // Precalculated vertices
173 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
174 v3s16 dir, v3f scale, v3f posRelative_f,
175 core::array<FastFace> &dest)
179 // Position is at the center of the cube.
184 v3s16 vertex_dirs[4];
185 getNodeVertexDirs(dir, vertex_dirs);
186 for(u16 i=0; i<4; i++)
189 BS/2*vertex_dirs[i].X,
190 BS/2*vertex_dirs[i].Y,
191 BS/2*vertex_dirs[i].Z
195 for(u16 i=0; i<4; i++)
197 vertex_pos[i].X *= scale.X;
198 vertex_pos[i].Y *= scale.Y;
199 vertex_pos[i].Z *= scale.Z;
200 vertex_pos[i] += pos + posRelative_f;
204 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
205 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
206 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
208 v3f zerovector = v3f(0,0,0);
210 u8 alpha = tile.alpha;
212 if(tile.id == TILE_WATER)
213 alpha = WATER_ALPHA;*/
215 float x0 = tile.texture.pos.X;
216 float y0 = tile.texture.pos.Y;
217 float w = tile.texture.size.X;
218 float h = tile.texture.size.Y;
220 /*video::SColor c = MapBlock_LightColor(alpha, li);
222 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
223 core::vector2d<f32>(x0+w*abs_scale, y0+h));
224 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
225 core::vector2d<f32>(x0, y0+h));
226 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
227 core::vector2d<f32>(x0, y0));
228 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
229 core::vector2d<f32>(x0+w*abs_scale, y0));*/
231 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
232 MapBlock_LightColor(alpha, li0),
233 core::vector2d<f32>(x0+w*abs_scale, y0+h));
234 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
235 MapBlock_LightColor(alpha, li1),
236 core::vector2d<f32>(x0, y0+h));
237 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
238 MapBlock_LightColor(alpha, li2),
239 core::vector2d<f32>(x0, y0));
240 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
241 MapBlock_LightColor(alpha, li3),
242 core::vector2d<f32>(x0+w*abs_scale, y0));
246 //f->tile = TILE_STONE;
248 dest.push_back(face);
252 Gets node tile from any place relative to block.
253 Returns TILE_NODE if doesn't exist or should not be drawn.
255 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
256 NodeModMap &temp_mods, ITextureSource *tsrc)
259 spec = mn.getTile(face_dir, tsrc);
262 Check temporary modifications on this node
264 /*core::map<v3s16, NodeMod>::Node *n;
265 n = m_temp_mods.find(p);
269 struct NodeMod mod = n->getValue();*/
271 if(temp_mods.get(p, &mod))
273 if(mod.type == NODEMOD_CHANGECONTENT)
275 MapNode mn2(mod.param);
276 spec = mn2.getTile(face_dir, tsrc);
278 if(mod.type == NODEMOD_CRACK)
281 Get texture id, translate it to name, append stuff to
285 // Get original texture name
286 u32 orig_id = spec.texture.id;
287 std::string orig_name = tsrc->getTextureName(orig_id);
289 // Create new texture name
290 std::ostringstream os;
291 os<<orig_name<<"^[crack"<<mod.param;
294 u32 new_id = tsrc->getTextureId(os.str());
296 /*dstream<<"MapBlock::getNodeTile(): Switching from "
297 <<orig_name<<" to "<<os.str()<<" ("
298 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
300 spec.texture = tsrc->getTexture(new_id);
307 content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
310 Check temporary modifications on this node
312 /*core::map<v3s16, NodeMod>::Node *n;
313 n = m_temp_mods.find(p);
317 struct NodeMod mod = n->getValue();*/
319 if(temp_mods.get(p, &mod))
321 if(mod.type == NODEMOD_CHANGECONTENT)
326 if(mod.type == NODEMOD_CRACK)
329 Content doesn't change.
331 face_contents works just like it should, because
332 there should not be faces between differently cracked
335 If a semi-transparent node is cracked in front an
336 another one, it really doesn't matter whether there
337 is a cracked face drawn in between or not.
342 return mn.getContent();
356 // Calculate lighting at the XYZ- corner of p
357 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
359 u16 ambient_occlusion = 0;
362 for(u32 i=0; i<8; i++)
364 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
365 if(content_features(n).param_type == CPT_LIGHT
366 // Fast-style leaves look better this way
367 && content_features(n).solidness != 2)
369 light += decode_light(n.getLightBlend(daynight_ratio));
374 if(n.getContent() != CONTENT_IGNORE)
382 light /= light_count;
384 if(ambient_occlusion > 4)
386 ambient_occlusion -= 4;
387 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
393 // Calculate lighting at the given corner of p
394 u8 getSmoothLight(v3s16 p, v3s16 corner,
395 VoxelManipulator &vmanip, u32 daynight_ratio)
397 if(corner.X == 1) p.X += 1;
398 else assert(corner.X == -1);
399 if(corner.Y == 1) p.Y += 1;
400 else assert(corner.Y == -1);
401 if(corner.Z == 1) p.Z += 1;
402 else assert(corner.Z == -1);
404 return getSmoothLight(p, vmanip, daynight_ratio);
409 v3s16 blockpos_nodes,
413 VoxelManipulator &vmanip,
414 NodeModMap &temp_mods,
415 bool smooth_lighting,
416 ITextureSource *tsrc,
420 v3s16 &face_dir_corrected,
425 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
426 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
427 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods, tsrc);
428 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods, tsrc);
431 content_t content0 = getNodeContent(p, n0, temp_mods);
432 content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
433 bool equivalent = false;
434 u8 mf = face_contents(content0, content1, &equivalent);
448 face_dir_corrected = face_dir;
453 p_corrected = p + face_dir;
454 face_dir_corrected = -face_dir;
457 // eg. water and glass
459 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
461 if(smooth_lighting == false)
463 lights[0] = lights[1] = lights[2] = lights[3] =
464 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
468 v3s16 vertex_dirs[4];
469 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
470 for(u16 i=0; i<4; i++)
472 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
473 vertex_dirs[i], vmanip, daynight_ratio);
482 translate_dir: unit vector with only one of x, y or z
483 face_dir: unit vector with only one of x, y or z
485 void updateFastFaceRow(
494 core::array<FastFace> &dest,
495 NodeModMap &temp_mods,
496 VoxelManipulator &vmanip,
497 v3s16 blockpos_nodes,
498 bool smooth_lighting,
499 ITextureSource *tsrc)
503 u16 continuous_tiles_count = 0;
505 bool makes_face = false;
507 v3s16 face_dir_corrected;
508 u8 lights[4] = {0,0,0,0};
510 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
511 vmanip, temp_mods, smooth_lighting, tsrc,
512 makes_face, p_corrected, face_dir_corrected, lights, tile);
514 for(u16 j=0; j<length; j++)
516 // If tiling can be done, this is set to false in the next step
517 bool next_is_different = true;
521 bool next_makes_face = false;
522 v3s16 next_p_corrected;
523 v3s16 next_face_dir_corrected;
524 u8 next_lights[4] = {0,0,0,0};
527 // If at last position, there is nothing to compare to and
528 // the face must be drawn anyway
531 p_next = p + translate_dir;
533 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
534 vmanip, temp_mods, smooth_lighting, tsrc,
535 next_makes_face, next_p_corrected,
536 next_face_dir_corrected, next_lights,
539 if(next_makes_face == makes_face
540 && next_p_corrected == p_corrected + translate_dir
541 && next_face_dir_corrected == face_dir_corrected
542 && next_lights[0] == lights[0]
543 && next_lights[1] == lights[1]
544 && next_lights[2] == lights[2]
545 && next_lights[3] == lights[3]
546 && next_tile == tile)
548 next_is_different = false;
552 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
553 next_makes_face != makes_face ? 1 : 0);
554 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
555 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
556 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
557 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
558 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
559 (next_lights[0] != lights[0] ||
560 next_lights[0] != lights[0] ||
561 next_lights[0] != lights[0] ||
562 next_lights[0] != lights[0]) ? 1 : 0);
563 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
564 !(next_tile == tile) ? 1 : 0);
567 /*g_profiler->add("Meshgen: Total faces checked", 1);
569 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
572 g_profiler->add("Meshgen: diff: last position", 1);*/
575 continuous_tiles_count++;
577 // This is set to true if the texture doesn't allow more tiling
578 bool end_of_texture = false;
580 If there is no texture, it can be tiled infinitely.
581 If tiled==0, it means the texture can be tiled infinitely.
582 Otherwise check tiled agains continuous_tiles_count.
584 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
586 if(tile.texture.tiled <= continuous_tiles_count)
587 end_of_texture = true;
590 // Do this to disable tiling textures
591 //end_of_texture = true; //DEBUG
593 if(next_is_different || end_of_texture)
596 Create a face if there should be one
600 // Floating point conversion of the position vector
601 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
602 // Center point of face (kind of)
603 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
604 if(continuous_tiles_count != 1)
605 sp += translate_dir_f;
608 if(translate_dir.X != 0)
610 scale.X = continuous_tiles_count;
612 if(translate_dir.Y != 0)
614 scale.Y = continuous_tiles_count;
616 if(translate_dir.Z != 0)
618 scale.Z = continuous_tiles_count;
621 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
622 sp, face_dir_corrected, scale,
623 posRelative_f, dest);
625 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
626 for(int i=1; i<continuous_tiles_count; i++){
627 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
631 continuous_tiles_count = 0;
633 makes_face = next_makes_face;
634 p_corrected = next_p_corrected;
635 face_dir_corrected = next_face_dir_corrected;
636 lights[0] = next_lights[0];
637 lights[1] = next_lights[1];
638 lights[2] = next_lights[2];
639 lights[3] = next_lights[3];
647 scene::SMesh* makeMapBlockMesh(MeshMakeData *data, ITextureSource *tsrc)
649 // 4-21ms for MAP_BLOCKSIZE=16
650 // 24-155ms for MAP_BLOCKSIZE=32
651 //TimeTaker timer1("makeMapBlockMesh()");
653 core::array<FastFace> fastfaces_new;
655 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
657 // floating point conversion
658 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
663 //bool new_style_water = g_settings->getBool("new_style_water");
664 //bool new_style_leaves = g_settings->getBool("new_style_leaves");
665 bool smooth_lighting = g_settings->getBool("smooth_lighting");
668 We are including the faces of the trailing edges of the block.
669 This means that when something changes, the caller must
670 also update the meshes of the blocks at the leading edges.
672 NOTE: This is the slowest part of this method.
676 // 4-23ms for MAP_BLOCKSIZE=16
677 //TimeTaker timer2("updateMesh() collect");
680 Go through every y,z and get top(y+) faces in rows of x+
682 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
683 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
684 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
685 v3s16(0,y,z), MAP_BLOCKSIZE,
688 v3s16(0,1,0), //face dir
699 Go through every x,y and get right(x+) faces in rows of z+
701 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
702 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
703 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
704 v3s16(x,y,0), MAP_BLOCKSIZE,
718 Go through every y,z and get back(z+) faces in rows of x+
720 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
721 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
722 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
723 v3s16(0,y,z), MAP_BLOCKSIZE,
741 Convert FastFaces to SMesh
744 MeshCollector collector;
746 if(fastfaces_new.size() > 0)
748 // avg 0ms (100ms spikes when loading textures the first time)
749 //TimeTaker timer2("updateMesh() mesh building");
751 video::SMaterial material;
752 material.setFlag(video::EMF_LIGHTING, false);
753 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
754 material.setFlag(video::EMF_BILINEAR_FILTER, false);
755 material.setFlag(video::EMF_FOG_ENABLE, true);
756 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
757 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
758 material.MaterialType
759 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
761 for(u32 i=0; i<fastfaces_new.size(); i++)
763 FastFace &f = fastfaces_new[i];
765 const u16 indices[] = {0,1,2,2,3,0};
766 const u16 indices_alternate[] = {0,1,3,2,3,1};
768 video::ITexture *texture = f.tile.texture.atlas;
772 material.setTexture(0, texture);
774 f.tile.applyMaterialOptions(material);
776 const u16 *indices_p = indices;
779 Revert triangles for nicer looking gradient if vertices
780 1 and 3 have same color or 0 and 2 have different color.
782 if(f.vertices[0].Color != f.vertices[2].Color
783 || f.vertices[1].Color == f.vertices[3].Color)
784 indices_p = indices_alternate;
786 collector.append(material, f.vertices, 4, indices_p, 6);
791 Add special graphics:
798 mapblock_mesh_generate_special(data, collector, tsrc);
801 Add stuff from collector to mesh
804 scene::SMesh *mesh_new = NULL;
805 mesh_new = new scene::SMesh();
807 collector.fillMesh(mesh_new);
810 Do some stuff to the mesh
813 mesh_new->recalculateBoundingBox();
816 Delete new mesh if it is empty
819 if(mesh_new->getMeshBufferCount() == 0)
828 // Usually 1-700 faces and 1-7 materials
829 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
830 <<"and uses "<<mesh_new->getMeshBufferCount()
831 <<" materials (meshbuffers)"<<std::endl;
834 // Use VBO for mesh (this just would set this for ever buffer)
835 // This will lead to infinite memory usage because or irrlicht.
836 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
839 NOTE: If that is enabled, some kind of a queue to the main
840 thread should be made which would call irrlicht to delete
841 the hardware buffer and then delete the mesh
847 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;