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"
27 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
29 m_daynight_ratio = daynight_ratio;
30 m_blockpos = block->getPos();
32 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
35 There is no harm not copying the TempMods of the neighbors
36 because they are already copied to this block
39 block->copyTempMods(m_temp_mods);
45 // Allocate this block + neighbors
47 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
48 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
51 //TimeTaker timer("copy central block data");
55 block->copyTo(m_vmanip);
58 //TimeTaker timer("copy neighbor block data");
62 Copy neighbors. This is lightning fast.
63 Copying only the borders would be *very* slow.
67 Map *map = block->getParent();
69 for(u16 i=0; i<6; i++)
71 const v3s16 &dir = g_6dirs[i];
72 v3s16 bp = m_blockpos + dir;
73 MapBlock *b = map->getBlockNoCreateNoEx(bp);
83 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
86 If looked from outside the node towards the face, the corners are:
92 if(dir == v3s16(0,0,1))
94 // If looking towards z+, this is the face that is behind
95 // the center point, facing towards z+.
96 vertex_dirs[0] = v3s16(-1,-1, 1);
97 vertex_dirs[1] = v3s16( 1,-1, 1);
98 vertex_dirs[2] = v3s16( 1, 1, 1);
99 vertex_dirs[3] = v3s16(-1, 1, 1);
101 else if(dir == v3s16(0,0,-1))
104 vertex_dirs[0] = v3s16( 1,-1,-1);
105 vertex_dirs[1] = v3s16(-1,-1,-1);
106 vertex_dirs[2] = v3s16(-1, 1,-1);
107 vertex_dirs[3] = v3s16( 1, 1,-1);
109 else if(dir == v3s16(1,0,0))
112 vertex_dirs[0] = v3s16( 1,-1, 1);
113 vertex_dirs[1] = v3s16( 1,-1,-1);
114 vertex_dirs[2] = v3s16( 1, 1,-1);
115 vertex_dirs[3] = v3s16( 1, 1, 1);
117 else if(dir == v3s16(-1,0,0))
120 vertex_dirs[0] = v3s16(-1,-1,-1);
121 vertex_dirs[1] = v3s16(-1,-1, 1);
122 vertex_dirs[2] = v3s16(-1, 1, 1);
123 vertex_dirs[3] = v3s16(-1, 1,-1);
125 else if(dir == v3s16(0,1,0))
127 // faces towards Y+ (assume Z- as "down" in texture)
128 vertex_dirs[0] = v3s16( 1, 1,-1);
129 vertex_dirs[1] = v3s16(-1, 1,-1);
130 vertex_dirs[2] = v3s16(-1, 1, 1);
131 vertex_dirs[3] = v3s16( 1, 1, 1);
133 else if(dir == v3s16(0,-1,0))
135 // faces towards Y- (assume Z+ as "down" in texture)
136 vertex_dirs[0] = v3s16( 1,-1, 1);
137 vertex_dirs[1] = v3s16(-1,-1, 1);
138 vertex_dirs[2] = v3s16(-1,-1,-1);
139 vertex_dirs[3] = v3s16( 1,-1,-1);
143 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
146 return video::SColor(alpha,light,light,light);
148 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
149 /*return video::SColor(alpha,light,light,MYMAX(0,
150 pow((float)light/255.0, 0.8)*255.0));*/
152 // Emphase blue a bit in darker places
156 return video::SColor(alpha,light,light,light);
158 return video::SColor(alpha,light,light,MYMAX(0,
159 pow((float)light/lim, power)*lim));
166 video::S3DVertex vertices[4]; // Precalculated vertices
169 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
170 v3s16 dir, v3f scale, v3f posRelative_f,
171 core::array<FastFace> &dest)
175 // Position is at the center of the cube.
180 v3s16 vertex_dirs[4];
181 getNodeVertexDirs(dir, vertex_dirs);
182 for(u16 i=0; i<4; i++)
185 BS/2*vertex_dirs[i].X,
186 BS/2*vertex_dirs[i].Y,
187 BS/2*vertex_dirs[i].Z
191 for(u16 i=0; i<4; i++)
193 vertex_pos[i].X *= scale.X;
194 vertex_pos[i].Y *= scale.Y;
195 vertex_pos[i].Z *= scale.Z;
196 vertex_pos[i] += pos + posRelative_f;
200 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
201 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
202 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
204 v3f zerovector = v3f(0,0,0);
206 u8 alpha = tile.alpha;
208 if(tile.id == TILE_WATER)
209 alpha = WATER_ALPHA;*/
211 float x0 = tile.texture.pos.X;
212 float y0 = tile.texture.pos.Y;
213 float w = tile.texture.size.X;
214 float h = tile.texture.size.Y;
216 /*video::SColor c = MapBlock_LightColor(alpha, li);
218 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
219 core::vector2d<f32>(x0+w*abs_scale, y0+h));
220 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
221 core::vector2d<f32>(x0, y0+h));
222 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
223 core::vector2d<f32>(x0, y0));
224 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
225 core::vector2d<f32>(x0+w*abs_scale, y0));*/
227 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
228 MapBlock_LightColor(alpha, li0),
229 core::vector2d<f32>(x0+w*abs_scale, y0+h));
230 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
231 MapBlock_LightColor(alpha, li1),
232 core::vector2d<f32>(x0, y0+h));
233 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
234 MapBlock_LightColor(alpha, li2),
235 core::vector2d<f32>(x0, y0));
236 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
237 MapBlock_LightColor(alpha, li3),
238 core::vector2d<f32>(x0+w*abs_scale, y0));
242 //f->tile = TILE_STONE;
244 dest.push_back(face);
248 Gets node tile from any place relative to block.
249 Returns TILE_NODE if doesn't exist or should not be drawn.
251 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
252 NodeModMap &temp_mods)
255 spec = mn.getTile(face_dir);
258 Check temporary modifications on this node
260 /*core::map<v3s16, NodeMod>::Node *n;
261 n = m_temp_mods.find(p);
265 struct NodeMod mod = n->getValue();*/
267 if(temp_mods.get(p, &mod))
269 if(mod.type == NODEMOD_CHANGECONTENT)
271 MapNode mn2(mod.param);
272 spec = mn2.getTile(face_dir);
274 if(mod.type == NODEMOD_CRACK)
277 Get texture id, translate it to name, append stuff to
281 // Get original texture name
282 u32 orig_id = spec.texture.id;
283 std::string orig_name = g_texturesource->getTextureName(orig_id);
285 // Create new texture name
286 std::ostringstream os;
287 os<<orig_name<<"^[crack"<<mod.param;
290 u32 new_id = g_texturesource->getTextureId(os.str());
292 /*dstream<<"MapBlock::getNodeTile(): Switching from "
293 <<orig_name<<" to "<<os.str()<<" ("
294 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
296 spec.texture = g_texturesource->getTexture(new_id);
303 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
306 Check temporary modifications on this node
308 /*core::map<v3s16, NodeMod>::Node *n;
309 n = m_temp_mods.find(p);
313 struct NodeMod mod = n->getValue();*/
315 if(temp_mods.get(p, &mod))
317 if(mod.type == NODEMOD_CHANGECONTENT)
322 if(mod.type == NODEMOD_CRACK)
325 Content doesn't change.
327 face_contents works just like it should, because
328 there should not be faces between differently cracked
331 If a semi-transparent node is cracked in front an
332 another one, it really doesn't matter whether there
333 is a cracked face drawn in between or not.
352 // Calculate lighting at the XYZ- corner of p
353 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
355 u16 ambient_occlusion = 0;
358 for(u32 i=0; i<8; i++)
360 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
361 if(content_features(n.d).param_type == CPT_LIGHT
362 // Fast-style leaves look better this way
363 && content_features(n.d).solidness != 2)
365 light += decode_light(n.getLightBlend(daynight_ratio));
370 if(n.d != CONTENT_IGNORE)
378 light /= light_count;
380 if(ambient_occlusion > 4)
382 ambient_occlusion -= 4;
383 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
389 // Calculate lighting at the given corner of p
390 u8 getSmoothLight(v3s16 p, v3s16 corner,
391 VoxelManipulator &vmanip, u32 daynight_ratio)
393 if(corner.X == 1) p.X += 1;
394 else assert(corner.X == -1);
395 if(corner.Y == 1) p.Y += 1;
396 else assert(corner.Y == -1);
397 if(corner.Z == 1) p.Z += 1;
398 else assert(corner.Z == -1);
400 return getSmoothLight(p, vmanip, daynight_ratio);
405 v3s16 blockpos_nodes,
409 VoxelManipulator &vmanip,
410 NodeModMap &temp_mods,
411 bool smooth_lighting,
415 v3s16 &face_dir_corrected,
420 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
421 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
422 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
423 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
426 u8 content0 = getNodeContent(p, n0, temp_mods);
427 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
428 u8 mf = face_contents(content0, content1);
442 face_dir_corrected = face_dir;
447 p_corrected = p + face_dir;
448 face_dir_corrected = -face_dir;
451 if(smooth_lighting == false)
453 lights[0] = lights[1] = lights[2] = lights[3] =
454 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
458 v3s16 vertex_dirs[4];
459 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
460 for(u16 i=0; i<4; i++)
462 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
463 vertex_dirs[i], vmanip, daynight_ratio);
472 translate_dir: unit vector with only one of x, y or z
473 face_dir: unit vector with only one of x, y or z
475 void updateFastFaceRow(
484 core::array<FastFace> &dest,
485 NodeModMap &temp_mods,
486 VoxelManipulator &vmanip,
487 v3s16 blockpos_nodes,
488 bool smooth_lighting)
492 u16 continuous_tiles_count = 0;
496 v3s16 face_dir_corrected;
499 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
500 vmanip, temp_mods, smooth_lighting,
501 makes_face, p_corrected, face_dir_corrected, lights, tile);
503 for(u16 j=0; j<length; j++)
505 // If tiling can be done, this is set to false in the next step
506 bool next_is_different = true;
510 bool next_makes_face = false;
511 v3s16 next_p_corrected;
512 v3s16 next_face_dir_corrected;
513 u8 next_lights[4] = {0,0,0,0};
516 // If at last position, there is nothing to compare to and
517 // the face must be drawn anyway
520 p_next = p + translate_dir;
522 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
523 vmanip, temp_mods, smooth_lighting,
524 next_makes_face, next_p_corrected,
525 next_face_dir_corrected, next_lights,
528 if(next_makes_face == makes_face
529 && next_p_corrected == p_corrected
530 && next_face_dir_corrected == face_dir_corrected
531 && next_lights[0] == lights[0]
532 && next_lights[1] == lights[1]
533 && next_lights[2] == lights[2]
534 && next_lights[3] == lights[3]
535 && next_tile == tile)
537 next_is_different = false;
541 continuous_tiles_count++;
543 // This is set to true if the texture doesn't allow more tiling
544 bool end_of_texture = false;
546 If there is no texture, it can be tiled infinitely.
547 If tiled==0, it means the texture can be tiled infinitely.
548 Otherwise check tiled agains continuous_tiles_count.
550 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
552 if(tile.texture.tiled <= continuous_tiles_count)
553 end_of_texture = true;
556 // Do this to disable tiling textures
557 //end_of_texture = true; //DEBUG
559 if(next_is_different || end_of_texture)
562 Create a face if there should be one
566 // Floating point conversion of the position vector
567 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
568 // Center point of face (kind of)
569 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
572 if(translate_dir.X != 0)
574 scale.X = continuous_tiles_count;
576 if(translate_dir.Y != 0)
578 scale.Y = continuous_tiles_count;
580 if(translate_dir.Z != 0)
582 scale.Z = continuous_tiles_count;
585 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
586 sp, face_dir_corrected, scale,
587 posRelative_f, dest);
590 continuous_tiles_count = 0;
592 makes_face = next_makes_face;
593 p_corrected = next_p_corrected;
594 face_dir_corrected = next_face_dir_corrected;
595 lights[0] = next_lights[0];
596 lights[1] = next_lights[1];
597 lights[2] = next_lights[2];
598 lights[3] = next_lights[3];
606 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
608 // 4-21ms for MAP_BLOCKSIZE=16
609 // 24-155ms for MAP_BLOCKSIZE=32
610 //TimeTaker timer1("makeMapBlockMesh()");
612 core::array<FastFace> fastfaces_new;
614 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
616 // floating point conversion
617 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
622 //bool new_style_water = g_settings.getBool("new_style_water");
623 //bool new_style_leaves = g_settings.getBool("new_style_leaves");
624 bool smooth_lighting = g_settings.getBool("smooth_lighting");
627 We are including the faces of the trailing edges of the block.
628 This means that when something changes, the caller must
629 also update the meshes of the blocks at the leading edges.
631 NOTE: This is the slowest part of this method.
635 // 4-23ms for MAP_BLOCKSIZE=16
636 //TimeTaker timer2("updateMesh() collect");
639 Go through every y,z and get top(y+) faces in rows of x+
641 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
642 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
643 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
644 v3s16(0,y,z), MAP_BLOCKSIZE,
647 v3s16(0,1,0), //face dir
657 Go through every x,y and get right(x+) faces in rows of z+
659 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
660 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
661 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
662 v3s16(x,y,0), MAP_BLOCKSIZE,
675 Go through every y,z and get back(z+) faces in rows of x+
677 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
678 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
679 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
680 v3s16(0,y,z), MAP_BLOCKSIZE,
697 Convert FastFaces to SMesh
700 MeshCollector collector;
702 if(fastfaces_new.size() > 0)
704 // avg 0ms (100ms spikes when loading textures the first time)
705 //TimeTaker timer2("updateMesh() mesh building");
707 video::SMaterial material;
708 material.setFlag(video::EMF_LIGHTING, false);
709 material.setFlag(video::EMF_BILINEAR_FILTER, false);
710 material.setFlag(video::EMF_FOG_ENABLE, true);
711 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
712 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
714 for(u32 i=0; i<fastfaces_new.size(); i++)
716 FastFace &f = fastfaces_new[i];
718 const u16 indices[] = {0,1,2,2,3,0};
719 const u16 indices_alternate[] = {0,1,3,2,3,1};
721 video::ITexture *texture = f.tile.texture.atlas;
725 material.setTexture(0, texture);
727 f.tile.applyMaterialOptions(material);
729 const u16 *indices_p = indices;
732 Revert triangles for nicer looking gradient if vertices
733 1 and 3 have same color or 0 and 2 have different color.
735 if(f.vertices[0].Color != f.vertices[2].Color
736 || f.vertices[1].Color == f.vertices[3].Color)
737 indices_p = indices_alternate;
739 collector.append(material, f.vertices, 4, indices_p, 6);
744 Add special graphics:
751 mapblock_mesh_generate_special(data, collector);
754 Add stuff from collector to mesh
757 scene::SMesh *mesh_new = NULL;
758 mesh_new = new scene::SMesh();
760 collector.fillMesh(mesh_new);
763 Do some stuff to the mesh
766 mesh_new->recalculateBoundingBox();
769 Delete new mesh if it is empty
772 if(mesh_new->getMeshBufferCount() == 0)
781 // Usually 1-700 faces and 1-7 materials
782 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
783 <<"and uses "<<mesh_new->getMeshBufferCount()
784 <<" materials (meshbuffers)"<<std::endl;
787 // Use VBO for mesh (this just would set this for ever buffer)
788 // This will lead to infinite memory usage because or irrlicht.
789 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
792 NOTE: If that is enabled, some kind of a queue to the main
793 thread should be made which would call irrlicht to delete
794 the hardware buffer and then delete the mesh
800 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;