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 NodeContainer *parentcontainer = block->getParent();
68 // This will only work if the parent is the map
69 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
70 // OK, we have the map!
71 Map *map = (Map*)parentcontainer;
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 inline video::SColor lightColor(u8 alpha, u8 light)
149 return video::SColor(alpha,light,light,light);
155 video::S3DVertex vertices[4]; // Precalculated vertices
158 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
159 v3s16 dir, v3f scale, v3f posRelative_f,
160 core::array<FastFace> &dest)
164 // Position is at the center of the cube.
169 v3s16 vertex_dirs[4];
170 getNodeVertexDirs(dir, vertex_dirs);
171 for(u16 i=0; i<4; i++)
174 BS/2*vertex_dirs[i].X,
175 BS/2*vertex_dirs[i].Y,
176 BS/2*vertex_dirs[i].Z
180 for(u16 i=0; i<4; i++)
182 vertex_pos[i].X *= scale.X;
183 vertex_pos[i].Y *= scale.Y;
184 vertex_pos[i].Z *= scale.Z;
185 vertex_pos[i] += pos + posRelative_f;
189 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
190 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
191 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
193 v3f zerovector = v3f(0,0,0);
195 u8 alpha = tile.alpha;
197 if(tile.id == TILE_WATER)
198 alpha = WATER_ALPHA;*/
200 float x0 = tile.texture.pos.X;
201 float y0 = tile.texture.pos.Y;
202 float w = tile.texture.size.X;
203 float h = tile.texture.size.Y;
205 /*video::SColor c = lightColor(alpha, li);
207 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
208 core::vector2d<f32>(x0+w*abs_scale, y0+h));
209 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
210 core::vector2d<f32>(x0, y0+h));
211 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
212 core::vector2d<f32>(x0, y0));
213 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
214 core::vector2d<f32>(x0+w*abs_scale, y0));*/
216 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
217 lightColor(alpha, li0),
218 core::vector2d<f32>(x0+w*abs_scale, y0+h));
219 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
220 lightColor(alpha, li1),
221 core::vector2d<f32>(x0, y0+h));
222 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
223 lightColor(alpha, li2),
224 core::vector2d<f32>(x0, y0));
225 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
226 lightColor(alpha, li3),
227 core::vector2d<f32>(x0+w*abs_scale, y0));
231 //f->tile = TILE_STONE;
233 dest.push_back(face);
237 Gets node tile from any place relative to block.
238 Returns TILE_NODE if doesn't exist or should not be drawn.
240 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
241 NodeModMap &temp_mods)
244 spec = mn.getTile(face_dir);
247 Check temporary modifications on this node
249 /*core::map<v3s16, NodeMod>::Node *n;
250 n = m_temp_mods.find(p);
254 struct NodeMod mod = n->getValue();*/
256 if(temp_mods.get(p, &mod))
258 if(mod.type == NODEMOD_CHANGECONTENT)
260 MapNode mn2(mod.param);
261 spec = mn2.getTile(face_dir);
263 if(mod.type == NODEMOD_CRACK)
266 Get texture id, translate it to name, append stuff to
270 // Get original texture name
271 u32 orig_id = spec.texture.id;
272 std::string orig_name = g_texturesource->getTextureName(orig_id);
274 // Create new texture name
275 std::ostringstream os;
276 os<<orig_name<<"^[crack"<<mod.param;
279 u32 new_id = g_texturesource->getTextureId(os.str());
281 /*dstream<<"MapBlock::getNodeTile(): Switching from "
282 <<orig_name<<" to "<<os.str()<<" ("
283 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
285 spec.texture = g_texturesource->getTexture(new_id);
292 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
295 Check temporary modifications on this node
297 /*core::map<v3s16, NodeMod>::Node *n;
298 n = m_temp_mods.find(p);
302 struct NodeMod mod = n->getValue();*/
304 if(temp_mods.get(p, &mod))
306 if(mod.type == NODEMOD_CHANGECONTENT)
311 if(mod.type == NODEMOD_CRACK)
314 Content doesn't change.
316 face_contents works just like it should, because
317 there should not be faces between differently cracked
320 If a semi-transparent node is cracked in front an
321 another one, it really doesn't matter whether there
322 is a cracked face drawn in between or not.
341 // Calculate lighting at the XYZ- corner of p
342 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
344 u16 ambient_occlusion = 0;
347 for(u32 i=0; i<8; i++)
349 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
350 if(content_features(n.d).param_type == CPT_LIGHT)
352 light += decode_light(n.getLightBlend(daynight_ratio));
357 if(n.d != CONTENT_IGNORE)
365 light /= light_count;
367 if(ambient_occlusion > 4)
369 ambient_occlusion -= 4;
370 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
376 // Calculate lighting at the given corner of p
377 u8 getSmoothLight(v3s16 p, v3s16 corner,
378 VoxelManipulator &vmanip, u32 daynight_ratio)
380 if(corner.X == 1) p.X += 1;
381 else assert(corner.X == -1);
382 if(corner.Y == 1) p.Y += 1;
383 else assert(corner.Y == -1);
384 if(corner.Z == 1) p.Z += 1;
385 else assert(corner.Z == -1);
387 return getSmoothLight(p, vmanip, daynight_ratio);
392 v3s16 blockpos_nodes,
396 VoxelManipulator &vmanip,
397 NodeModMap &temp_mods,
398 bool smooth_lighting,
402 v3s16 &face_dir_corrected,
407 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
408 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
409 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
410 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
413 u8 content0 = getNodeContent(p, n0, temp_mods);
414 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
415 u8 mf = face_contents(content0, content1);
429 face_dir_corrected = face_dir;
434 p_corrected = p + face_dir;
435 face_dir_corrected = -face_dir;
438 if(smooth_lighting == false)
440 lights[0] = lights[1] = lights[2] = lights[3] =
441 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
445 v3s16 vertex_dirs[4];
446 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
447 for(u16 i=0; i<4; i++)
449 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
450 vertex_dirs[i], vmanip, daynight_ratio);
459 translate_dir: unit vector with only one of x, y or z
460 face_dir: unit vector with only one of x, y or z
462 void updateFastFaceRow(
471 core::array<FastFace> &dest,
472 NodeModMap &temp_mods,
473 VoxelManipulator &vmanip,
474 v3s16 blockpos_nodes,
475 bool smooth_lighting)
479 u16 continuous_tiles_count = 0;
483 v3s16 face_dir_corrected;
486 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
487 vmanip, temp_mods, smooth_lighting,
488 makes_face, p_corrected, face_dir_corrected, lights, tile);
490 for(u16 j=0; j<length; j++)
492 // If tiling can be done, this is set to false in the next step
493 bool next_is_different = true;
497 bool next_makes_face = false;
498 v3s16 next_p_corrected;
499 v3s16 next_face_dir_corrected;
500 u8 next_lights[4] = {0,0,0,0};
503 // If at last position, there is nothing to compare to and
504 // the face must be drawn anyway
507 p_next = p + translate_dir;
509 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
510 vmanip, temp_mods, smooth_lighting,
511 next_makes_face, next_p_corrected,
512 next_face_dir_corrected, next_lights,
515 if(next_makes_face == makes_face
516 && next_p_corrected == p_corrected
517 && next_face_dir_corrected == face_dir_corrected
518 && next_lights[0] == lights[0]
519 && next_lights[1] == lights[1]
520 && next_lights[2] == lights[2]
521 && next_lights[3] == lights[3]
522 && next_tile == tile)
524 next_is_different = false;
528 continuous_tiles_count++;
530 // This is set to true if the texture doesn't allow more tiling
531 bool end_of_texture = false;
533 If there is no texture, it can be tiled infinitely.
534 If tiled==0, it means the texture can be tiled infinitely.
535 Otherwise check tiled agains continuous_tiles_count.
537 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
539 if(tile.texture.tiled <= continuous_tiles_count)
540 end_of_texture = true;
543 // Do this to disable tiling textures
544 //end_of_texture = true; //DEBUG
546 if(next_is_different || end_of_texture)
549 Create a face if there should be one
553 // Floating point conversion of the position vector
554 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
555 // Center point of face (kind of)
556 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
559 if(translate_dir.X != 0)
561 scale.X = continuous_tiles_count;
563 if(translate_dir.Y != 0)
565 scale.Y = continuous_tiles_count;
567 if(translate_dir.Z != 0)
569 scale.Z = continuous_tiles_count;
572 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
573 sp, face_dir_corrected, scale,
574 posRelative_f, dest);
577 continuous_tiles_count = 0;
579 makes_face = next_makes_face;
580 p_corrected = next_p_corrected;
581 face_dir_corrected = next_face_dir_corrected;
582 lights[0] = next_lights[0];
583 lights[1] = next_lights[1];
584 lights[2] = next_lights[2];
585 lights[3] = next_lights[3];
593 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
595 // 4-21ms for MAP_BLOCKSIZE=16
596 // 24-155ms for MAP_BLOCKSIZE=32
597 //TimeTaker timer1("makeMapBlockMesh()");
599 core::array<FastFace> fastfaces_new;
601 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
603 // floating point conversion
604 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
609 //bool new_style_water = g_settings.getBool("new_style_water");
610 //bool new_style_leaves = g_settings.getBool("new_style_leaves");
611 bool smooth_lighting = g_settings.getBool("smooth_lighting");
614 We are including the faces of the trailing edges of the block.
615 This means that when something changes, the caller must
616 also update the meshes of the blocks at the leading edges.
618 NOTE: This is the slowest part of this method.
622 // 4-23ms for MAP_BLOCKSIZE=16
623 //TimeTaker timer2("updateMesh() collect");
626 Go through every y,z and get top(y+) faces in rows of x+
628 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
629 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
630 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
631 v3s16(0,y,z), MAP_BLOCKSIZE,
634 v3s16(0,1,0), //face dir
644 Go through every x,y and get right(x+) faces in rows of z+
646 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
647 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
648 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
649 v3s16(x,y,0), MAP_BLOCKSIZE,
662 Go through every y,z and get back(z+) faces in rows of x+
664 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
665 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
666 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
667 v3s16(0,y,z), MAP_BLOCKSIZE,
684 Convert FastFaces to SMesh
687 MeshCollector collector;
689 if(fastfaces_new.size() > 0)
691 // avg 0ms (100ms spikes when loading textures the first time)
692 //TimeTaker timer2("updateMesh() mesh building");
694 video::SMaterial material;
695 material.setFlag(video::EMF_LIGHTING, false);
696 material.setFlag(video::EMF_BILINEAR_FILTER, false);
697 material.setFlag(video::EMF_FOG_ENABLE, true);
698 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
699 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
701 for(u32 i=0; i<fastfaces_new.size(); i++)
703 FastFace &f = fastfaces_new[i];
705 const u16 indices[] = {0,1,2,2,3,0};
706 const u16 indices_alternate[] = {0,1,3,2,3,1};
708 video::ITexture *texture = f.tile.texture.atlas;
712 material.setTexture(0, texture);
714 f.tile.applyMaterialOptions(material);
716 const u16 *indices_p = indices;
719 Revert triangles for nicer looking gradient if vertices
720 1 and 3 have same color or 0 and 2 have different color.
722 if(f.vertices[0].Color != f.vertices[2].Color
723 || f.vertices[1].Color == f.vertices[3].Color)
724 indices_p = indices_alternate;
726 collector.append(material, f.vertices, 4, indices_p, 6);
731 Add special graphics:
738 mapblock_mesh_generate_special(data, collector);
741 Add stuff from collector to mesh
744 scene::SMesh *mesh_new = NULL;
745 mesh_new = new scene::SMesh();
747 collector.fillMesh(mesh_new);
750 Do some stuff to the mesh
753 mesh_new->recalculateBoundingBox();
756 Delete new mesh if it is empty
759 if(mesh_new->getMeshBufferCount() == 0)
768 // Usually 1-700 faces and 1-7 materials
769 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
770 <<"and uses "<<mesh_new->getMeshBufferCount()
771 <<" materials (meshbuffers)"<<std::endl;
774 // Use VBO for mesh (this just would set this for ever buffer)
775 // This will lead to infinite memory usage because or irrlicht.
776 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
779 NOTE: If that is enabled, some kind of a queue to the main
780 thread should be made which would call irrlicht to delete
781 the hardware buffer and then delete the mesh
787 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;