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 Map *map = block->getParent();
69 for(u16 i=0; i<6; i++)
71 const v3s16 &dir = g_6dirs[i];
72 v3s16 bp = m_blockpos + dir;
73 MapBlock *b = map->getBlockNoCreateNoEx(bp);
83 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
86 If looked from outside the node towards the face, the corners are:
92 if(dir == v3s16(0,0,1))
94 // If looking towards z+, this is the face that is behind
95 // the center point, facing towards z+.
96 vertex_dirs[0] = v3s16(-1,-1, 1);
97 vertex_dirs[1] = v3s16( 1,-1, 1);
98 vertex_dirs[2] = v3s16( 1, 1, 1);
99 vertex_dirs[3] = v3s16(-1, 1, 1);
101 else if(dir == v3s16(0,0,-1))
104 vertex_dirs[0] = v3s16( 1,-1,-1);
105 vertex_dirs[1] = v3s16(-1,-1,-1);
106 vertex_dirs[2] = v3s16(-1, 1,-1);
107 vertex_dirs[3] = v3s16( 1, 1,-1);
109 else if(dir == v3s16(1,0,0))
112 vertex_dirs[0] = v3s16( 1,-1, 1);
113 vertex_dirs[1] = v3s16( 1,-1,-1);
114 vertex_dirs[2] = v3s16( 1, 1,-1);
115 vertex_dirs[3] = v3s16( 1, 1, 1);
117 else if(dir == v3s16(-1,0,0))
120 vertex_dirs[0] = v3s16(-1,-1,-1);
121 vertex_dirs[1] = v3s16(-1,-1, 1);
122 vertex_dirs[2] = v3s16(-1, 1, 1);
123 vertex_dirs[3] = v3s16(-1, 1,-1);
125 else if(dir == v3s16(0,1,0))
127 // faces towards Y+ (assume Z- as "down" in texture)
128 vertex_dirs[0] = v3s16( 1, 1,-1);
129 vertex_dirs[1] = v3s16(-1, 1,-1);
130 vertex_dirs[2] = v3s16(-1, 1, 1);
131 vertex_dirs[3] = v3s16( 1, 1, 1);
133 else if(dir == v3s16(0,-1,0))
135 // faces towards Y- (assume Z+ as "down" in texture)
136 vertex_dirs[0] = v3s16( 1,-1, 1);
137 vertex_dirs[1] = v3s16(-1,-1, 1);
138 vertex_dirs[2] = v3s16(-1,-1,-1);
139 vertex_dirs[3] = v3s16( 1,-1,-1);
143 inline video::SColor lightColor(u8 alpha, u8 light)
145 return video::SColor(alpha,light,light,light);
151 video::S3DVertex vertices[4]; // Precalculated vertices
154 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
155 v3s16 dir, v3f scale, v3f posRelative_f,
156 core::array<FastFace> &dest)
160 // Position is at the center of the cube.
165 v3s16 vertex_dirs[4];
166 getNodeVertexDirs(dir, vertex_dirs);
167 for(u16 i=0; i<4; i++)
170 BS/2*vertex_dirs[i].X,
171 BS/2*vertex_dirs[i].Y,
172 BS/2*vertex_dirs[i].Z
176 for(u16 i=0; i<4; i++)
178 vertex_pos[i].X *= scale.X;
179 vertex_pos[i].Y *= scale.Y;
180 vertex_pos[i].Z *= scale.Z;
181 vertex_pos[i] += pos + posRelative_f;
185 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
186 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
187 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
189 v3f zerovector = v3f(0,0,0);
191 u8 alpha = tile.alpha;
193 if(tile.id == TILE_WATER)
194 alpha = WATER_ALPHA;*/
196 float x0 = tile.texture.pos.X;
197 float y0 = tile.texture.pos.Y;
198 float w = tile.texture.size.X;
199 float h = tile.texture.size.Y;
201 /*video::SColor c = lightColor(alpha, li);
203 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
204 core::vector2d<f32>(x0+w*abs_scale, y0+h));
205 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
206 core::vector2d<f32>(x0, y0+h));
207 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
208 core::vector2d<f32>(x0, y0));
209 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
210 core::vector2d<f32>(x0+w*abs_scale, y0));*/
212 face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
213 lightColor(alpha, li0),
214 core::vector2d<f32>(x0+w*abs_scale, y0+h));
215 face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
216 lightColor(alpha, li1),
217 core::vector2d<f32>(x0, y0+h));
218 face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
219 lightColor(alpha, li2),
220 core::vector2d<f32>(x0, y0));
221 face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
222 lightColor(alpha, li3),
223 core::vector2d<f32>(x0+w*abs_scale, y0));
227 //f->tile = TILE_STONE;
229 dest.push_back(face);
233 Gets node tile from any place relative to block.
234 Returns TILE_NODE if doesn't exist or should not be drawn.
236 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
237 NodeModMap &temp_mods)
240 spec = mn.getTile(face_dir);
243 Check temporary modifications on this node
245 /*core::map<v3s16, NodeMod>::Node *n;
246 n = m_temp_mods.find(p);
250 struct NodeMod mod = n->getValue();*/
252 if(temp_mods.get(p, &mod))
254 if(mod.type == NODEMOD_CHANGECONTENT)
256 MapNode mn2(mod.param);
257 spec = mn2.getTile(face_dir);
259 if(mod.type == NODEMOD_CRACK)
262 Get texture id, translate it to name, append stuff to
266 // Get original texture name
267 u32 orig_id = spec.texture.id;
268 std::string orig_name = g_texturesource->getTextureName(orig_id);
270 // Create new texture name
271 std::ostringstream os;
272 os<<orig_name<<"^[crack"<<mod.param;
275 u32 new_id = g_texturesource->getTextureId(os.str());
277 /*dstream<<"MapBlock::getNodeTile(): Switching from "
278 <<orig_name<<" to "<<os.str()<<" ("
279 <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
281 spec.texture = g_texturesource->getTexture(new_id);
288 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
291 Check temporary modifications on this node
293 /*core::map<v3s16, NodeMod>::Node *n;
294 n = m_temp_mods.find(p);
298 struct NodeMod mod = n->getValue();*/
300 if(temp_mods.get(p, &mod))
302 if(mod.type == NODEMOD_CHANGECONTENT)
307 if(mod.type == NODEMOD_CRACK)
310 Content doesn't change.
312 face_contents works just like it should, because
313 there should not be faces between differently cracked
316 If a semi-transparent node is cracked in front an
317 another one, it really doesn't matter whether there
318 is a cracked face drawn in between or not.
337 // Calculate lighting at the XYZ- corner of p
338 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
340 u16 ambient_occlusion = 0;
343 for(u32 i=0; i<8; i++)
345 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
346 if(content_features(n.d).param_type == CPT_LIGHT
347 // Fast-style leaves look better this way
348 && content_features(n.d).solidness != 2)
350 light += decode_light(n.getLightBlend(daynight_ratio));
355 if(n.d != CONTENT_IGNORE)
363 light /= light_count;
365 if(ambient_occlusion > 4)
367 ambient_occlusion -= 4;
368 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
374 // Calculate lighting at the given corner of p
375 u8 getSmoothLight(v3s16 p, v3s16 corner,
376 VoxelManipulator &vmanip, u32 daynight_ratio)
378 if(corner.X == 1) p.X += 1;
379 else assert(corner.X == -1);
380 if(corner.Y == 1) p.Y += 1;
381 else assert(corner.Y == -1);
382 if(corner.Z == 1) p.Z += 1;
383 else assert(corner.Z == -1);
385 return getSmoothLight(p, vmanip, daynight_ratio);
390 v3s16 blockpos_nodes,
394 VoxelManipulator &vmanip,
395 NodeModMap &temp_mods,
396 bool smooth_lighting,
400 v3s16 &face_dir_corrected,
405 MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
406 MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
407 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
408 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
411 u8 content0 = getNodeContent(p, n0, temp_mods);
412 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
413 u8 mf = face_contents(content0, content1);
427 face_dir_corrected = face_dir;
432 p_corrected = p + face_dir;
433 face_dir_corrected = -face_dir;
436 if(smooth_lighting == false)
438 lights[0] = lights[1] = lights[2] = lights[3] =
439 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
443 v3s16 vertex_dirs[4];
444 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
445 for(u16 i=0; i<4; i++)
447 lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
448 vertex_dirs[i], vmanip, daynight_ratio);
457 translate_dir: unit vector with only one of x, y or z
458 face_dir: unit vector with only one of x, y or z
460 void updateFastFaceRow(
469 core::array<FastFace> &dest,
470 NodeModMap &temp_mods,
471 VoxelManipulator &vmanip,
472 v3s16 blockpos_nodes,
473 bool smooth_lighting)
477 u16 continuous_tiles_count = 0;
481 v3s16 face_dir_corrected;
484 getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
485 vmanip, temp_mods, smooth_lighting,
486 makes_face, p_corrected, face_dir_corrected, lights, tile);
488 for(u16 j=0; j<length; j++)
490 // If tiling can be done, this is set to false in the next step
491 bool next_is_different = true;
495 bool next_makes_face = false;
496 v3s16 next_p_corrected;
497 v3s16 next_face_dir_corrected;
498 u8 next_lights[4] = {0,0,0,0};
501 // If at last position, there is nothing to compare to and
502 // the face must be drawn anyway
505 p_next = p + translate_dir;
507 getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
508 vmanip, temp_mods, smooth_lighting,
509 next_makes_face, next_p_corrected,
510 next_face_dir_corrected, next_lights,
513 if(next_makes_face == makes_face
514 && next_p_corrected == p_corrected
515 && next_face_dir_corrected == face_dir_corrected
516 && next_lights[0] == lights[0]
517 && next_lights[1] == lights[1]
518 && next_lights[2] == lights[2]
519 && next_lights[3] == lights[3]
520 && next_tile == tile)
522 next_is_different = false;
526 continuous_tiles_count++;
528 // This is set to true if the texture doesn't allow more tiling
529 bool end_of_texture = false;
531 If there is no texture, it can be tiled infinitely.
532 If tiled==0, it means the texture can be tiled infinitely.
533 Otherwise check tiled agains continuous_tiles_count.
535 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
537 if(tile.texture.tiled <= continuous_tiles_count)
538 end_of_texture = true;
541 // Do this to disable tiling textures
542 //end_of_texture = true; //DEBUG
544 if(next_is_different || end_of_texture)
547 Create a face if there should be one
551 // Floating point conversion of the position vector
552 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
553 // Center point of face (kind of)
554 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
557 if(translate_dir.X != 0)
559 scale.X = continuous_tiles_count;
561 if(translate_dir.Y != 0)
563 scale.Y = continuous_tiles_count;
565 if(translate_dir.Z != 0)
567 scale.Z = continuous_tiles_count;
570 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
571 sp, face_dir_corrected, scale,
572 posRelative_f, dest);
575 continuous_tiles_count = 0;
577 makes_face = next_makes_face;
578 p_corrected = next_p_corrected;
579 face_dir_corrected = next_face_dir_corrected;
580 lights[0] = next_lights[0];
581 lights[1] = next_lights[1];
582 lights[2] = next_lights[2];
583 lights[3] = next_lights[3];
591 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
593 // 4-21ms for MAP_BLOCKSIZE=16
594 // 24-155ms for MAP_BLOCKSIZE=32
595 //TimeTaker timer1("makeMapBlockMesh()");
597 core::array<FastFace> fastfaces_new;
599 v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
601 // floating point conversion
602 v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
607 //bool new_style_water = g_settings.getBool("new_style_water");
608 //bool new_style_leaves = g_settings.getBool("new_style_leaves");
609 bool smooth_lighting = g_settings.getBool("smooth_lighting");
612 We are including the faces of the trailing edges of the block.
613 This means that when something changes, the caller must
614 also update the meshes of the blocks at the leading edges.
616 NOTE: This is the slowest part of this method.
620 // 4-23ms for MAP_BLOCKSIZE=16
621 //TimeTaker timer2("updateMesh() collect");
624 Go through every y,z and get top(y+) faces in rows of x+
626 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
627 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
628 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
629 v3s16(0,y,z), MAP_BLOCKSIZE,
632 v3s16(0,1,0), //face dir
642 Go through every x,y and get right(x+) faces in rows of z+
644 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
645 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
646 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
647 v3s16(x,y,0), MAP_BLOCKSIZE,
660 Go through every y,z and get back(z+) faces in rows of x+
662 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
663 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
664 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
665 v3s16(0,y,z), MAP_BLOCKSIZE,
682 Convert FastFaces to SMesh
685 MeshCollector collector;
687 if(fastfaces_new.size() > 0)
689 // avg 0ms (100ms spikes when loading textures the first time)
690 //TimeTaker timer2("updateMesh() mesh building");
692 video::SMaterial material;
693 material.setFlag(video::EMF_LIGHTING, false);
694 material.setFlag(video::EMF_BILINEAR_FILTER, false);
695 material.setFlag(video::EMF_FOG_ENABLE, true);
696 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
697 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
699 for(u32 i=0; i<fastfaces_new.size(); i++)
701 FastFace &f = fastfaces_new[i];
703 const u16 indices[] = {0,1,2,2,3,0};
704 const u16 indices_alternate[] = {0,1,3,2,3,1};
706 video::ITexture *texture = f.tile.texture.atlas;
710 material.setTexture(0, texture);
712 f.tile.applyMaterialOptions(material);
714 const u16 *indices_p = indices;
717 Revert triangles for nicer looking gradient if vertices
718 1 and 3 have same color or 0 and 2 have different color.
720 if(f.vertices[0].Color != f.vertices[2].Color
721 || f.vertices[1].Color == f.vertices[3].Color)
722 indices_p = indices_alternate;
724 collector.append(material, f.vertices, 4, indices_p, 6);
729 Add special graphics:
736 mapblock_mesh_generate_special(data, collector);
739 Add stuff from collector to mesh
742 scene::SMesh *mesh_new = NULL;
743 mesh_new = new scene::SMesh();
745 collector.fillMesh(mesh_new);
748 Do some stuff to the mesh
751 mesh_new->recalculateBoundingBox();
754 Delete new mesh if it is empty
757 if(mesh_new->getMeshBufferCount() == 0)
766 // Usually 1-700 faces and 1-7 materials
767 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
768 <<"and uses "<<mesh_new->getMeshBufferCount()
769 <<" materials (meshbuffers)"<<std::endl;
772 // Use VBO for mesh (this just would set this for ever buffer)
773 // This will lead to infinite memory usage because or irrlicht.
774 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
777 NOTE: If that is enabled, some kind of a queue to the main
778 thread should be made which would call irrlicht to delete
779 the hardware buffer and then delete the mesh
785 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;