3 Copyright (C) 2010 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.
32 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
36 is_underground(false),
37 m_day_night_differs(false),
44 m_spawn_timer = -10000;
47 m_mesh_expired = false;
50 m_temp_mods_mutex.Init();
58 JMutexAutoLock lock(mesh_mutex);
72 bool MapBlock::isValidPositionParent(v3s16 p)
74 if(isValidPosition(p))
79 return m_parent->isValidPosition(getPosRelative() + p);
83 MapNode MapBlock::getNodeParent(v3s16 p)
85 if(isValidPosition(p) == false)
87 return m_parent->getNode(getPosRelative() + p);
92 throw InvalidPositionException();
93 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
97 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
99 if(isValidPosition(p) == false)
101 m_parent->setNode(getPosRelative() + p, n);
106 throw InvalidPositionException();
107 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
111 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
113 if(isValidPosition(p) == false)
116 return m_parent->getNode(getPosRelative() + p);
118 catch(InvalidPositionException &e)
120 return MapNode(CONTENT_IGNORE);
127 return MapNode(CONTENT_IGNORE);
129 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
134 Parameters must consist of air and !air.
135 Order doesn't matter.
137 If either of the nodes doesn't exist, light is 0.
140 daynight_ratio: 0...1000
142 n2: getNodeParent(p + face_dir)
143 face_dir: axis oriented unit vector from p to p2
145 returns encoded light value.
147 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
153 if(n.d == CONTENT_WATER)
160 if(n2.d == CONTENT_WATER)
171 u8 l1 = n.getLightBlend(daynight_ratio);
172 u8 l2 = n2.getLightBlend(daynight_ratio);
178 // Make some nice difference to different sides
180 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
181 light = diminish_light(diminish_light(light));
182 else if(face_dir.X == -1 || face_dir.Z == -1)
183 light = diminish_light(light);*/
185 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
186 light = diminish_light(diminish_light(light));
187 else if(face_dir.Z == 1 || face_dir.Z == -1)
188 light = diminish_light(light);
192 catch(InvalidPositionException &e)
200 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
201 v3s16 dir, v3f scale, v3f posRelative_f,
202 core::array<FastFace> &dest)
206 // Position is at the center of the cube.
211 // If looking towards z+, this is the face that is behind
212 // the center point, facing towards z+.
213 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
214 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
215 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
216 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
218 if(dir == v3s16(0,0,1))
220 for(u16 i=0; i<4; i++)
221 vertex_pos[i].rotateXZBy(0);
223 else if(dir == v3s16(0,0,-1))
225 for(u16 i=0; i<4; i++)
226 vertex_pos[i].rotateXZBy(180);
228 else if(dir == v3s16(1,0,0))
230 for(u16 i=0; i<4; i++)
231 vertex_pos[i].rotateXZBy(-90);
233 else if(dir == v3s16(-1,0,0))
235 for(u16 i=0; i<4; i++)
236 vertex_pos[i].rotateXZBy(90);
238 else if(dir == v3s16(0,1,0))
240 for(u16 i=0; i<4; i++)
241 vertex_pos[i].rotateYZBy(-90);
243 else if(dir == v3s16(0,-1,0))
245 for(u16 i=0; i<4; i++)
246 vertex_pos[i].rotateYZBy(90);
249 for(u16 i=0; i<4; i++)
251 vertex_pos[i].X *= scale.X;
252 vertex_pos[i].Y *= scale.Y;
253 vertex_pos[i].Z *= scale.Z;
254 vertex_pos[i] += pos + posRelative_f;
258 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
259 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
260 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
262 v3f zerovector = v3f(0,0,0);
264 //u8 li = decode_light(light);
266 //u8 li = 255; //DEBUG
268 u8 alpha = tile.alpha;
270 if(tile.id == TILE_WATER)
271 alpha = WATER_ALPHA;*/
273 video::SColor c = video::SColor(alpha,li,li,li);
275 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
276 core::vector2d<f32>(0,1));
277 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
278 core::vector2d<f32>(abs_scale,1));
279 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
280 core::vector2d<f32>(abs_scale,0));
281 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
282 core::vector2d<f32>(0,0));
286 //f->tile = TILE_STONE;
288 dest.push_back(face);
293 Gets node tile from any place relative to block.
294 Returns TILE_NODE if doesn't exist or should not be drawn.
296 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
299 spec = mn.getTile(face_dir);
302 Check temporary modifications on this node
304 core::map<v3s16, NodeMod>::Node *n;
305 n = m_temp_mods.find(p);
310 struct NodeMod mod = n->getValue();
311 if(mod.type == NODEMOD_CHANGECONTENT)
313 MapNode mn2(mod.param);
314 spec = mn2.getTile(face_dir);
316 if(mod.type == NODEMOD_CRACK)
318 std::ostringstream os;
319 os<<"[crack"<<mod.param;
321 textureid_t tid = g_irrlicht->getTextureId(os.str());
322 spec.spec.addTid(tid);
329 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
332 Check temporary modifications on this node
334 core::map<v3s16, NodeMod>::Node *n;
335 n = m_temp_mods.find(p);
340 struct NodeMod mod = n->getValue();
341 if(mod.type == NODEMOD_CHANGECONTENT)
346 if(mod.type == NODEMOD_CRACK)
349 Content doesn't change.
351 face_contents works just like it should, because
352 there should not be faces between differently cracked
355 If a semi-transparent node is cracked in front an
356 another one, it really doesn't matter whether there
357 is a cracked face drawn in between or not.
367 translate_dir: unit vector with only one of x, y or z
368 face_dir: unit vector with only one of x, y or z
370 void MapBlock::updateFastFaceRow(
379 core::array<FastFace> &dest)
383 u16 continuous_tiles_count = 0;
385 MapNode n0 = getNodeParentNoEx(p);
386 MapNode n1 = getNodeParentNoEx(p + face_dir);
388 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
390 TileSpec tile0 = getNodeTile(n0, p, face_dir);
391 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
393 for(u16 j=0; j<length; j++)
395 bool next_is_different = true;
406 p_next = p + translate_dir;
407 n0_next = getNodeParentNoEx(p_next);
408 n1_next = getNodeParentNoEx(p_next + face_dir);
409 tile0_next = getNodeTile(n0_next, p_next, face_dir);
410 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
411 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
413 if(tile0_next == tile0
414 && tile1_next == tile1
415 && light_next == light)
417 next_is_different = false;
421 continuous_tiles_count++;
423 if(next_is_different)
426 Create a face if there should be one
428 //u8 mf = face_contents(tile0, tile1);
430 u8 content0 = getNodeContent(p, n0);
431 u8 content1 = getNodeContent(p + face_dir, n1);
432 u8 mf = face_contents(content0, content1);
436 // Floating point conversion of the position vector
437 v3f pf(p.X, p.Y, p.Z);
438 // Center point of face (kind of)
439 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
441 if(translate_dir.X != 0){
442 scale.X = continuous_tiles_count;
444 if(translate_dir.Y != 0){
445 scale.Y = continuous_tiles_count;
447 if(translate_dir.Z != 0){
448 scale.Z = continuous_tiles_count;
453 // If node at sp (tile0) is more solid
456 makeFastFace(tile0, decode_light(light),
458 posRelative_f, dest);
460 // If node at sp is less solid (mf == 2)
463 makeFastFace(tile1, decode_light(light),
464 sp+face_dir_f, -face_dir, scale,
465 posRelative_f, dest);
470 continuous_tiles_count = 0;
483 This is used because CMeshBuffer::append() is very slow
487 video::SMaterial material;
488 core::array<u16> indices;
489 core::array<video::S3DVertex> vertices;
496 video::SMaterial material,
497 const video::S3DVertex* const vertices,
499 const u16* const indices,
503 PreMeshBuffer *p = NULL;
504 for(u32 i=0; i<m_prebuffers.size(); i++)
506 PreMeshBuffer &pp = m_prebuffers[i];
507 if(pp.material != material)
517 pp.material = material;
518 m_prebuffers.push_back(pp);
519 p = &m_prebuffers[m_prebuffers.size()-1];
522 u32 vertex_count = p->vertices.size();
523 for(u32 i=0; i<numIndices; i++)
525 u32 j = indices[i] + vertex_count;
528 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
529 // NOTE: Fix is to just add an another MeshBuffer
531 p->indices.push_back(j);
533 for(u32 i=0; i<numVertices; i++)
535 p->vertices.push_back(vertices[i]);
539 void fillMesh(scene::SMesh *mesh)
541 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
542 <<" meshbuffers"<<std::endl;*/
543 for(u32 i=0; i<m_prebuffers.size(); i++)
545 PreMeshBuffer &p = m_prebuffers[i];
547 /*dstream<<"p.vertices.size()="<<p.vertices.size()
548 <<", p.indices.size()="<<p.indices.size()
553 // This is a "Standard MeshBuffer",
554 // it's a typedeffed CMeshBuffer<video::S3DVertex>
555 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
557 buf->Material = p.material;
558 //((scene::SMeshBuffer*)buf)->Material = p.material;
560 //buf->setHardwareMappingHint(scene::EHM_STATIC);
562 mesh->addMeshBuffer(buf);
566 buf->append(p.vertices.pointer(), p.vertices.size(),
567 p.indices.pointer(), p.indices.size());
572 core::array<PreMeshBuffer> m_prebuffers;
575 void MapBlock::updateMesh(u32 daynight_ratio)
579 DEBUG: If mesh has been generated, don't generate it again
582 JMutexAutoLock meshlock(mesh_mutex);
588 // 4-21ms for MAP_BLOCKSIZE=16
589 // 24-155ms for MAP_BLOCKSIZE=32
590 //TimeTaker timer1("updateMesh()");
592 core::array<FastFace> fastfaces_new;
594 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
595 getPosRelative().Z); // floating point conversion
598 We are including the faces of the trailing edges of the block.
599 This means that when something changes, the caller must
600 also update the meshes of the blocks at the leading edges.
602 NOTE: This is the slowest part of this method.
606 // 4-23ms for MAP_BLOCKSIZE=16
607 //TimeTaker timer2("updateMesh() collect");
609 // Lock this, as m_temp_mods will be used directly
610 JMutexAutoLock lock(m_temp_mods_mutex);
613 Go through every y,z and get top faces in rows of x+
615 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
616 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
617 updateFastFaceRow(daynight_ratio, posRelative_f,
618 v3s16(0,y,z), MAP_BLOCKSIZE,
621 v3s16(0,1,0), //face dir
627 Go through every x,y and get right faces in rows of z+
629 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
630 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
631 updateFastFaceRow(daynight_ratio, posRelative_f,
632 v3s16(x,y,0), MAP_BLOCKSIZE,
641 Go through every y,z and get back faces in rows of x+
643 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
644 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
645 updateFastFaceRow(daynight_ratio, posRelative_f,
646 v3s16(0,y,z), MAP_BLOCKSIZE,
659 Convert FastFaces to SMesh
662 scene::SMesh *mesh_new = NULL;
664 mesh_new = new scene::SMesh();
666 MeshCollector collector;
668 if(fastfaces_new.size() > 0)
670 // avg 0ms (100ms spikes when loading textures the first time)
671 //TimeTaker timer2("updateMesh() mesh building");
673 video::SMaterial material;
674 material.Lighting = false;
675 material.BackfaceCulling = false;
676 material.setFlag(video::EMF_BILINEAR_FILTER, false);
677 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
678 material.setFlag(video::EMF_FOG_ENABLE, true);
680 for(u32 i=0; i<fastfaces_new.size(); i++)
682 FastFace &f = fastfaces_new[i];
684 const u16 indices[] = {0,1,2,2,3,0};
686 video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
687 material.setTexture(0, texture);
688 if(f.tile.alpha != 255)
689 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
691 material.MaterialType = video::EMT_SOLID;
693 collector.append(material, f.vertices, 4, indices, 6);
698 Add special graphics:
704 //TimeTaker timer2("updateMesh() adding special stuff");
706 // Flowing water material
707 video::SMaterial material_w1;
708 material_w1.setFlag(video::EMF_LIGHTING, false);
709 material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
710 material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
711 material_w1.setFlag(video::EMF_FOG_ENABLE, true);
712 material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
713 material_w1.setTexture(0,
714 g_irrlicht->getTexture("water.png"));
716 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
717 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
718 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
722 MapNode &n = getNodeRef(x,y,z);
727 if(n.d == CONTENT_TORCH)
729 video::SColor c(255,255,255,255);
731 video::S3DVertex vertices[4] =
733 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
734 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
735 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
736 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
739 v3s16 dir = unpackDir(n.dir);
741 for(s32 i=0; i<4; i++)
743 if(dir == v3s16(1,0,0))
744 vertices[i].Pos.rotateXZBy(0);
745 if(dir == v3s16(-1,0,0))
746 vertices[i].Pos.rotateXZBy(180);
747 if(dir == v3s16(0,0,1))
748 vertices[i].Pos.rotateXZBy(90);
749 if(dir == v3s16(0,0,-1))
750 vertices[i].Pos.rotateXZBy(-90);
751 if(dir == v3s16(0,-1,0))
752 vertices[i].Pos.rotateXZBy(45);
753 if(dir == v3s16(0,1,0))
754 vertices[i].Pos.rotateXZBy(-45);
756 vertices[i].Pos += intToFloat(p + getPosRelative());
760 video::SMaterial material;
761 material.setFlag(video::EMF_LIGHTING, false);
762 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
763 material.setFlag(video::EMF_BILINEAR_FILTER, false);
764 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
765 material.MaterialType
766 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
767 if(dir == v3s16(0,-1,0))
768 material.setTexture(0,
769 g_irrlicht->getTexture("torch_on_floor.png"));
770 else if(dir == v3s16(0,1,0))
771 material.setTexture(0,
772 g_irrlicht->getTexture("torch_on_ceiling.png"));
773 // For backwards compatibility
774 else if(dir == v3s16(0,0,0))
775 material.setTexture(0,
776 g_irrlicht->getTexture("torch_on_floor.png"));
778 material.setTexture(0,
779 g_irrlicht->getTexture("torch.png"));
781 u16 indices[] = {0,1,2,2,3,0};
782 // Add to mesh collector
783 collector.append(material, vertices, 4, indices, 6);
786 Add flowing water to mesh
788 else if(n.d == CONTENT_WATER)
790 bool top_is_water = false;
792 MapNode n = getNodeParent(v3s16(x,y+1,z));
793 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
795 }catch(InvalidPositionException &e){}
797 u8 l = decode_light(n.getLightBlend(daynight_ratio));
798 video::SColor c(WATER_ALPHA,l,l,l);
800 // Neighbor water levels (key = relative position)
801 // Includes current node
802 core::map<v3s16, f32> neighbor_levels;
803 core::map<v3s16, u8> neighbor_contents;
804 v3s16 neighbor_dirs[9] = {
815 for(u32 i=0; i<9; i++)
817 u8 content = CONTENT_AIR;
818 float level = -0.5 * BS;
820 v3s16 p2 = p + neighbor_dirs[i];
821 MapNode n2 = getNodeParent(p2);
825 if(n2.d == CONTENT_WATERSOURCE)
827 else if(n2.d == CONTENT_WATER)
828 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
830 catch(InvalidPositionException &e){}
832 neighbor_levels.insert(neighbor_dirs[i], level);
833 neighbor_contents.insert(neighbor_dirs[i], content);
836 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
837 //float water_level = neighbor_levels[v3s16(0,0,0)];
839 // Corner heights (average between four waters)
840 f32 corner_levels[4];
842 v3s16 halfdirs[4] = {
848 for(u32 i=0; i<4; i++)
850 v3s16 cornerdir = halfdirs[i];
851 float cornerlevel = 0;
853 for(u32 j=0; j<4; j++)
855 v3s16 neighbordir = cornerdir - halfdirs[j];
856 u8 content = neighbor_contents[neighbordir];
857 // Special case for source nodes
858 if(content == CONTENT_WATERSOURCE)
860 cornerlevel = 0.5*BS;
864 else if(content == CONTENT_WATER)
866 cornerlevel += neighbor_levels[neighbordir];
869 else if(content == CONTENT_AIR)
871 cornerlevel += -0.5*BS;
876 cornerlevel /= valid_count;
877 corner_levels[i] = cornerlevel;
884 v3s16 side_dirs[4] = {
890 s16 side_corners[4][2] = {
896 for(u32 i=0; i<4; i++)
898 v3s16 dir = side_dirs[i];
900 //float neighbor_level = neighbor_levels[dir];
901 /*if(neighbor_level > -0.5*BS + 0.001)
903 /*if(neighbor_level > water_level - 0.1*BS)
906 u8 neighbor_content = neighbor_contents[dir];
908 if(neighbor_content != CONTENT_AIR
909 && neighbor_content != CONTENT_WATER)
912 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
914 if(neighbor_is_water == true && top_is_water == false)
917 video::S3DVertex vertices[4] =
919 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
920 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
921 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
922 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
923 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
924 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
925 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
926 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
931 vertices[2].Pos.Y = 0.5*BS;
932 vertices[3].Pos.Y = 0.5*BS;
936 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
937 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
940 if(neighbor_is_water)
942 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
943 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
947 vertices[0].Pos.Y = -0.5*BS;
948 vertices[1].Pos.Y = -0.5*BS;
951 for(s32 j=0; j<4; j++)
953 if(dir == v3s16(0,0,1))
954 vertices[j].Pos.rotateXZBy(0);
955 if(dir == v3s16(0,0,-1))
956 vertices[j].Pos.rotateXZBy(180);
957 if(dir == v3s16(-1,0,0))
958 vertices[j].Pos.rotateXZBy(90);
959 if(dir == v3s16(1,0,-0))
960 vertices[j].Pos.rotateXZBy(-90);
962 vertices[j].Pos += intToFloat(p + getPosRelative());
965 u16 indices[] = {0,1,2,2,3,0};
966 // Add to mesh collector
967 collector.append(material_w1, vertices, 4, indices, 6);
971 Generate top side, if appropriate
974 if(top_is_water == false)
976 video::S3DVertex vertices[4] =
978 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
979 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
980 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
981 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
984 for(s32 i=0; i<4; i++)
986 //vertices[i].Pos.Y += water_level;
987 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
988 vertices[i].Pos.Y += corner_levels[i];
989 vertices[i].Pos += intToFloat(p + getPosRelative());
992 u16 indices[] = {0,1,2,2,3,0};
993 // Add to mesh collector
994 collector.append(material_w1, vertices, 4, indices, 6);
1000 Add stuff from collector to mesh
1003 collector.fillMesh(mesh_new);
1006 Do some stuff to the mesh
1009 mesh_new->recalculateBoundingBox();
1012 Delete new mesh if it is empty
1015 if(mesh_new->getMeshBufferCount() == 0)
1021 // Use VBO for mesh (this just would set this for ever buffer)
1022 // This will lead to infinite memory usage because or irrlicht.
1023 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1025 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1026 <<"and uses "<<mesh_new->getMeshBufferCount()
1027 <<" materials (meshbuffers)"<<std::endl;*/
1035 //scene::SMesh *mesh_old = mesh[daynight_i];
1036 //mesh[daynight_i] = mesh_new;
1038 scene::SMesh *mesh_old = mesh;
1040 setMeshExpired(false);
1042 if(mesh_old != NULL)
1044 // Remove hardware buffers of meshbuffers of mesh
1045 // NOTE: No way, this runs in a different thread and everything
1046 /*u32 c = mesh_old->getMeshBufferCount();
1047 for(u32 i=0; i<c; i++)
1049 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1052 /*dstream<<"mesh_old->getReferenceCount()="
1053 <<mesh_old->getReferenceCount()<<std::endl;
1054 u32 c = mesh_old->getMeshBufferCount();
1055 for(u32 i=0; i<c; i++)
1057 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1058 dstream<<"buf->getReferenceCount()="
1059 <<buf->getReferenceCount()<<std::endl;
1068 mesh_mutex.Unlock();
1070 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1073 /*void MapBlock::updateMeshes(s32 first_i)
1075 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1076 updateMesh(first_i);
1077 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1088 Propagates sunlight down through the block.
1089 Doesn't modify nodes that are not affected by sunlight.
1091 Returns false if sunlight at bottom block is invalid
1092 Returns true if bottom block doesn't exist.
1094 If there is a block above, continues from it.
1095 If there is no block above, assumes there is sunlight, unless
1096 is_underground is set or highest node is water.
1098 At the moment, all sunlighted nodes are added to light_sources.
1099 - SUGG: This could be optimized
1101 Turns sunglighted mud into grass.
1103 if remove_light==true, sets non-sunlighted nodes black.
1105 if black_air_left!=NULL, it is set to true if non-sunlighted
1106 air is left in block.
1108 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1109 bool remove_light, bool *black_air_left,
1112 // Whether the sunlight at the top of the bottom block is valid
1113 bool block_below_is_valid = true;
1115 v3s16 pos_relative = getPosRelative();
1117 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1119 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1122 bool no_sunlight = false;
1123 bool no_top_block = false;
1124 // Check if node above block has sunlight
1126 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1127 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1132 catch(InvalidPositionException &e)
1134 no_top_block = true;
1136 // NOTE: This makes over-ground roofed places sunlighted
1137 // Assume sunlight, unless is_underground==true
1144 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1145 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1150 // NOTE: As of now, this just would make everything dark.
1152 //no_sunlight = true;
1155 #if 0 // Doesn't work; nothing gets light.
1156 bool no_sunlight = true;
1157 bool no_top_block = false;
1158 // Check if node above block has sunlight
1160 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1161 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1163 no_sunlight = false;
1166 catch(InvalidPositionException &e)
1168 no_top_block = true;
1172 /*std::cout<<"("<<x<<","<<z<<"): "
1173 <<"no_top_block="<<no_top_block
1174 <<", is_underground="<<is_underground
1175 <<", no_sunlight="<<no_sunlight
1178 s16 y = MAP_BLOCKSIZE-1;
1180 // This makes difference to diminishing in water.
1181 bool stopped_to_solid_object = false;
1183 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1188 MapNode &n = getNodeRef(pos);
1190 if(current_light == 0)
1194 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1196 // Do nothing: Sunlight is continued
1198 else if(n.light_propagates() == false)
1202 bool upper_is_air = false;
1205 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1206 upper_is_air = true;
1208 catch(InvalidPositionException &e)
1211 // Turn mud into grass
1212 if(upper_is_air && n.d == CONTENT_MUD
1213 && current_light == LIGHT_SUN)
1215 n.d = CONTENT_GRASS;
1219 // A solid object is on the way.
1220 stopped_to_solid_object = true;
1228 current_light = diminish_light(current_light);
1231 u8 old_light = n.getLight(LIGHTBANK_DAY);
1233 if(current_light > old_light || remove_light)
1235 n.setLight(LIGHTBANK_DAY, current_light);
1238 if(diminish_light(current_light) != 0)
1240 light_sources.insert(pos_relative + pos, true);
1243 if(current_light == 0 && stopped_to_solid_object)
1247 *black_air_left = true;
1252 // Whether or not the block below should see LIGHT_SUN
1253 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1256 If the block below hasn't already been marked invalid:
1258 Check if the node below the block has proper sunlight at top.
1259 If not, the block below is invalid.
1261 Ignore non-transparent nodes as they always have no light
1265 if(block_below_is_valid)
1267 MapNode n = getNodeParent(v3s16(x, -1, z));
1268 if(n.light_propagates())
1270 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1271 && sunlight_should_go_down == false)
1272 block_below_is_valid = false;
1273 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1274 && sunlight_should_go_down == true)
1275 block_below_is_valid = false;
1279 catch(InvalidPositionException &e)
1281 /*std::cout<<"InvalidBlockException for bottom block node"
1283 // Just no block below, no need to panic.
1288 return block_below_is_valid;
1291 void MapBlock::copyTo(VoxelManipulator &dst)
1293 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1294 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1296 dst.copyFrom(data, data_area, v3s16(0,0,0),
1297 getPosRelative(), data_size);
1300 /*void getPseudoObjects(v3f origin, f32 max_d,
1301 core::array<DistanceSortedObject> &dest)
1304 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1309 m_objects.step(dtime, server, daynight_ratio);
1312 Spawn some objects at random.
1314 Use dayNightDiffed() to approximate being near ground level
1316 if(m_spawn_timer < -999)
1320 if(dayNightDiffed() == true && getObjectCount() == 0)
1322 m_spawn_timer -= dtime;
1323 if(m_spawn_timer <= 0.0)
1325 m_spawn_timer += myrand() % 300;
1328 (myrand()%(MAP_BLOCKSIZE-1))+0,
1329 (myrand()%(MAP_BLOCKSIZE-1))+0
1332 s16 y = getGroundLevel(p2d);
1336 v3s16 p(p2d.X, y+1, p2d.Y);
1338 if(getNode(p).d == CONTENT_AIR
1339 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1341 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1352 void MapBlock::updateDayNightDiff()
1356 m_day_night_differs = false;
1360 bool differs = false;
1363 Check if any lighting value differs
1365 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1367 MapNode &n = data[i];
1368 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1376 If some lighting values differ, check if the whole thing is
1377 just air. If it is, differ = false
1381 bool only_air = true;
1382 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1384 MapNode &n = data[i];
1385 if(n.d != CONTENT_AIR)
1395 // Set member variable
1396 m_day_night_differs = differs;
1399 s16 MapBlock::getGroundLevel(v2s16 p2d)
1405 s16 y = MAP_BLOCKSIZE-1;
1408 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1409 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1411 if(y == MAP_BLOCKSIZE-1)
1419 catch(InvalidPositionException &e)
1429 void MapBlock::serialize(std::ostream &os, u8 version)
1431 if(!ser_ver_supported(version))
1432 throw VersionMismatchException("ERROR: MapBlock format not supported");
1436 throw SerializationError("ERROR: Not writing dummy block.");
1439 // These have no compression
1440 if(version <= 3 || version == 5 || version == 6)
1442 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1444 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1445 SharedBuffer<u8> dest(buflen);
1447 dest[0] = is_underground;
1448 for(u32 i=0; i<nodecount; i++)
1450 u32 s = 1 + i * MapNode::serializedLength(version);
1451 data[i].serialize(&dest[s], version);
1454 os.write((char*)*dest, dest.getSize());
1456 else if(version <= 10)
1460 Compress the materials and the params separately.
1464 os.write((char*)&is_underground, 1);
1466 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1468 // Get and compress materials
1469 SharedBuffer<u8> materialdata(nodecount);
1470 for(u32 i=0; i<nodecount; i++)
1472 materialdata[i] = data[i].d;
1474 compress(materialdata, os, version);
1476 // Get and compress lights
1477 SharedBuffer<u8> lightdata(nodecount);
1478 for(u32 i=0; i<nodecount; i++)
1480 lightdata[i] = data[i].param;
1482 compress(lightdata, os, version);
1486 // Get and compress param2
1487 SharedBuffer<u8> param2data(nodecount);
1488 for(u32 i=0; i<nodecount; i++)
1490 param2data[i] = data[i].param2;
1492 compress(param2data, os, version);
1495 // All other versions (newest)
1502 if(m_day_night_differs)
1504 os.write((char*)&flags, 1);
1506 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1512 SharedBuffer<u8> databuf(nodecount*3);
1515 for(u32 i=0; i<nodecount; i++)
1517 databuf[i] = data[i].d;
1521 for(u32 i=0; i<nodecount; i++)
1523 databuf[i+nodecount] = data[i].param;
1527 for(u32 i=0; i<nodecount; i++)
1529 databuf[i+nodecount*2] = data[i].param2;
1533 Compress data to output stream
1536 compress(databuf, os, version);
1540 void MapBlock::deSerialize(std::istream &is, u8 version)
1542 if(!ser_ver_supported(version))
1543 throw VersionMismatchException("ERROR: MapBlock format not supported");
1545 // These have no compression
1546 if(version <= 3 || version == 5 || version == 6)
1548 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1551 if(is.gcount() != 1)
1552 throw SerializationError
1553 ("MapBlock::deSerialize: no enough input data");
1554 is_underground = tmp;
1555 for(u32 i=0; i<nodecount; i++)
1557 s32 len = MapNode::serializedLength(version);
1558 SharedBuffer<u8> d(len);
1559 is.read((char*)*d, len);
1560 if(is.gcount() != len)
1561 throw SerializationError
1562 ("MapBlock::deSerialize: no enough input data");
1563 data[i].deSerialize(*d, version);
1566 else if(version <= 10)
1568 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1571 is.read((char*)&t8, 1);
1572 is_underground = t8;
1575 // Uncompress and set material data
1576 std::ostringstream os(std::ios_base::binary);
1577 decompress(is, os, version);
1578 std::string s = os.str();
1579 if(s.size() != nodecount)
1580 throw SerializationError
1581 ("MapBlock::deSerialize: invalid format");
1582 for(u32 i=0; i<s.size(); i++)
1588 // Uncompress and set param data
1589 std::ostringstream os(std::ios_base::binary);
1590 decompress(is, os, version);
1591 std::string s = os.str();
1592 if(s.size() != nodecount)
1593 throw SerializationError
1594 ("MapBlock::deSerialize: invalid format");
1595 for(u32 i=0; i<s.size(); i++)
1597 data[i].param = s[i];
1603 // Uncompress and set param2 data
1604 std::ostringstream os(std::ios_base::binary);
1605 decompress(is, os, version);
1606 std::string s = os.str();
1607 if(s.size() != nodecount)
1608 throw SerializationError
1609 ("MapBlock::deSerialize: invalid format");
1610 for(u32 i=0; i<s.size(); i++)
1612 data[i].param2 = s[i];
1616 // All other versions (newest)
1619 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1622 is.read((char*)&flags, 1);
1623 is_underground = (flags & 1) ? true : false;
1624 m_day_night_differs = (flags & 2) ? true : false;
1627 std::ostringstream os(std::ios_base::binary);
1628 decompress(is, os, version);
1629 std::string s = os.str();
1630 if(s.size() != nodecount*3)
1631 throw SerializationError
1632 ("MapBlock::deSerialize: invalid format");
1635 for(u32 i=0; i<nodecount; i++)
1640 for(u32 i=0; i<nodecount; i++)
1642 data[i].param = s[i+nodecount];
1645 for(u32 i=0; i<nodecount; i++)
1647 data[i].param2 = s[i+nodecount*2];