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"
30 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
32 m_daynight_ratio = daynight_ratio;
33 m_blockpos = block->getPos();
35 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
38 There is no harm not copying the TempMods of the neighbors
39 because they are already copied to this block
42 block->copyTempMods(m_temp_mods);
48 // Allocate this block + neighbors
50 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
51 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
54 //TimeTaker timer("copy central block data");
58 block->copyTo(m_vmanip);
61 //TimeTaker timer("copy neighbor block data");
65 Copy neighbors. This is lightning fast.
66 Copying only the borders would be *very* slow.
70 Map *map = block->getParent();
72 for(u16 i=0; i<6; i++)
74 const v3s16 &dir = g_6dirs[i];
75 v3s16 bp = m_blockpos + dir;
76 MapBlock *b = map->getBlockNoCreateNoEx(bp);
86 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
89 If looked from outside the node towards the face, the corners are:
95 if(dir == v3s16(0,0,1))
97 // If looking towards z+, this is the face that is behind
98 // the center point, facing towards z+.
99 vertex_dirs[0] = v3s16(-1,-1, 1);
100 vertex_dirs[1] = v3s16( 1,-1, 1);
101 vertex_dirs[2] = v3s16( 1, 1, 1);
102 vertex_dirs[3] = v3s16(-1, 1, 1);
104 else if(dir == v3s16(0,0,-1))
107 vertex_dirs[0] = v3s16( 1,-1,-1);
108 vertex_dirs[1] = v3s16(-1,-1,-1);
109 vertex_dirs[2] = v3s16(-1, 1,-1);
110 vertex_dirs[3] = v3s16( 1, 1,-1);
112 else if(dir == v3s16(1,0,0))
115 vertex_dirs[0] = v3s16( 1,-1, 1);
116 vertex_dirs[1] = v3s16( 1,-1,-1);
117 vertex_dirs[2] = v3s16( 1, 1,-1);
118 vertex_dirs[3] = v3s16( 1, 1, 1);
120 else if(dir == v3s16(-1,0,0))
123 vertex_dirs[0] = v3s16(-1,-1,-1);
124 vertex_dirs[1] = v3s16(-1,-1, 1);
125 vertex_dirs[2] = v3s16(-1, 1, 1);
126 vertex_dirs[3] = v3s16(-1, 1,-1);
128 else if(dir == v3s16(0,1,0))
130 // faces towards Y+ (assume Z- as "down" in texture)
131 vertex_dirs[0] = v3s16( 1, 1,-1);
132 vertex_dirs[1] = v3s16(-1, 1,-1);
133 vertex_dirs[2] = v3s16(-1, 1, 1);
134 vertex_dirs[3] = v3s16( 1, 1, 1);
136 else if(dir == v3s16(0,-1,0))
138 // faces towards Y- (assume Z+ as "down" in texture)
139 vertex_dirs[0] = v3s16( 1,-1, 1);
140 vertex_dirs[1] = v3s16(-1,-1, 1);
141 vertex_dirs[2] = v3s16(-1,-1,-1);
142 vertex_dirs[3] = v3s16( 1,-1,-1);
146 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
149 return video::SColor(alpha,light,light,light);
151 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
152 /*return video::SColor(alpha,light,light,MYMAX(0,
153 pow((float)light/255.0, 0.8)*255.0));*/
155 // Emphase blue a bit in darker places
159 return video::SColor(alpha,light,light,light);
161 return video::SColor(alpha,light,light,MYMAX(0,
162 pow((float)light/lim, power)*lim));
169 video::S3DVertex vertices[4]; // Precalculated vertices
172 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
173 v3s16 dir, v3f scale, v3f posRelative_f,
174 core::array<FastFace> &dest)
178 // Position is at the center of the cube.
183 v3s16 vertex_dirs[4];
184 getNodeVertexDirs(dir, vertex_dirs);
185 for(u16 i=0; i<4; i++)
188 BS/2*vertex_dirs[i].X,
189 BS/2*vertex_dirs[i].Y,
190 BS/2*vertex_dirs[i].Z
194 for(u16 i=0; i<4; i++)
196 vertex_pos[i].X *= scale.X;
197 vertex_pos[i].Y *= scale.Y;
198 vertex_pos[i].Z *= scale.Z;
199 vertex_pos[i] += pos + posRelative_f;
203 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
204 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
205 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
207 v3f zerovector = v3f(0,0,0);
209 u8 alpha = tile.alpha;
211 if(tile.id == TILE_WATER)
212 alpha = WATER_ALPHA;*/
214 float x0 = tile.texture.pos.X;
215 float y0 = tile.texture.pos.Y;
216 float w = tile.texture.size.X;
217 float h = tile.texture.size.Y;
219 /*video::SColor c = MapBlock_LightColor(alpha, li);
221 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
222 core::vector2d<f32>(x0+w*abs_scale, y0+h));
223 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
224 core::vector2d<f32>(x0, y0+h));
225 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
226 core::vector2d<f32>(x0, y0));
227 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
228 core::vector2d<f32>(x0+w*abs_scale, y0));*/
230 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
231 MapBlock_LightColor(alpha, li0),
232 core::vector2d<f32>(x0+w*abs_scale, y0+h));
233 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
234 MapBlock_LightColor(alpha, li1),
235 core::vector2d<f32>(x0, y0+h));
236 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
237 MapBlock_LightColor(alpha, li2),
238 core::vector2d<f32>(x0, y0));
239 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
240 MapBlock_LightColor(alpha, li3),
241 core::vector2d<f32>(x0+w*abs_scale, y0));
245 //f->tile = TILE_STONE;
247 dest.push_back(face);
251 Gets node tile from any place relative to block.
252 Returns TILE_NODE if doesn't exist or should not be drawn.
254 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
255 NodeModMap &temp_mods)
258 spec = mn.getTile(face_dir);
261 Check temporary modifications on this node
263 /*core::map<v3s16, NodeMod>::Node *n;
264 n = m_temp_mods.find(p);
268 struct NodeMod mod = n->getValue();*/
270 if(temp_mods.get(p, &mod))
272 if(mod.type == NODEMOD_CHANGECONTENT)
274 MapNode mn2(mod.param);
275 spec = mn2.getTile(face_dir);
277 if(mod.type == NODEMOD_CRACK)
280 Get texture id, translate it to name, append stuff to
284 // Get original texture name
285 u32 orig_id = spec.texture.id;
286 std::string orig_name = g_texturesource->getTextureName(orig_id);
288 // Create new texture name
289 std::ostringstream os;
290 os<<orig_name<<"^[crack"<<mod.param;
293 u32 new_id = g_texturesource->getTextureId(os.str());
295 /*dstream<<"MapBlock::getNodeTile(): Switching from "
296 <<orig_name<<" to "<<os.str()<<" ("
297 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
299 spec.texture = g_texturesource->getTexture(new_id);
306 content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
309 Check temporary modifications on this node
311 /*core::map<v3s16, NodeMod>::Node *n;
312 n = m_temp_mods.find(p);
316 struct NodeMod mod = n->getValue();*/
318 if(temp_mods.get(p, &mod))
320 if(mod.type == NODEMOD_CHANGECONTENT)
325 if(mod.type == NODEMOD_CRACK)
328 Content doesn't change.
330 face_contents works just like it should, because
331 there should not be faces between differently cracked
334 If a semi-transparent node is cracked in front an
335 another one, it really doesn't matter whether there
336 is a cracked face drawn in between or not.
341 return mn.getContent();
355 // Calculate lighting at the XYZ- corner of p
356 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
358 u16 ambient_occlusion = 0;
361 for(u32 i=0; i<8; i++)
363 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
364 if(content_features(n).param_type == CPT_LIGHT
365 // Fast-style leaves look better this way
366 && content_features(n).solidness != 2)
368 light += decode_light(n.getLightBlend(daynight_ratio));
373 if(n.getContent() != CONTENT_IGNORE)
381 light /= light_count;
383 if(ambient_occlusion > 4)
385 ambient_occlusion -= 4;
386 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
392 // Calculate lighting at the given corner of p
393 u8 getSmoothLight(v3s16 p, v3s16 corner,
394 VoxelManipulator &vmanip, u32 daynight_ratio)
396 if(corner.X == 1) p.X += 1;
397 else assert(corner.X == -1);
398 if(corner.Y == 1) p.Y += 1;
399 else assert(corner.Y == -1);
400 if(corner.Z == 1) p.Z += 1;
401 else assert(corner.Z == -1);
403 return getSmoothLight(p, vmanip, daynight_ratio);
408 v3s16 blockpos_nodes,
412 VoxelManipulator &vmanip,
413 NodeModMap &temp_mods,
414 bool smooth_lighting,
418 v3s16 &face_dir_corrected,
423 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
424 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
425 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
426 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
429 content_t content0 = getNodeContent(p, n0, temp_mods);
430 content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
431 bool equivalent = false;
432 u8 mf = face_contents(content0, content1, &equivalent);
446 face_dir_corrected = face_dir;
451 p_corrected = p + face_dir;
452 face_dir_corrected = -face_dir;
455 // eg. water and glass
457 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
459 if(smooth_lighting == false)
461 lights[0] = lights[1] = lights[2] = lights[3] =
462 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
466 v3s16 vertex_dirs[4];
467 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
468 for(u16 i=0; i<4; i++)
470 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
471 vertex_dirs[i], vmanip, daynight_ratio);
480 translate_dir: unit vector with only one of x, y or z
481 face_dir: unit vector with only one of x, y or z
483 void updateFastFaceRow(
492 core::array<FastFace> &dest,
493 NodeModMap &temp_mods,
494 VoxelManipulator &vmanip,
495 v3s16 blockpos_nodes,
496 bool smooth_lighting)
500 u16 continuous_tiles_count = 0;
502 bool makes_face = false;
504 v3s16 face_dir_corrected;
505 u8 lights[4] = {0,0,0,0};
507 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
508 vmanip, temp_mods, smooth_lighting,
509 makes_face, p_corrected, face_dir_corrected, lights, tile);
511 for(u16 j=0; j<length; j++)
513 // If tiling can be done, this is set to false in the next step
514 bool next_is_different = true;
518 bool next_makes_face = false;
519 v3s16 next_p_corrected;
520 v3s16 next_face_dir_corrected;
521 u8 next_lights[4] = {0,0,0,0};
524 // If at last position, there is nothing to compare to and
525 // the face must be drawn anyway
528 p_next = p + translate_dir;
530 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
531 vmanip, temp_mods, smooth_lighting,
532 next_makes_face, next_p_corrected,
533 next_face_dir_corrected, next_lights,
536 if(next_makes_face == makes_face
537 && next_p_corrected == p_corrected + translate_dir
538 && next_face_dir_corrected == face_dir_corrected
539 && next_lights[0] == lights[0]
540 && next_lights[1] == lights[1]
541 && next_lights[2] == lights[2]
542 && next_lights[3] == lights[3]
543 && next_tile == tile)
545 next_is_different = false;
549 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
550 next_makes_face != makes_face ? 1 : 0);
551 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
552 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
553 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
554 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
555 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
556 (next_lights[0] != lights[0] ||
557 next_lights[0] != lights[0] ||
558 next_lights[0] != lights[0] ||
559 next_lights[0] != lights[0]) ? 1 : 0);
560 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
561 !(next_tile == tile) ? 1 : 0);
564 /*g_profiler->add("Meshgen: Total faces checked", 1);
566 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
569 g_profiler->add("Meshgen: diff: last position", 1);*/
572 continuous_tiles_count++;
574 // This is set to true if the texture doesn't allow more tiling
575 bool end_of_texture = false;
577 If there is no texture, it can be tiled infinitely.
578 If tiled==0, it means the texture can be tiled infinitely.
579 Otherwise check tiled agains continuous_tiles_count.
581 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
583 if(tile.texture.tiled <= continuous_tiles_count)
584 end_of_texture = true;
587 // Do this to disable tiling textures
588 //end_of_texture = true; //DEBUG
590 if(next_is_different || end_of_texture)
593 Create a face if there should be one
597 // Floating point conversion of the position vector
598 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
599 // Center point of face (kind of)
600 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
601 if(continuous_tiles_count != 1)
602 sp += translate_dir_f;
605 if(translate_dir.X != 0)
607 scale.X = continuous_tiles_count;
609 if(translate_dir.Y != 0)
611 scale.Y = continuous_tiles_count;
613 if(translate_dir.Z != 0)
615 scale.Z = continuous_tiles_count;
618 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
619 sp, face_dir_corrected, scale,
620 posRelative_f, dest);
622 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
623 for(int i=1; i<continuous_tiles_count; i++){
624 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
628 continuous_tiles_count = 0;
630 makes_face = next_makes_face;
631 p_corrected = next_p_corrected;
632 face_dir_corrected = next_face_dir_corrected;
633 lights[0] = next_lights[0];
634 lights[1] = next_lights[1];
635 lights[2] = next_lights[2];
636 lights[3] = next_lights[3];
644 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
646 // 4-21ms for MAP_BLOCKSIZE=16
647 // 24-155ms for MAP_BLOCKSIZE=32
648 //TimeTaker timer1("makeMapBlockMesh()");
650 core::array<FastFace> fastfaces_new;
652 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
654 // floating point conversion
655 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
660 //bool new_style_water = g_settings->getBool("new_style_water");
661 //bool new_style_leaves = g_settings->getBool("new_style_leaves");
662 bool smooth_lighting = g_settings->getBool("smooth_lighting");
665 We are including the faces of the trailing edges of the block.
666 This means that when something changes, the caller must
667 also update the meshes of the blocks at the leading edges.
669 NOTE: This is the slowest part of this method.
673 // 4-23ms for MAP_BLOCKSIZE=16
674 //TimeTaker timer2("updateMesh() collect");
677 Go through every y,z and get top(y+) faces in rows of x+
679 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
680 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
681 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
682 v3s16(0,y,z), MAP_BLOCKSIZE,
685 v3s16(0,1,0), //face dir
695 Go through every x,y and get right(x+) faces in rows of z+
697 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
698 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
699 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
700 v3s16(x,y,0), MAP_BLOCKSIZE,
713 Go through every y,z and get back(z+) faces in rows of x+
715 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
716 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
717 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
718 v3s16(0,y,z), MAP_BLOCKSIZE,
735 Convert FastFaces to SMesh
738 MeshCollector collector;
740 if(fastfaces_new.size() > 0)
742 // avg 0ms (100ms spikes when loading textures the first time)
743 //TimeTaker timer2("updateMesh() mesh building");
745 video::SMaterial material;
746 material.setFlag(video::EMF_LIGHTING, false);
747 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
748 material.setFlag(video::EMF_BILINEAR_FILTER, false);
749 material.setFlag(video::EMF_FOG_ENABLE, true);
750 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
751 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
752 material.MaterialType
753 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
755 for(u32 i=0; i<fastfaces_new.size(); i++)
757 FastFace &f = fastfaces_new[i];
759 const u16 indices[] = {0,1,2,2,3,0};
760 const u16 indices_alternate[] = {0,1,3,2,3,1};
762 video::ITexture *texture = f.tile.texture.atlas;
766 material.setTexture(0, texture);
768 f.tile.applyMaterialOptions(material);
770 const u16 *indices_p = indices;
773 Revert triangles for nicer looking gradient if vertices
774 1 and 3 have same color or 0 and 2 have different color.
776 if(f.vertices[0].Color != f.vertices[2].Color
777 || f.vertices[1].Color == f.vertices[3].Color)
778 indices_p = indices_alternate;
780 collector.append(material, f.vertices, 4, indices_p, 6);
785 Add special graphics:
792 mapblock_mesh_generate_special(data, collector);
795 Add stuff from collector to mesh
798 scene::SMesh *mesh_new = NULL;
799 mesh_new = new scene::SMesh();
801 collector.fillMesh(mesh_new);
804 Do some stuff to the mesh
807 mesh_new->recalculateBoundingBox();
810 Delete new mesh if it is empty
813 if(mesh_new->getMeshBufferCount() == 0)
822 // Usually 1-700 faces and 1-7 materials
823 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
824 <<"and uses "<<mesh_new->getMeshBufferCount()
825 <<" materials (meshbuffers)"<<std::endl;
828 // Use VBO for mesh (this just would set this for ever buffer)
829 // This will lead to infinite memory usage because or irrlicht.
830 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
833 NOTE: If that is enabled, some kind of a queue to the main
834 thread should be made which would call irrlicht to delete
835 the hardware buffer and then delete the mesh
841 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;