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 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
150 u8 l1 = n.getLightBlend(daynight_ratio);
151 u8 l2 = n2.getLightBlend(daynight_ratio);
157 // Make some nice difference to different sides
159 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
160 light = diminish_light(diminish_light(light));
161 else if(face_dir.X == -1 || face_dir.Z == -1)
162 light = diminish_light(light);*/
164 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
165 light = diminish_light(diminish_light(light));
166 else if(face_dir.Z == 1 || face_dir.Z == -1)
167 light = diminish_light(light);
171 catch(InvalidPositionException &e)
179 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
180 v3s16 dir, v3f scale, v3f posRelative_f,
181 core::array<FastFace> &dest)
185 // Position is at the center of the cube.
190 // If looking towards z+, this is the face that is behind
191 // the center point, facing towards z+.
192 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
193 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
194 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
195 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
197 if(dir == v3s16(0,0,1))
199 for(u16 i=0; i<4; i++)
200 vertex_pos[i].rotateXZBy(0);
202 else if(dir == v3s16(0,0,-1))
204 for(u16 i=0; i<4; i++)
205 vertex_pos[i].rotateXZBy(180);
207 else if(dir == v3s16(1,0,0))
209 for(u16 i=0; i<4; i++)
210 vertex_pos[i].rotateXZBy(-90);
212 else if(dir == v3s16(-1,0,0))
214 for(u16 i=0; i<4; i++)
215 vertex_pos[i].rotateXZBy(90);
217 else if(dir == v3s16(0,1,0))
219 for(u16 i=0; i<4; i++)
220 vertex_pos[i].rotateYZBy(-90);
222 else if(dir == v3s16(0,-1,0))
224 for(u16 i=0; i<4; i++)
225 vertex_pos[i].rotateYZBy(90);
228 for(u16 i=0; i<4; i++)
230 vertex_pos[i].X *= scale.X;
231 vertex_pos[i].Y *= scale.Y;
232 vertex_pos[i].Z *= scale.Z;
233 vertex_pos[i] += pos + posRelative_f;
237 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
238 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
239 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
241 v3f zerovector = v3f(0,0,0);
243 //u8 li = decode_light(light);
248 if(tile.id == TILE_WATER)
253 video::SColor c = video::SColor(alpha,li,li,li);
255 face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
256 core::vector2d<f32>(0,1));
257 face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
258 core::vector2d<f32>(abs_scale,1));
259 face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
260 core::vector2d<f32>(abs_scale,0));
261 face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
262 core::vector2d<f32>(0,0));
266 //f->tile = TILE_STONE;
268 dest.push_back(face);
273 Gets node tile from any place relative to block.
274 Returns TILE_NODE if doesn't exist or should not be drawn.
276 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
282 spec.id = TILE_STONE;
286 spec.feature = TILEFEAT_NONE;
287 //spec.id = TILE_STONE;
288 spec.id = mn.getTile(face_dir);
291 Check temporary modifications on this node
293 core::map<v3s16, NodeMod>::Node *n;
294 n = m_temp_mods.find(p);
299 struct NodeMod mod = n->getValue();
300 if(mod.type == NODEMOD_CHANGECONTENT)
302 spec.id = content_tile(mod.param, face_dir);
304 if(mod.type == NODEMOD_CRACK)
306 spec.feature = TILEFEAT_CRACK;
307 spec.param.crack.progression = mod.param;
314 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
317 Check temporary modifications on this node
319 core::map<v3s16, NodeMod>::Node *n;
320 n = m_temp_mods.find(p);
325 struct NodeMod mod = n->getValue();
326 if(mod.type == NODEMOD_CHANGECONTENT)
331 if(mod.type == NODEMOD_CRACK)
334 Content doesn't change.
336 face_contents works just like it should, because
337 there should not be faces between differently cracked
340 If a semi-transparent node is cracked in front an
341 another one, it really doesn't matter whether there
342 is a cracked face drawn in between or not.
352 translate_dir: unit vector with only one of x, y or z
353 face_dir: unit vector with only one of x, y or z
355 void MapBlock::updateFastFaceRow(
364 core::array<FastFace> &dest)
368 u16 continuous_tiles_count = 0;
370 MapNode n0 = getNodeParentNoEx(p);
371 MapNode n1 = getNodeParentNoEx(p + face_dir);
373 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
375 TileSpec tile0 = getNodeTile(n0, p, face_dir);
376 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
378 for(u16 j=0; j<length; j++)
380 bool next_is_different = true;
391 p_next = p + translate_dir;
392 n0_next = getNodeParentNoEx(p_next);
393 n1_next = getNodeParentNoEx(p_next + face_dir);
394 tile0_next = getNodeTile(n0_next, p_next, face_dir);
395 tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
396 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
398 if(tile0_next == tile0
399 && tile1_next == tile1
400 && light_next == light)
402 next_is_different = false;
406 continuous_tiles_count++;
408 if(next_is_different)
411 Create a face if there should be one
413 //u8 mf = face_contents(tile0, tile1);
415 u8 content0 = getNodeContent(p, n0);
416 u8 content1 = getNodeContent(p + face_dir, n1);
417 u8 mf = face_contents(content0, content1);
421 // Floating point conversion of the position vector
422 v3f pf(p.X, p.Y, p.Z);
423 // Center point of face (kind of)
424 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
426 if(translate_dir.X != 0){
427 scale.X = continuous_tiles_count;
429 if(translate_dir.Y != 0){
430 scale.Y = continuous_tiles_count;
432 if(translate_dir.Z != 0){
433 scale.Z = continuous_tiles_count;
438 // If node at sp (tile0) is more solid
441 makeFastFace(tile0, decode_light(light),
443 posRelative_f, dest);
445 // If node at sp is less solid (mf == 2)
448 makeFastFace(tile1, decode_light(light),
449 sp+face_dir_f, -face_dir, scale,
450 posRelative_f, dest);
455 continuous_tiles_count = 0;
468 This is used because CMeshBuffer::append() is very slow
472 video::SMaterial material;
473 core::array<u16> indices;
474 core::array<video::S3DVertex> vertices;
481 video::SMaterial material,
482 const video::S3DVertex* const vertices,
484 const u16* const indices,
488 PreMeshBuffer *p = NULL;
489 for(u32 i=0; i<m_prebuffers.size(); i++)
491 PreMeshBuffer &pp = m_prebuffers[i];
492 if(pp.material != material)
502 pp.material = material;
503 m_prebuffers.push_back(pp);
504 p = &m_prebuffers[m_prebuffers.size()-1];
507 u32 vertex_count = p->vertices.size();
508 for(u32 i=0; i<numIndices; i++)
510 u32 j = indices[i] + vertex_count;
513 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
514 // NOTE: Fix is to just add an another MeshBuffer
516 p->indices.push_back(j);
518 for(u32 i=0; i<numVertices; i++)
520 p->vertices.push_back(vertices[i]);
524 void fillMesh(scene::SMesh *mesh)
526 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
527 <<" meshbuffers"<<std::endl;*/
528 for(u32 i=0; i<m_prebuffers.size(); i++)
530 PreMeshBuffer &p = m_prebuffers[i];
532 /*dstream<<"p.vertices.size()="<<p.vertices.size()
533 <<", p.indices.size()="<<p.indices.size()
538 // This is a "Standard MeshBuffer",
539 // it's a typedeffed CMeshBuffer<video::S3DVertex>
540 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
542 buf->Material = p.material;
543 //((scene::SMeshBuffer*)buf)->Material = p.material;
545 //buf->setHardwareMappingHint(scene::EHM_STATIC);
547 mesh->addMeshBuffer(buf);
551 buf->append(p.vertices.pointer(), p.vertices.size(),
552 p.indices.pointer(), p.indices.size());
557 core::array<PreMeshBuffer> m_prebuffers;
560 void MapBlock::updateMesh(u32 daynight_ratio)
564 DEBUG: If mesh has been generated, don't generate it again
567 JMutexAutoLock meshlock(mesh_mutex);
574 //TimeTaker timer1("updateMesh()", g_device);
576 core::array<FastFace> fastfaces_new;
578 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
579 getPosRelative().Z); // floating point conversion
582 We are including the faces of the trailing edges of the block.
583 This means that when something changes, the caller must
584 also update the meshes of the blocks at the leading edges.
586 NOTE: This is the slowest part of this method.
590 // Lock this, as m_temp_mods will be used directly
591 JMutexAutoLock lock(m_temp_mods_mutex);
594 Go through every y,z and get top faces in rows of x+
596 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
597 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
598 updateFastFaceRow(daynight_ratio, posRelative_f,
599 v3s16(0,y,z), MAP_BLOCKSIZE,
602 v3s16(0,1,0), //face dir
608 Go through every x,y and get right faces in rows of z+
610 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
611 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
612 updateFastFaceRow(daynight_ratio, posRelative_f,
613 v3s16(x,y,0), MAP_BLOCKSIZE,
622 Go through every y,z and get back faces in rows of x+
624 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
625 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
626 updateFastFaceRow(daynight_ratio, posRelative_f,
627 v3s16(0,y,z), MAP_BLOCKSIZE,
640 Convert FastFaces to SMesh
643 scene::SMesh *mesh_new = NULL;
645 mesh_new = new scene::SMesh();
647 if(fastfaces_new.size() > 0)
649 MeshCollector collector;
651 for(u32 i=0; i<fastfaces_new.size(); i++)
653 FastFace &f = fastfaces_new[i];
655 const u16 indices[] = {0,1,2,2,3,0};
657 if(f.tile.feature == TILEFEAT_NONE)
659 collector.append(tile_material_get(f.tile.id), f.vertices, 4,
662 else if(f.tile.feature == TILEFEAT_CRACK)
664 const char *path = tile_texture_path_get(f.tile.id);
666 u16 progression = f.tile.param.crack.progression;
668 std::string name = (std::string)path + "_cracked_"
669 + (char)('0' + progression);
671 TextureMod *mod = new CrackTextureMod(progression);
673 video::ITexture *texture = g_irrlicht->getTexture(
674 TextureSpec(name, path, mod));
676 video::SMaterial material = tile_material_get(f.tile.id);
677 material.setTexture(0, texture);
679 collector.append(material, f.vertices, 4, indices, 6);
688 collector.fillMesh(mesh_new);
690 // Use VBO for mesh (this just would set this for ever buffer)
691 // This will lead to infinite memory usage because or irrlicht.
692 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
694 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
695 <<"and uses "<<mesh_new->getMeshBufferCount()
696 <<" materials (meshbuffers)"<<std::endl;*/
700 Add special graphics:
703 TODO: Optimize by using same meshbuffer for same textures
706 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
707 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
708 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
712 MapNode &n = getNodeRef(x,y,z);
714 if(n.d == CONTENT_TORCH)
716 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
717 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
718 video::SColor c(255,255,255,255);
720 video::S3DVertex vertices[4] =
722 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
723 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
724 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
725 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
728 v3s16 dir = unpackDir(n.dir);
730 for(s32 i=0; i<4; i++)
732 if(dir == v3s16(1,0,0))
733 vertices[i].Pos.rotateXZBy(0);
734 if(dir == v3s16(-1,0,0))
735 vertices[i].Pos.rotateXZBy(180);
736 if(dir == v3s16(0,0,1))
737 vertices[i].Pos.rotateXZBy(90);
738 if(dir == v3s16(0,0,-1))
739 vertices[i].Pos.rotateXZBy(-90);
740 if(dir == v3s16(0,-1,0))
741 vertices[i].Pos.rotateXZBy(45);
742 if(dir == v3s16(0,1,0))
743 vertices[i].Pos.rotateXZBy(-45);
745 vertices[i].Pos += intToFloat(p + getPosRelative());
748 u16 indices[] = {0,1,2,2,3,0};
749 buf->append(vertices, 4, indices, 6);
752 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
753 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
754 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
755 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
756 buf->getMaterial().MaterialType
757 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
758 if(dir == v3s16(0,-1,0))
759 buf->getMaterial().setTexture(0,
760 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
761 else if(dir == v3s16(0,1,0))
762 buf->getMaterial().setTexture(0,
763 g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
764 // For backwards compatibility
765 else if(dir == v3s16(0,0,0))
766 buf->getMaterial().setTexture(0,
767 g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
769 buf->getMaterial().setTexture(0,
770 g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
773 mesh_new->addMeshBuffer(buf);
779 Do some stuff to the mesh
782 mesh_new->recalculateBoundingBox();
785 Delete new mesh if it is empty
788 if(mesh_new->getMeshBufferCount() == 0)
800 //scene::SMesh *mesh_old = mesh[daynight_i];
801 //mesh[daynight_i] = mesh_new;
803 scene::SMesh *mesh_old = mesh;
805 setMeshExpired(false);
809 // Remove hardware buffers of meshbuffers of mesh
810 // NOTE: No way, this runs in a different thread and everything
811 /*u32 c = mesh_old->getMeshBufferCount();
812 for(u32 i=0; i<c; i++)
814 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
817 /*dstream<<"mesh_old->getReferenceCount()="
818 <<mesh_old->getReferenceCount()<<std::endl;
819 u32 c = mesh_old->getMeshBufferCount();
820 for(u32 i=0; i<c; i++)
822 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
823 dstream<<"buf->getReferenceCount()="
824 <<buf->getReferenceCount()<<std::endl;
835 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
838 /*void MapBlock::updateMeshes(s32 first_i)
840 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
842 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
853 Propagates sunlight down through the block.
854 Doesn't modify nodes that are not affected by sunlight.
856 Returns false if sunlight at bottom block is invalid
857 Returns true if bottom block doesn't exist.
859 If there is a block above, continues from it.
860 If there is no block above, assumes there is sunlight, unless
861 is_underground is set.
863 At the moment, all sunlighted nodes are added to light_sources.
864 - SUGG: This could be optimized
866 Turns sunglighted mud into grass.
868 if remove_light==true, sets non-sunlighted nodes black.
870 if black_air_left!=NULL, it is set to true if non-sunlighted
871 air is left in block.
873 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
874 bool remove_light, bool *black_air_left)
876 // Whether the sunlight at the top of the bottom block is valid
877 bool block_below_is_valid = true;
879 v3s16 pos_relative = getPosRelative();
881 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
883 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
886 bool no_sunlight = false;
887 bool no_top_block = false;
888 // Check if node above block has sunlight
890 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
891 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
896 catch(InvalidPositionException &e)
900 // NOTE: This makes over-ground roofed places sunlighted
901 // Assume sunlight, unless is_underground==true
907 // NOTE: As of now, it just would make everything dark.
909 //no_sunlight = true;
912 #if 0 // Doesn't work; nothing gets light.
913 bool no_sunlight = true;
914 bool no_top_block = false;
915 // Check if node above block has sunlight
917 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
918 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
923 catch(InvalidPositionException &e)
929 /*std::cout<<"("<<x<<","<<z<<"): "
930 <<"no_top_block="<<no_top_block
931 <<", is_underground="<<is_underground
932 <<", no_sunlight="<<no_sunlight
935 s16 y = MAP_BLOCKSIZE-1;
937 // This makes difference to diminishing in water.
938 bool stopped_to_solid_object = false;
940 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
945 MapNode &n = getNodeRef(pos);
947 if(current_light == 0)
951 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
953 // Do nothing: Sunlight is continued
955 else if(n.light_propagates() == false)
957 // Turn mud into grass
958 if(n.d == CONTENT_MUD && current_light == LIGHT_SUN)
963 // A solid object is on the way.
964 stopped_to_solid_object = true;
972 current_light = diminish_light(current_light);
975 u8 old_light = n.getLight(LIGHTBANK_DAY);
977 if(current_light > old_light || remove_light)
979 n.setLight(LIGHTBANK_DAY, current_light);
982 if(diminish_light(current_light) != 0)
984 light_sources.insert(pos_relative + pos, true);
987 if(current_light == 0 && stopped_to_solid_object)
991 *black_air_left = true;
996 // Whether or not the block below should see LIGHT_SUN
997 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1000 If the block below hasn't already been marked invalid:
1002 Check if the node below the block has proper sunlight at top.
1003 If not, the block below is invalid.
1005 Ignore non-transparent nodes as they always have no light
1009 if(block_below_is_valid)
1011 MapNode n = getNodeParent(v3s16(x, -1, z));
1012 if(n.light_propagates())
1014 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1015 && sunlight_should_go_down == false)
1016 block_below_is_valid = false;
1017 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1018 && sunlight_should_go_down == true)
1019 block_below_is_valid = false;
1023 catch(InvalidPositionException &e)
1025 /*std::cout<<"InvalidBlockException for bottom block node"
1027 // Just no block below, no need to panic.
1032 return block_below_is_valid;
1035 void MapBlock::copyTo(VoxelManipulator &dst)
1037 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1038 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1040 dst.copyFrom(data, data_area, v3s16(0,0,0),
1041 getPosRelative(), data_size);
1044 /*void getPseudoObjects(v3f origin, f32 max_d,
1045 core::array<DistanceSortedObject> &dest)
1048 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1053 m_objects.step(dtime, server, daynight_ratio);
1056 Spawn some objects at random.
1058 Use dayNightDiffed() to approximate being near ground level
1060 if(m_spawn_timer < -999)
1064 if(dayNightDiffed() == true && getObjectCount() == 0)
1066 m_spawn_timer -= dtime;
1067 if(m_spawn_timer <= 0.0)
1069 m_spawn_timer += myrand() % 300;
1072 (myrand()%(MAP_BLOCKSIZE-1))+0,
1073 (myrand()%(MAP_BLOCKSIZE-1))+0
1076 s16 y = getGroundLevel(p2d);
1080 v3s16 p(p2d.X, y+1, p2d.Y);
1082 if(getNode(p).d == CONTENT_AIR
1083 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1085 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1096 void MapBlock::updateDayNightDiff()
1100 m_day_night_differs = false;
1104 bool differs = false;
1107 Check if any lighting value differs
1109 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1111 MapNode &n = data[i];
1112 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1120 If some lighting values differ, check if the whole thing is
1121 just air. If it is, differ = false
1125 bool only_air = true;
1126 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1128 MapNode &n = data[i];
1129 if(n.d != CONTENT_AIR)
1139 // Set member variable
1140 m_day_night_differs = differs;
1143 s16 MapBlock::getGroundLevel(v2s16 p2d)
1149 s16 y = MAP_BLOCKSIZE-1;
1152 if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1154 if(y == MAP_BLOCKSIZE-1)
1162 catch(InvalidPositionException &e)
1172 void MapBlock::serialize(std::ostream &os, u8 version)
1174 if(!ser_ver_supported(version))
1175 throw VersionMismatchException("ERROR: MapBlock format not supported");
1179 throw SerializationError("ERROR: Not writing dummy block.");
1182 // These have no compression
1183 if(version <= 3 || version == 5 || version == 6)
1185 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1187 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1188 SharedBuffer<u8> dest(buflen);
1190 dest[0] = is_underground;
1191 for(u32 i=0; i<nodecount; i++)
1193 u32 s = 1 + i * MapNode::serializedLength(version);
1194 data[i].serialize(&dest[s], version);
1197 os.write((char*)*dest, dest.getSize());
1199 else if(version <= 10)
1203 Compress the materials and the params separately.
1207 os.write((char*)&is_underground, 1);
1209 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1211 // Get and compress materials
1212 SharedBuffer<u8> materialdata(nodecount);
1213 for(u32 i=0; i<nodecount; i++)
1215 materialdata[i] = data[i].d;
1217 compress(materialdata, os, version);
1219 // Get and compress lights
1220 SharedBuffer<u8> lightdata(nodecount);
1221 for(u32 i=0; i<nodecount; i++)
1223 lightdata[i] = data[i].param;
1225 compress(lightdata, os, version);
1229 // Get and compress pressure
1230 SharedBuffer<u8> pressuredata(nodecount);
1231 for(u32 i=0; i<nodecount; i++)
1233 pressuredata[i] = data[i].pressure;
1235 compress(pressuredata, os, version);
1238 // All other versions (newest)
1245 if(m_day_night_differs)
1247 os.write((char*)&flags, 1);
1249 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1255 SharedBuffer<u8> databuf(nodecount*3);
1258 for(u32 i=0; i<nodecount; i++)
1260 databuf[i] = data[i].d;
1264 for(u32 i=0; i<nodecount; i++)
1266 databuf[i+nodecount] = data[i].param;
1270 for(u32 i=0; i<nodecount; i++)
1272 databuf[i+nodecount*2] = data[i].pressure;
1276 Compress data to output stream
1279 compress(databuf, os, version);
1283 void MapBlock::deSerialize(std::istream &is, u8 version)
1285 if(!ser_ver_supported(version))
1286 throw VersionMismatchException("ERROR: MapBlock format not supported");
1288 // These have no compression
1289 if(version <= 3 || version == 5 || version == 6)
1291 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1294 if(is.gcount() != 1)
1295 throw SerializationError
1296 ("MapBlock::deSerialize: no enough input data");
1297 is_underground = tmp;
1298 for(u32 i=0; i<nodecount; i++)
1300 s32 len = MapNode::serializedLength(version);
1301 SharedBuffer<u8> d(len);
1302 is.read((char*)*d, len);
1303 if(is.gcount() != len)
1304 throw SerializationError
1305 ("MapBlock::deSerialize: no enough input data");
1306 data[i].deSerialize(*d, version);
1309 else if(version <= 10)
1311 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1314 is.read((char*)&t8, 1);
1315 is_underground = t8;
1318 // Uncompress and set material data
1319 std::ostringstream os(std::ios_base::binary);
1320 decompress(is, os, version);
1321 std::string s = os.str();
1322 if(s.size() != nodecount)
1323 throw SerializationError
1324 ("MapBlock::deSerialize: invalid format");
1325 for(u32 i=0; i<s.size(); i++)
1331 // Uncompress and set param data
1332 std::ostringstream os(std::ios_base::binary);
1333 decompress(is, os, version);
1334 std::string s = os.str();
1335 if(s.size() != nodecount)
1336 throw SerializationError
1337 ("MapBlock::deSerialize: invalid format");
1338 for(u32 i=0; i<s.size(); i++)
1340 data[i].param = s[i];
1346 // Uncompress and set pressure data
1347 std::ostringstream os(std::ios_base::binary);
1348 decompress(is, os, version);
1349 std::string s = os.str();
1350 if(s.size() != nodecount)
1351 throw SerializationError
1352 ("MapBlock::deSerialize: invalid format");
1353 for(u32 i=0; i<s.size(); i++)
1355 data[i].pressure = s[i];
1359 // All other versions (newest)
1362 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1365 is.read((char*)&flags, 1);
1366 is_underground = (flags & 1) ? true : false;
1367 m_day_night_differs = (flags & 2) ? true : false;
1370 std::ostringstream os(std::ios_base::binary);
1371 decompress(is, os, version);
1372 std::string s = os.str();
1373 if(s.size() != nodecount*3)
1374 throw SerializationError
1375 ("MapBlock::deSerialize: invalid format");
1378 for(u32 i=0; i<nodecount; i++)
1383 for(u32 i=0; i<nodecount; i++)
1385 data[i].param = s[i+nodecount];
1388 for(u32 i=0; i<nodecount; i++)
1390 data[i].pressure = s[i+nodecount*2];