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 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
30 m_daynight_ratio = daynight_ratio;
31 m_blockpos = block->getPos();
33 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
36 There is no harm not copying the TempMods of the neighbors
37 because they are already copied to this block
40 block->copyTempMods(m_temp_mods);
46 // Allocate this block + neighbors
48 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
49 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
52 //TimeTaker timer("copy central block data");
56 block->copyTo(m_vmanip);
59 //TimeTaker timer("copy neighbor block data");
63 Copy neighbors. This is lightning fast.
64 Copying only the borders would be *very* slow.
68 Map *map = block->getParent();
70 for(u16 i=0; i<6; i++)
72 const v3s16 &dir = g_6dirs[i];
73 v3s16 bp = m_blockpos + dir;
74 MapBlock *b = map->getBlockNoCreateNoEx(bp);
84 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
87 If looked from outside the node towards the face, the corners are:
93 if(dir == v3s16(0,0,1))
95 // If looking towards z+, this is the face that is behind
96 // the center point, facing towards z+.
97 vertex_dirs[0] = v3s16(-1,-1, 1);
98 vertex_dirs[1] = v3s16( 1,-1, 1);
99 vertex_dirs[2] = v3s16( 1, 1, 1);
100 vertex_dirs[3] = v3s16(-1, 1, 1);
102 else if(dir == v3s16(0,0,-1))
105 vertex_dirs[0] = v3s16( 1,-1,-1);
106 vertex_dirs[1] = v3s16(-1,-1,-1);
107 vertex_dirs[2] = v3s16(-1, 1,-1);
108 vertex_dirs[3] = v3s16( 1, 1,-1);
110 else if(dir == v3s16(1,0,0))
113 vertex_dirs[0] = v3s16( 1,-1, 1);
114 vertex_dirs[1] = v3s16( 1,-1,-1);
115 vertex_dirs[2] = v3s16( 1, 1,-1);
116 vertex_dirs[3] = v3s16( 1, 1, 1);
118 else if(dir == v3s16(-1,0,0))
121 vertex_dirs[0] = v3s16(-1,-1,-1);
122 vertex_dirs[1] = v3s16(-1,-1, 1);
123 vertex_dirs[2] = v3s16(-1, 1, 1);
124 vertex_dirs[3] = v3s16(-1, 1,-1);
126 else if(dir == v3s16(0,1,0))
128 // faces towards Y+ (assume Z- as "down" in texture)
129 vertex_dirs[0] = v3s16( 1, 1,-1);
130 vertex_dirs[1] = v3s16(-1, 1,-1);
131 vertex_dirs[2] = v3s16(-1, 1, 1);
132 vertex_dirs[3] = v3s16( 1, 1, 1);
134 else if(dir == v3s16(0,-1,0))
136 // faces towards Y- (assume Z+ as "down" in texture)
137 vertex_dirs[0] = v3s16( 1,-1, 1);
138 vertex_dirs[1] = v3s16(-1,-1, 1);
139 vertex_dirs[2] = v3s16(-1,-1,-1);
140 vertex_dirs[3] = v3s16( 1,-1,-1);
144 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
147 return video::SColor(alpha,light,light,light);
149 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
150 /*return video::SColor(alpha,light,light,MYMAX(0,
151 pow((float)light/255.0, 0.8)*255.0));*/
153 // Emphase blue a bit in darker places
157 return video::SColor(alpha,light,light,light);
159 return video::SColor(alpha,light,light,MYMAX(0,
160 pow((float)light/lim, power)*lim));
167 video::S3DVertex vertices[4]; // Precalculated vertices
170 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
171 v3s16 dir, v3f scale, v3f posRelative_f,
172 core::array<FastFace> &dest)
176 // Position is at the center of the cube.
181 v3s16 vertex_dirs[4];
182 getNodeVertexDirs(dir, vertex_dirs);
183 for(u16 i=0; i<4; i++)
186 BS/2*vertex_dirs[i].X,
187 BS/2*vertex_dirs[i].Y,
188 BS/2*vertex_dirs[i].Z
192 for(u16 i=0; i<4; i++)
194 vertex_pos[i].X *= scale.X;
195 vertex_pos[i].Y *= scale.Y;
196 vertex_pos[i].Z *= scale.Z;
197 vertex_pos[i] += pos + posRelative_f;
201 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
202 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
203 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
205 v3f zerovector = v3f(0,0,0);
207 u8 alpha = tile.alpha;
209 if(tile.id == TILE_WATER)
210 alpha = WATER_ALPHA;*/
212 float x0 = tile.texture.pos.X;
213 float y0 = tile.texture.pos.Y;
214 float w = tile.texture.size.X;
215 float h = tile.texture.size.Y;
217 /*video::SColor c = MapBlock_LightColor(alpha, li);
219 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
220 core::vector2d<f32>(x0+w*abs_scale, y0+h));
221 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
222 core::vector2d<f32>(x0, y0+h));
223 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
224 core::vector2d<f32>(x0, y0));
225 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
226 core::vector2d<f32>(x0+w*abs_scale, y0));*/
228 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
229 MapBlock_LightColor(alpha, li0),
230 core::vector2d<f32>(x0+w*abs_scale, y0+h));
231 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
232 MapBlock_LightColor(alpha, li1),
233 core::vector2d<f32>(x0, y0+h));
234 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
235 MapBlock_LightColor(alpha, li2),
236 core::vector2d<f32>(x0, y0));
237 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
238 MapBlock_LightColor(alpha, li3),
239 core::vector2d<f32>(x0+w*abs_scale, y0));
243 //f->tile = TILE_STONE;
245 dest.push_back(face);
249 Gets node tile from any place relative to block.
250 Returns TILE_NODE if doesn't exist or should not be drawn.
252 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
253 NodeModMap &temp_mods)
256 spec = mn.getTile(face_dir);
259 Check temporary modifications on this node
261 /*core::map<v3s16, NodeMod>::Node *n;
262 n = m_temp_mods.find(p);
266 struct NodeMod mod = n->getValue();*/
268 if(temp_mods.get(p, &mod))
270 if(mod.type == NODEMOD_CHANGECONTENT)
272 MapNode mn2(mod.param);
273 spec = mn2.getTile(face_dir);
275 if(mod.type == NODEMOD_CRACK)
278 Get texture id, translate it to name, append stuff to
282 // Get original texture name
283 u32 orig_id = spec.texture.id;
284 std::string orig_name = g_texturesource->getTextureName(orig_id);
286 // Create new texture name
287 std::ostringstream os;
288 os<<orig_name<<"^[crack"<<mod.param;
291 u32 new_id = g_texturesource->getTextureId(os.str());
293 /*dstream<<"MapBlock::getNodeTile(): Switching from "
294 <<orig_name<<" to "<<os.str()<<" ("
295 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
297 spec.texture = g_texturesource->getTexture(new_id);
304 content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
307 Check temporary modifications on this node
309 /*core::map<v3s16, NodeMod>::Node *n;
310 n = m_temp_mods.find(p);
314 struct NodeMod mod = n->getValue();*/
316 if(temp_mods.get(p, &mod))
318 if(mod.type == NODEMOD_CHANGECONTENT)
323 if(mod.type == NODEMOD_CRACK)
326 Content doesn't change.
328 face_contents works just like it should, because
329 there should not be faces between differently cracked
332 If a semi-transparent node is cracked in front an
333 another one, it really doesn't matter whether there
334 is a cracked face drawn in between or not.
339 return mn.getContent();
353 // Calculate lighting at the XYZ- corner of p
354 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
356 u16 ambient_occlusion = 0;
359 for(u32 i=0; i<8; i++)
361 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
362 if(content_features(n).param_type == CPT_LIGHT
363 // Fast-style leaves look better this way
364 && content_features(n).solidness != 2)
366 light += decode_light(n.getLightBlend(daynight_ratio));
371 if(n.getContent() != CONTENT_IGNORE)
379 light /= light_count;
381 if(ambient_occlusion > 4)
383 ambient_occlusion -= 4;
384 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
390 // Calculate lighting at the given corner of p
391 u8 getSmoothLight(v3s16 p, v3s16 corner,
392 VoxelManipulator &vmanip, u32 daynight_ratio)
394 if(corner.X == 1) p.X += 1;
395 else assert(corner.X == -1);
396 if(corner.Y == 1) p.Y += 1;
397 else assert(corner.Y == -1);
398 if(corner.Z == 1) p.Z += 1;
399 else assert(corner.Z == -1);
401 return getSmoothLight(p, vmanip, daynight_ratio);
406 v3s16 blockpos_nodes,
410 VoxelManipulator &vmanip,
411 NodeModMap &temp_mods,
412 bool smooth_lighting,
416 v3s16 &face_dir_corrected,
421 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
422 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
423 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
424 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
427 content_t content0 = getNodeContent(p, n0, temp_mods);
428 content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
429 u8 mf = face_contents(content0, content1);
443 face_dir_corrected = face_dir;
448 p_corrected = p + face_dir;
449 face_dir_corrected = -face_dir;
452 if(smooth_lighting == false)
454 lights[0] = lights[1] = lights[2] = lights[3] =
455 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
459 v3s16 vertex_dirs[4];
460 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
461 for(u16 i=0; i<4; i++)
463 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
464 vertex_dirs[i], vmanip, daynight_ratio);
473 translate_dir: unit vector with only one of x, y or z
474 face_dir: unit vector with only one of x, y or z
476 void updateFastFaceRow(
485 core::array<FastFace> &dest,
486 NodeModMap &temp_mods,
487 VoxelManipulator &vmanip,
488 v3s16 blockpos_nodes,
489 bool smooth_lighting)
493 u16 continuous_tiles_count = 0;
495 bool makes_face = false;
497 v3s16 face_dir_corrected;
498 u8 lights[4] = {0,0,0,0};
500 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
501 vmanip, temp_mods, smooth_lighting,
502 makes_face, p_corrected, face_dir_corrected, lights, tile);
504 for(u16 j=0; j<length; j++)
506 // If tiling can be done, this is set to false in the next step
507 bool next_is_different = true;
511 bool next_makes_face = false;
512 v3s16 next_p_corrected;
513 v3s16 next_face_dir_corrected;
514 u8 next_lights[4] = {0,0,0,0};
517 // If at last position, there is nothing to compare to and
518 // the face must be drawn anyway
521 p_next = p + translate_dir;
523 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
524 vmanip, temp_mods, smooth_lighting,
525 next_makes_face, next_p_corrected,
526 next_face_dir_corrected, next_lights,
529 if(next_makes_face == makes_face
530 && next_p_corrected == p_corrected
531 && next_face_dir_corrected == face_dir_corrected
532 && next_lights[0] == lights[0]
533 && next_lights[1] == lights[1]
534 && next_lights[2] == lights[2]
535 && next_lights[3] == lights[3]
536 && next_tile == tile)
538 next_is_different = false;
542 continuous_tiles_count++;
544 // This is set to true if the texture doesn't allow more tiling
545 bool end_of_texture = false;
547 If there is no texture, it can be tiled infinitely.
548 If tiled==0, it means the texture can be tiled infinitely.
549 Otherwise check tiled agains continuous_tiles_count.
551 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
553 if(tile.texture.tiled <= continuous_tiles_count)
554 end_of_texture = true;
557 // Do this to disable tiling textures
558 //end_of_texture = true; //DEBUG
560 if(next_is_different || end_of_texture)
563 Create a face if there should be one
567 // Floating point conversion of the position vector
568 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
569 // Center point of face (kind of)
570 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
573 if(translate_dir.X != 0)
575 scale.X = continuous_tiles_count;
577 if(translate_dir.Y != 0)
579 scale.Y = continuous_tiles_count;
581 if(translate_dir.Z != 0)
583 scale.Z = continuous_tiles_count;
586 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
587 sp, face_dir_corrected, scale,
588 posRelative_f, dest);
591 continuous_tiles_count = 0;
593 makes_face = next_makes_face;
594 p_corrected = next_p_corrected;
595 face_dir_corrected = next_face_dir_corrected;
596 lights[0] = next_lights[0];
597 lights[1] = next_lights[1];
598 lights[2] = next_lights[2];
599 lights[3] = next_lights[3];
607 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
609 // 4-21ms for MAP_BLOCKSIZE=16
610 // 24-155ms for MAP_BLOCKSIZE=32
611 //TimeTaker timer1("makeMapBlockMesh()");
613 core::array<FastFace> fastfaces_new;
615 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
617 // floating point conversion
618 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
623 //bool new_style_water = g_settings->getBool("new_style_water");
624 //bool new_style_leaves = g_settings->getBool("new_style_leaves");
625 bool smooth_lighting = g_settings->getBool("smooth_lighting");
628 We are including the faces of the trailing edges of the block.
629 This means that when something changes, the caller must
630 also update the meshes of the blocks at the leading edges.
632 NOTE: This is the slowest part of this method.
636 // 4-23ms for MAP_BLOCKSIZE=16
637 //TimeTaker timer2("updateMesh() collect");
640 Go through every y,z and get top(y+) faces in rows of x+
642 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
643 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
644 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
645 v3s16(0,y,z), MAP_BLOCKSIZE,
648 v3s16(0,1,0), //face dir
658 Go through every x,y and get right(x+) faces in rows of z+
660 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
661 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
662 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
663 v3s16(x,y,0), MAP_BLOCKSIZE,
676 Go through every y,z and get back(z+) faces in rows of x+
678 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
679 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
680 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
681 v3s16(0,y,z), MAP_BLOCKSIZE,
698 Convert FastFaces to SMesh
701 MeshCollector collector;
703 if(fastfaces_new.size() > 0)
705 // avg 0ms (100ms spikes when loading textures the first time)
706 //TimeTaker timer2("updateMesh() mesh building");
708 video::SMaterial material;
709 material.setFlag(video::EMF_LIGHTING, false);
710 material.setFlag(video::EMF_BILINEAR_FILTER, false);
711 material.setFlag(video::EMF_FOG_ENABLE, true);
712 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
713 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
715 for(u32 i=0; i<fastfaces_new.size(); i++)
717 FastFace &f = fastfaces_new[i];
719 const u16 indices[] = {0,1,2,2,3,0};
720 const u16 indices_alternate[] = {0,1,3,2,3,1};
722 video::ITexture *texture = f.tile.texture.atlas;
726 material.setTexture(0, texture);
728 f.tile.applyMaterialOptions(material);
730 const u16 *indices_p = indices;
733 Revert triangles for nicer looking gradient if vertices
734 1 and 3 have same color or 0 and 2 have different color.
736 if(f.vertices[0].Color != f.vertices[2].Color
737 || f.vertices[1].Color == f.vertices[3].Color)
738 indices_p = indices_alternate;
740 collector.append(material, f.vertices, 4, indices_p, 6);
745 Add special graphics:
752 mapblock_mesh_generate_special(data, collector);
755 Add stuff from collector to mesh
758 scene::SMesh *mesh_new = NULL;
759 mesh_new = new scene::SMesh();
761 collector.fillMesh(mesh_new);
764 Do some stuff to the mesh
767 mesh_new->recalculateBoundingBox();
770 Delete new mesh if it is empty
773 if(mesh_new->getMeshBufferCount() == 0)
782 // Usually 1-700 faces and 1-7 materials
783 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
784 <<"and uses "<<mesh_new->getMeshBufferCount()
785 <<" materials (meshbuffers)"<<std::endl;
788 // Use VBO for mesh (this just would set this for ever buffer)
789 // This will lead to infinite memory usage because or irrlicht.
790 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
793 NOTE: If that is enabled, some kind of a queue to the main
794 thread should be made which would call irrlicht to delete
795 the hardware buffer and then delete the mesh
801 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;