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"
29 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
31 m_daynight_ratio = daynight_ratio;
32 m_blockpos = block->getPos();
34 v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
37 There is no harm not copying the TempMods of the neighbors
38 because they are already copied to this block
41 block->copyTempMods(m_temp_mods);
47 // Allocate this block + neighbors
49 m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
50 blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
53 //TimeTaker timer("copy central block data");
57 block->copyTo(m_vmanip);
60 //TimeTaker timer("copy neighbor block data");
64 Copy neighbors. This is lightning fast.
65 Copying only the borders would be *very* slow.
69 Map *map = block->getParent();
71 for(u16 i=0; i<6; i++)
73 const v3s16 &dir = g_6dirs[i];
74 v3s16 bp = m_blockpos + dir;
75 MapBlock *b = map->getBlockNoCreateNoEx(bp);
85 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
88 If looked from outside the node towards the face, the corners are:
94 if(dir == v3s16(0,0,1))
96 // If looking towards z+, this is the face that is behind
97 // the center point, facing towards z+.
98 vertex_dirs[0] = v3s16(-1,-1, 1);
99 vertex_dirs[1] = v3s16( 1,-1, 1);
100 vertex_dirs[2] = v3s16( 1, 1, 1);
101 vertex_dirs[3] = v3s16(-1, 1, 1);
103 else if(dir == v3s16(0,0,-1))
106 vertex_dirs[0] = v3s16( 1,-1,-1);
107 vertex_dirs[1] = v3s16(-1,-1,-1);
108 vertex_dirs[2] = v3s16(-1, 1,-1);
109 vertex_dirs[3] = v3s16( 1, 1,-1);
111 else if(dir == v3s16(1,0,0))
114 vertex_dirs[0] = v3s16( 1,-1, 1);
115 vertex_dirs[1] = v3s16( 1,-1,-1);
116 vertex_dirs[2] = v3s16( 1, 1,-1);
117 vertex_dirs[3] = v3s16( 1, 1, 1);
119 else if(dir == v3s16(-1,0,0))
122 vertex_dirs[0] = v3s16(-1,-1,-1);
123 vertex_dirs[1] = v3s16(-1,-1, 1);
124 vertex_dirs[2] = v3s16(-1, 1, 1);
125 vertex_dirs[3] = v3s16(-1, 1,-1);
127 else if(dir == v3s16(0,1,0))
129 // faces towards Y+ (assume Z- as "down" in texture)
130 vertex_dirs[0] = v3s16( 1, 1,-1);
131 vertex_dirs[1] = v3s16(-1, 1,-1);
132 vertex_dirs[2] = v3s16(-1, 1, 1);
133 vertex_dirs[3] = v3s16( 1, 1, 1);
135 else if(dir == v3s16(0,-1,0))
137 // faces towards Y- (assume Z+ as "down" in texture)
138 vertex_dirs[0] = v3s16( 1,-1, 1);
139 vertex_dirs[1] = v3s16(-1,-1, 1);
140 vertex_dirs[2] = v3s16(-1,-1,-1);
141 vertex_dirs[3] = v3s16( 1,-1,-1);
145 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
148 return video::SColor(alpha,light,light,light);
150 //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
151 /*return video::SColor(alpha,light,light,MYMAX(0,
152 pow((float)light/255.0, 0.8)*255.0));*/
154 // Emphase blue a bit in darker places
158 return video::SColor(alpha,light,light,light);
160 return video::SColor(alpha,light,light,MYMAX(0,
161 pow((float)light/lim, power)*lim));
168 video::S3DVertex vertices[4]; // Precalculated vertices
171 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
172 v3s16 dir, v3f scale, v3f posRelative_f,
173 core::array<FastFace> &dest)
177 // Position is at the center of the cube.
182 v3s16 vertex_dirs[4];
183 getNodeVertexDirs(dir, vertex_dirs);
184 for(u16 i=0; i<4; i++)
187 BS/2*vertex_dirs[i].X,
188 BS/2*vertex_dirs[i].Y,
189 BS/2*vertex_dirs[i].Z
193 for(u16 i=0; i<4; i++)
195 vertex_pos[i].X *= scale.X;
196 vertex_pos[i].Y *= scale.Y;
197 vertex_pos[i].Z *= scale.Z;
198 vertex_pos[i] += pos + posRelative_f;
202 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
203 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
204 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
206 v3f zerovector = v3f(0,0,0);
208 u8 alpha = tile.alpha;
210 if(tile.id == TILE_WATER)
211 alpha = WATER_ALPHA;*/
213 float x0 = tile.texture.pos.X;
214 float y0 = tile.texture.pos.Y;
215 float w = tile.texture.size.X;
216 float h = tile.texture.size.Y;
218 /*video::SColor c = MapBlock_LightColor(alpha, li);
220 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
221 core::vector2d<f32>(x0+w*abs_scale, y0+h));
222 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
223 core::vector2d<f32>(x0, y0+h));
224 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
225 core::vector2d<f32>(x0, y0));
226 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
227 core::vector2d<f32>(x0+w*abs_scale, y0));*/
229 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
230 MapBlock_LightColor(alpha, li0),
231 core::vector2d<f32>(x0+w*abs_scale, y0+h));
232 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
233 MapBlock_LightColor(alpha, li1),
234 core::vector2d<f32>(x0, y0+h));
235 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
236 MapBlock_LightColor(alpha, li2),
237 core::vector2d<f32>(x0, y0));
238 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
239 MapBlock_LightColor(alpha, li3),
240 core::vector2d<f32>(x0+w*abs_scale, y0));
244 //f->tile = TILE_STONE;
246 dest.push_back(face);
250 Gets node tile from any place relative to block.
251 Returns TILE_NODE if doesn't exist or should not be drawn.
253 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
254 NodeModMap &temp_mods)
257 spec = mn.getTile(face_dir);
260 Check temporary modifications on this node
262 /*core::map<v3s16, NodeMod>::Node *n;
263 n = m_temp_mods.find(p);
267 struct NodeMod mod = n->getValue();*/
269 if(temp_mods.get(p, &mod))
271 if(mod.type == NODEMOD_CHANGECONTENT)
273 MapNode mn2(mod.param);
274 spec = mn2.getTile(face_dir);
276 if(mod.type == NODEMOD_CRACK)
279 Get texture id, translate it to name, append stuff to
283 // Get original texture name
284 u32 orig_id = spec.texture.id;
285 std::string orig_name = g_texturesource->getTextureName(orig_id);
287 // Create new texture name
288 std::ostringstream os;
289 os<<orig_name<<"^[crack"<<mod.param;
292 u32 new_id = g_texturesource->getTextureId(os.str());
294 /*dstream<<"MapBlock::getNodeTile(): Switching from "
295 <<orig_name<<" to "<<os.str()<<" ("
296 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
298 spec.texture = g_texturesource->getTexture(new_id);
305 content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
308 Check temporary modifications on this node
310 /*core::map<v3s16, NodeMod>::Node *n;
311 n = m_temp_mods.find(p);
315 struct NodeMod mod = n->getValue();*/
317 if(temp_mods.get(p, &mod))
319 if(mod.type == NODEMOD_CHANGECONTENT)
324 if(mod.type == NODEMOD_CRACK)
327 Content doesn't change.
329 face_contents works just like it should, because
330 there should not be faces between differently cracked
333 If a semi-transparent node is cracked in front an
334 another one, it really doesn't matter whether there
335 is a cracked face drawn in between or not.
340 return mn.getContent();
354 // Calculate lighting at the XYZ- corner of p
355 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
357 u16 ambient_occlusion = 0;
360 for(u32 i=0; i<8; i++)
362 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
363 if(content_features(n).param_type == CPT_LIGHT
364 // Fast-style leaves look better this way
365 && content_features(n).solidness != 2)
367 light += decode_light(n.getLightBlend(daynight_ratio));
372 if(n.getContent() != CONTENT_IGNORE)
380 light /= light_count;
382 if(ambient_occlusion > 4)
384 ambient_occlusion -= 4;
385 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
391 // Calculate lighting at the given corner of p
392 u8 getSmoothLight(v3s16 p, v3s16 corner,
393 VoxelManipulator &vmanip, u32 daynight_ratio)
395 if(corner.X == 1) p.X += 1;
396 else assert(corner.X == -1);
397 if(corner.Y == 1) p.Y += 1;
398 else assert(corner.Y == -1);
399 if(corner.Z == 1) p.Z += 1;
400 else assert(corner.Z == -1);
402 return getSmoothLight(p, vmanip, daynight_ratio);
407 v3s16 blockpos_nodes,
411 VoxelManipulator &vmanip,
412 NodeModMap &temp_mods,
413 bool smooth_lighting,
417 v3s16 &face_dir_corrected,
422 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
423 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
424 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
425 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
428 content_t content0 = getNodeContent(p, n0, temp_mods);
429 content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
430 bool equivalent = false;
431 u8 mf = face_contents(content0, content1, &equivalent);
445 face_dir_corrected = face_dir;
450 p_corrected = p + face_dir;
451 face_dir_corrected = -face_dir;
454 // eg. water and glass
456 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
458 if(smooth_lighting == false)
460 lights[0] = lights[1] = lights[2] = lights[3] =
461 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
465 v3s16 vertex_dirs[4];
466 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
467 for(u16 i=0; i<4; i++)
469 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
470 vertex_dirs[i], vmanip, daynight_ratio);
479 translate_dir: unit vector with only one of x, y or z
480 face_dir: unit vector with only one of x, y or z
482 void updateFastFaceRow(
491 core::array<FastFace> &dest,
492 NodeModMap &temp_mods,
493 VoxelManipulator &vmanip,
494 v3s16 blockpos_nodes,
495 bool smooth_lighting)
499 u16 continuous_tiles_count = 0;
501 bool makes_face = false;
503 v3s16 face_dir_corrected;
504 u8 lights[4] = {0,0,0,0};
506 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
507 vmanip, temp_mods, smooth_lighting,
508 makes_face, p_corrected, face_dir_corrected, lights, tile);
510 for(u16 j=0; j<length; j++)
512 // If tiling can be done, this is set to false in the next step
513 bool next_is_different = true;
517 bool next_makes_face = false;
518 v3s16 next_p_corrected;
519 v3s16 next_face_dir_corrected;
520 u8 next_lights[4] = {0,0,0,0};
523 // If at last position, there is nothing to compare to and
524 // the face must be drawn anyway
527 p_next = p + translate_dir;
529 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
530 vmanip, temp_mods, smooth_lighting,
531 next_makes_face, next_p_corrected,
532 next_face_dir_corrected, next_lights,
535 if(next_makes_face == makes_face
536 && next_p_corrected == p_corrected + translate_dir
537 && next_face_dir_corrected == face_dir_corrected
538 && next_lights[0] == lights[0]
539 && next_lights[1] == lights[1]
540 && next_lights[2] == lights[2]
541 && next_lights[3] == lights[3]
542 && next_tile == tile)
544 next_is_different = false;
548 g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
549 next_makes_face != makes_face ? 1 : 0);
550 g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
551 (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
552 g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
553 next_face_dir_corrected != face_dir_corrected ? 1 : 0);
554 g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
555 (next_lights[0] != lights[0] ||
556 next_lights[0] != lights[0] ||
557 next_lights[0] != lights[0] ||
558 next_lights[0] != lights[0]) ? 1 : 0);
559 g_profiler->add("Meshgen: diff: !(next_tile == tile)",
560 !(next_tile == tile) ? 1 : 0);
563 /*g_profiler->add("Meshgen: Total faces checked", 1);
565 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
568 g_profiler->add("Meshgen: diff: last position", 1);*/
571 continuous_tiles_count++;
573 // This is set to true if the texture doesn't allow more tiling
574 bool end_of_texture = false;
576 If there is no texture, it can be tiled infinitely.
577 If tiled==0, it means the texture can be tiled infinitely.
578 Otherwise check tiled agains continuous_tiles_count.
580 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
582 if(tile.texture.tiled <= continuous_tiles_count)
583 end_of_texture = true;
586 // Do this to disable tiling textures
587 //end_of_texture = true; //DEBUG
589 if(next_is_different || end_of_texture)
592 Create a face if there should be one
596 // Floating point conversion of the position vector
597 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
598 // Center point of face (kind of)
599 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
600 if(continuous_tiles_count != 1)
601 sp += translate_dir_f;
604 if(translate_dir.X != 0)
606 scale.X = continuous_tiles_count;
608 if(translate_dir.Y != 0)
610 scale.Y = continuous_tiles_count;
612 if(translate_dir.Z != 0)
614 scale.Z = continuous_tiles_count;
617 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
618 sp, face_dir_corrected, scale,
619 posRelative_f, dest);
621 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
622 for(int i=1; i<continuous_tiles_count; i++){
623 g_profiler->avg("Meshgen: faces drawn by tiling", 1);
627 continuous_tiles_count = 0;
629 makes_face = next_makes_face;
630 p_corrected = next_p_corrected;
631 face_dir_corrected = next_face_dir_corrected;
632 lights[0] = next_lights[0];
633 lights[1] = next_lights[1];
634 lights[2] = next_lights[2];
635 lights[3] = next_lights[3];
643 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
645 // 4-21ms for MAP_BLOCKSIZE=16
646 // 24-155ms for MAP_BLOCKSIZE=32
647 //TimeTaker timer1("makeMapBlockMesh()");
649 core::array<FastFace> fastfaces_new;
651 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
653 // floating point conversion
654 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
659 //bool new_style_water = g_settings->getBool("new_style_water");
660 //bool new_style_leaves = g_settings->getBool("new_style_leaves");
661 bool smooth_lighting = g_settings->getBool("smooth_lighting");
664 We are including the faces of the trailing edges of the block.
665 This means that when something changes, the caller must
666 also update the meshes of the blocks at the leading edges.
668 NOTE: This is the slowest part of this method.
672 // 4-23ms for MAP_BLOCKSIZE=16
673 //TimeTaker timer2("updateMesh() collect");
676 Go through every y,z and get top(y+) faces in rows of x+
678 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
679 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
680 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
681 v3s16(0,y,z), MAP_BLOCKSIZE,
684 v3s16(0,1,0), //face dir
694 Go through every x,y and get right(x+) faces in rows of z+
696 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
697 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
698 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
699 v3s16(x,y,0), MAP_BLOCKSIZE,
712 Go through every y,z and get back(z+) faces in rows of x+
714 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
715 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
716 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
717 v3s16(0,y,z), MAP_BLOCKSIZE,
734 Convert FastFaces to SMesh
737 MeshCollector collector;
739 if(fastfaces_new.size() > 0)
741 // avg 0ms (100ms spikes when loading textures the first time)
742 //TimeTaker timer2("updateMesh() mesh building");
744 video::SMaterial material;
745 material.setFlag(video::EMF_LIGHTING, false);
746 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
747 material.setFlag(video::EMF_BILINEAR_FILTER, false);
748 material.setFlag(video::EMF_FOG_ENABLE, true);
749 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
750 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
751 material.MaterialType
752 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
754 for(u32 i=0; i<fastfaces_new.size(); i++)
756 FastFace &f = fastfaces_new[i];
758 const u16 indices[] = {0,1,2,2,3,0};
759 const u16 indices_alternate[] = {0,1,3,2,3,1};
761 video::ITexture *texture = f.tile.texture.atlas;
765 material.setTexture(0, texture);
767 f.tile.applyMaterialOptions(material);
769 const u16 *indices_p = indices;
772 Revert triangles for nicer looking gradient if vertices
773 1 and 3 have same color or 0 and 2 have different color.
775 if(f.vertices[0].Color != f.vertices[2].Color
776 || f.vertices[1].Color == f.vertices[3].Color)
777 indices_p = indices_alternate;
779 collector.append(material, f.vertices, 4, indices_p, 6);
784 Add special graphics:
791 mapblock_mesh_generate_special(data, collector);
794 Add stuff from collector to mesh
797 scene::SMesh *mesh_new = NULL;
798 mesh_new = new scene::SMesh();
800 collector.fillMesh(mesh_new);
803 Do some stuff to the mesh
806 mesh_new->recalculateBoundingBox();
809 Delete new mesh if it is empty
812 if(mesh_new->getMeshBufferCount() == 0)
821 // Usually 1-700 faces and 1-7 materials
822 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
823 <<"and uses "<<mesh_new->getMeshBufferCount()
824 <<" materials (meshbuffers)"<<std::endl;
827 // Use VBO for mesh (this just would set this for ever buffer)
828 // This will lead to infinite memory usage because or irrlicht.
829 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
832 NOTE: If that is enabled, some kind of a queue to the main
833 thread should be made which would call irrlicht to delete
834 the hardware buffer and then delete the mesh
840 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;