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
30 #include "content_mapblock.h"
32 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
34 m_daynight_ratio = daynight_ratio;
35 m_blockpos = block->getPos();
37 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
40 There is no harm not copying the TempMods of the neighbors
41 because they are already copied to this block
44 block->copyTempMods(m_temp_mods);
50 // Allocate this block + neighbors
52 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
53 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
56 //TimeTaker timer("copy central block data");
60 block->copyTo(m_vmanip);
63 //TimeTaker timer("copy neighbor block data");
67 Copy neighbors. This is lightning fast.
68 Copying only the borders would be *very* slow.
72 Map *map = block->getParent();
74 for(u16 i=0; i<6; i++)
76 const v3s16 &dir = g_6dirs[i];
77 v3s16 bp = m_blockpos + dir;
78 MapBlock *b = map->getBlockNoCreateNoEx(bp);
88 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
91 If looked from outside the node towards the face, the corners are:
97 if(dir == v3s16(0,0,1))
99 // If looking towards z+, this is the face that is behind
100 // the center point, facing towards z+.
101 vertex_dirs[0] = v3s16(-1,-1, 1);
102 vertex_dirs[1] = v3s16( 1,-1, 1);
103 vertex_dirs[2] = v3s16( 1, 1, 1);
104 vertex_dirs[3] = v3s16(-1, 1, 1);
106 else if(dir == v3s16(0,0,-1))
109 vertex_dirs[0] = v3s16( 1,-1,-1);
110 vertex_dirs[1] = v3s16(-1,-1,-1);
111 vertex_dirs[2] = v3s16(-1, 1,-1);
112 vertex_dirs[3] = v3s16( 1, 1,-1);
114 else if(dir == v3s16(1,0,0))
117 vertex_dirs[0] = v3s16( 1,-1, 1);
118 vertex_dirs[1] = v3s16( 1,-1,-1);
119 vertex_dirs[2] = v3s16( 1, 1,-1);
120 vertex_dirs[3] = v3s16( 1, 1, 1);
122 else if(dir == v3s16(-1,0,0))
125 vertex_dirs[0] = v3s16(-1,-1,-1);
126 vertex_dirs[1] = v3s16(-1,-1, 1);
127 vertex_dirs[2] = v3s16(-1, 1, 1);
128 vertex_dirs[3] = v3s16(-1, 1,-1);
130 else if(dir == v3s16(0,1,0))
132 // faces towards Y+ (assume Z- as "down" in texture)
133 vertex_dirs[0] = v3s16( 1, 1,-1);
134 vertex_dirs[1] = v3s16(-1, 1,-1);
135 vertex_dirs[2] = v3s16(-1, 1, 1);
136 vertex_dirs[3] = v3s16( 1, 1, 1);
138 else if(dir == v3s16(0,-1,0))
140 // faces towards Y- (assume Z+ as "down" in texture)
141 vertex_dirs[0] = v3s16( 1,-1, 1);
142 vertex_dirs[1] = v3s16(-1,-1, 1);
143 vertex_dirs[2] = v3s16(-1,-1,-1);
144 vertex_dirs[3] = v3s16( 1,-1,-1);
148 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
151 return video::SColor(alpha,light,light,light);
153 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
154 /*return video::SColor(alpha,light,light,MYMAX(0,
155 pow((float)light/255.0, 0.8)*255.0));*/
157 // Emphase blue a bit in darker places
161 return video::SColor(alpha,light,light,light);
163 return video::SColor(alpha,light,light,MYMAX(0,
164 pow((float)light/lim, power)*lim));
171 video::S3DVertex vertices[4]; // Precalculated vertices
174 static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
175 v3s16 dir, v3f scale, v3f posRelative_f,
176 core::array<FastFace> &dest)
180 // Position is at the center of the cube.
185 v3s16 vertex_dirs[4];
186 getNodeVertexDirs(dir, vertex_dirs);
187 for(u16 i=0; i<4; i++)
190 BS/2*vertex_dirs[i].X,
191 BS/2*vertex_dirs[i].Y,
192 BS/2*vertex_dirs[i].Z
196 for(u16 i=0; i<4; i++)
198 vertex_pos[i].X *= scale.X;
199 vertex_pos[i].Y *= scale.Y;
200 vertex_pos[i].Z *= scale.Z;
201 vertex_pos[i] += pos + posRelative_f;
205 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
206 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
207 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
209 v3f zerovector = v3f(0,0,0);
211 u8 alpha = tile.alpha;
213 if(tile.id == TILE_WATER)
214 alpha = WATER_ALPHA;*/
216 float x0 = tile.texture.pos.X;
217 float y0 = tile.texture.pos.Y;
218 float w = tile.texture.size.X;
219 float h = tile.texture.size.Y;
221 /*video::SColor c = MapBlock_LightColor(alpha, li);
223 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
224 core::vector2d<f32>(x0+w*abs_scale, y0+h));
225 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
226 core::vector2d<f32>(x0, y0+h));
227 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
228 core::vector2d<f32>(x0, y0));
229 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
230 core::vector2d<f32>(x0+w*abs_scale, y0));*/
232 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
233 MapBlock_LightColor(alpha, li0),
234 core::vector2d<f32>(x0+w*abs_scale, y0+h));
235 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
236 MapBlock_LightColor(alpha, li1),
237 core::vector2d<f32>(x0, y0+h));
238 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
239 MapBlock_LightColor(alpha, li2),
240 core::vector2d<f32>(x0, y0));
241 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
242 MapBlock_LightColor(alpha, li3),
243 core::vector2d<f32>(x0+w*abs_scale, y0));
247 //f->tile = TILE_STONE;
249 dest.push_back(face);
253 Gets node tile from any place relative to block.
254 Returns TILE_NODE if doesn't exist or should not be drawn.
256 static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
257 NodeModMap &temp_mods, ITextureSource *tsrc, INodeDefManager *ndef)
260 spec = mn.getTile(face_dir, tsrc, ndef);
263 Check temporary modifications on this node
265 /*core::map<v3s16, NodeMod>::Node *n;
266 n = m_temp_mods.find(p);
270 struct NodeMod mod = n->getValue();*/
272 if(temp_mods.get(p, &mod))
274 if(mod.type == NODEMOD_CHANGECONTENT)
276 MapNode mn2(mod.param);
277 spec = mn2.getTile(face_dir, tsrc, ndef);
279 if(mod.type == NODEMOD_CRACK)
282 Get texture id, translate it to name, append stuff to
286 // Get original texture name
287 u32 orig_id = spec.texture.id;
288 std::string orig_name = tsrc->getTextureName(orig_id);
290 // Create new texture name
291 std::ostringstream os;
292 os<<orig_name<<"^[crack"<<mod.param;
295 u32 new_id = tsrc->getTextureId(os.str());
297 /*dstream<<"MapBlock::getNodeTile(): Switching from "
298 <<orig_name<<" to "<<os.str()<<" ("
299 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
301 spec.texture = tsrc->getTexture(new_id);
308 static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
311 Check temporary modifications on this node
313 /*core::map<v3s16, NodeMod>::Node *n;
314 n = m_temp_mods.find(p);
318 struct NodeMod mod = n->getValue();*/
320 if(temp_mods.get(p, &mod))
322 if(mod.type == NODEMOD_CHANGECONTENT)
327 if(mod.type == NODEMOD_CRACK)
330 Content doesn't change.
332 face_contents works just like it should, because
333 there should not be faces between differently cracked
336 If a semi-transparent node is cracked in front an
337 another one, it really doesn't matter whether there
338 is a cracked face drawn in between or not.
343 return mn.getContent();
357 // Calculate lighting at the XYZ- corner of p
358 static u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio,
359 INodeDefManager *ndef)
361 u16 ambient_occlusion = 0;
364 for(u32 i=0; i<8; i++)
366 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
367 if(ndef->get(n).param_type == CPT_LIGHT
368 // Fast-style leaves look better this way
369 && ndef->get(n).solidness != 2)
371 light += decode_light(n.getLightBlend(daynight_ratio, ndef));
376 if(n.getContent() != CONTENT_IGNORE)
384 light /= light_count;
386 if(ambient_occlusion > 4)
388 ambient_occlusion -= 4;
389 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
395 // Calculate lighting at the given corner of p
396 static u8 getSmoothLight(v3s16 p, v3s16 corner,
397 VoxelManipulator &vmanip, u32 daynight_ratio, INodeDefManager *ndef)
399 if(corner.X == 1) p.X += 1;
400 else assert(corner.X == -1);
401 if(corner.Y == 1) p.Y += 1;
402 else assert(corner.Y == -1);
403 if(corner.Z == 1) p.Z += 1;
404 else assert(corner.Z == -1);
406 return getSmoothLight(p, vmanip, daynight_ratio, ndef);
409 static void getTileInfo(
411 v3s16 blockpos_nodes,
415 VoxelManipulator &vmanip,
416 NodeModMap &temp_mods,
417 bool smooth_lighting,
422 v3s16 &face_dir_corrected,
427 ITextureSource *tsrc = gamedef->tsrc();
428 INodeDefManager *ndef = gamedef->ndef();
430 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
431 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
432 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods, tsrc, ndef);
433 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods, tsrc, ndef);
436 content_t content0 = getNodeContent(p, n0, temp_mods);
437 content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
438 bool equivalent = false;
439 u8 mf = face_contents(content0, content1, &equivalent, ndef);
453 face_dir_corrected = face_dir;
458 p_corrected = p + face_dir;
459 face_dir_corrected = -face_dir;
462 // eg. water and glass
464 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
466 if(smooth_lighting == false)
468 lights[0] = lights[1] = lights[2] = lights[3] =
469 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir, ndef));
473 v3s16 vertex_dirs[4];
474 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
475 for(u16 i=0; i<4; i++)
477 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
478 vertex_dirs[i], vmanip, daynight_ratio, ndef);
487 translate_dir: unit vector with only one of x, y or z
488 face_dir: unit vector with only one of x, y or z
490 static void updateFastFaceRow(
499 core::array<FastFace> &dest,
500 NodeModMap &temp_mods,
501 VoxelManipulator &vmanip,
502 v3s16 blockpos_nodes,
503 bool smooth_lighting,
508 u16 continuous_tiles_count = 0;
510 bool makes_face = false;
512 v3s16 face_dir_corrected;
513 u8 lights[4] = {0,0,0,0};
515 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
516 vmanip, temp_mods, smooth_lighting, gamedef,
517 makes_face, p_corrected, face_dir_corrected, lights, tile);
519 for(u16 j=0; j<length; j++)
521 // If tiling can be done, this is set to false in the next step
522 bool next_is_different = true;
526 bool next_makes_face = false;
527 v3s16 next_p_corrected;
528 v3s16 next_face_dir_corrected;
529 u8 next_lights[4] = {0,0,0,0};
532 // If at last position, there is nothing to compare to and
533 // the face must be drawn anyway
536 p_next = p + translate_dir;
538 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
539 vmanip, temp_mods, smooth_lighting, gamedef,
540 next_makes_face, next_p_corrected,
541 next_face_dir_corrected, next_lights,
544 if(next_makes_face == makes_face
545 && next_p_corrected == p_corrected + translate_dir
546 && next_face_dir_corrected == face_dir_corrected
547 && next_lights[0] == lights[0]
548 && next_lights[1] == lights[1]
549 && next_lights[2] == lights[2]
550 && next_lights[3] == lights[3]
551 && next_tile == tile)
553 next_is_different = false;
557 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
558 next_makes_face != makes_face ? 1 : 0);
559 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
560 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
561 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
562 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
563 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
564 (next_lights[0] != lights[0] ||
565 next_lights[0] != lights[0] ||
566 next_lights[0] != lights[0] ||
567 next_lights[0] != lights[0]) ? 1 : 0);
568 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
569 !(next_tile == tile) ? 1 : 0);
572 /*g_profiler->add("Meshgen: Total faces checked", 1);
574 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
577 g_profiler->add("Meshgen: diff: last position", 1);*/
580 continuous_tiles_count++;
582 // This is set to true if the texture doesn't allow more tiling
583 bool end_of_texture = false;
585 If there is no texture, it can be tiled infinitely.
586 If tiled==0, it means the texture can be tiled infinitely.
587 Otherwise check tiled agains continuous_tiles_count.
589 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
591 if(tile.texture.tiled <= continuous_tiles_count)
592 end_of_texture = true;
595 // Do this to disable tiling textures
596 //end_of_texture = true; //DEBUG
598 if(next_is_different || end_of_texture)
601 Create a face if there should be one
605 // Floating point conversion of the position vector
606 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
607 // Center point of face (kind of)
608 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
609 if(continuous_tiles_count != 1)
610 sp += translate_dir_f;
613 if(translate_dir.X != 0)
615 scale.X = continuous_tiles_count;
617 if(translate_dir.Y != 0)
619 scale.Y = continuous_tiles_count;
621 if(translate_dir.Z != 0)
623 scale.Z = continuous_tiles_count;
626 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
627 sp, face_dir_corrected, scale,
628 posRelative_f, dest);
630 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
631 for(int i=1; i<continuous_tiles_count; i++){
632 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
636 continuous_tiles_count = 0;
638 makes_face = next_makes_face;
639 p_corrected = next_p_corrected;
640 face_dir_corrected = next_face_dir_corrected;
641 lights[0] = next_lights[0];
642 lights[1] = next_lights[1];
643 lights[2] = next_lights[2];
644 lights[3] = next_lights[3];
652 scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
654 // 4-21ms for MAP_BLOCKSIZE=16
655 // 24-155ms for MAP_BLOCKSIZE=32
656 //TimeTaker timer1("makeMapBlockMesh()");
658 core::array<FastFace> fastfaces_new;
660 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
662 // floating point conversion
663 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
668 //bool new_style_water = g_settings->getBool("new_style_water");
669 //bool new_style_leaves = g_settings->getBool("new_style_leaves");
670 bool smooth_lighting = g_settings->getBool("smooth_lighting");
673 We are including the faces of the trailing edges of the block.
674 This means that when something changes, the caller must
675 also update the meshes of the blocks at the leading edges.
677 NOTE: This is the slowest part of this method.
681 // 4-23ms for MAP_BLOCKSIZE=16
682 //TimeTaker timer2("updateMesh() collect");
685 Go through every y,z and get top(y+) faces in rows of x+
687 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
688 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
689 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
690 v3s16(0,y,z), MAP_BLOCKSIZE,
693 v3s16(0,1,0), //face dir
704 Go through every x,y and get right(x+) faces in rows of z+
706 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
707 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
708 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
709 v3s16(x,y,0), MAP_BLOCKSIZE,
723 Go through every y,z and get back(z+) faces in rows of x+
725 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
726 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
727 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
728 v3s16(0,y,z), MAP_BLOCKSIZE,
746 Convert FastFaces to SMesh
749 MeshCollector collector;
751 if(fastfaces_new.size() > 0)
753 // avg 0ms (100ms spikes when loading textures the first time)
754 //TimeTaker timer2("updateMesh() mesh building");
756 video::SMaterial material;
757 material.setFlag(video::EMF_LIGHTING, false);
758 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
759 material.setFlag(video::EMF_BILINEAR_FILTER, false);
760 material.setFlag(video::EMF_FOG_ENABLE, true);
761 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
762 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
763 material.MaterialType
764 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
766 for(u32 i=0; i<fastfaces_new.size(); i++)
768 FastFace &f = fastfaces_new[i];
770 const u16 indices[] = {0,1,2,2,3,0};
771 const u16 indices_alternate[] = {0,1,3,2,3,1};
773 video::ITexture *texture = f.tile.texture.atlas;
777 material.setTexture(0, texture);
779 f.tile.applyMaterialOptions(material);
781 const u16 *indices_p = indices;
784 Revert triangles for nicer looking gradient if vertices
785 1 and 3 have same color or 0 and 2 have different color.
787 if(f.vertices[0].Color != f.vertices[2].Color
788 || f.vertices[1].Color == f.vertices[3].Color)
789 indices_p = indices_alternate;
791 collector.append(material, f.vertices, 4, indices_p, 6);
796 Add special graphics:
803 mapblock_mesh_generate_special(data, collector, gamedef);
806 Add stuff from collector to mesh
809 scene::SMesh *mesh_new = NULL;
810 mesh_new = new scene::SMesh();
812 collector.fillMesh(mesh_new);
815 Do some stuff to the mesh
818 mesh_new->recalculateBoundingBox();
821 Delete new mesh if it is empty
824 if(mesh_new->getMeshBufferCount() == 0)
833 // Usually 1-700 faces and 1-7 materials
834 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
835 <<"and uses "<<mesh_new->getMeshBufferCount()
836 <<" materials (meshbuffers)"<<std::endl;
839 // Use VBO for mesh (this just would set this for ever buffer)
840 // This will lead to infinite memory usage because or irrlicht.
841 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
844 NOTE: If that is enabled, some kind of a queue to the main
845 thread should be made which would call irrlicht to delete
846 the hardware buffer and then delete the mesh
852 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;