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,
297 NodeModMap &temp_mods)
300 spec = mn.getTile(face_dir);
303 Check temporary modifications on this node
305 /*core::map<v3s16, NodeMod>::Node *n;
306 n = m_temp_mods.find(p);
310 struct NodeMod mod = n->getValue();*/
312 if(temp_mods.get(p, &mod))
314 if(mod.type == NODEMOD_CHANGECONTENT)
316 MapNode mn2(mod.param);
317 spec = mn2.getTile(face_dir);
319 if(mod.type == NODEMOD_CRACK)
321 std::ostringstream os;
322 os<<"[crack"<<mod.param;
324 textureid_t tid = g_irrlicht->getTextureId(os.str());
325 spec.spec.addTid(tid);
332 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
335 Check temporary modifications on this node
337 /*core::map<v3s16, NodeMod>::Node *n;
338 n = m_temp_mods.find(p);
342 struct NodeMod mod = n->getValue();*/
344 if(temp_mods.get(p, &mod))
346 if(mod.type == NODEMOD_CHANGECONTENT)
351 if(mod.type == NODEMOD_CRACK)
354 Content doesn't change.
356 face_contents works just like it should, because
357 there should not be faces between differently cracked
360 If a semi-transparent node is cracked in front an
361 another one, it really doesn't matter whether there
362 is a cracked face drawn in between or not.
372 translate_dir: unit vector with only one of x, y or z
373 face_dir: unit vector with only one of x, y or z
375 void MapBlock::updateFastFaceRow(
384 core::array<FastFace> &dest,
385 NodeModMap &temp_mods)
389 u16 continuous_tiles_count = 0;
391 MapNode n0 = getNodeParentNoEx(p);
392 MapNode n1 = getNodeParentNoEx(p + face_dir);
394 u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
396 TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
397 TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
399 for(u16 j=0; j<length; j++)
401 bool next_is_different = true;
412 p_next = p + translate_dir;
413 n0_next = getNodeParentNoEx(p_next);
414 n1_next = getNodeParentNoEx(p_next + face_dir);
415 tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
416 tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
417 light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
419 if(tile0_next == tile0
420 && tile1_next == tile1
421 && light_next == light)
423 next_is_different = false;
427 continuous_tiles_count++;
429 if(next_is_different)
432 Create a face if there should be one
434 //u8 mf = face_contents(tile0, tile1);
436 u8 content0 = getNodeContent(p, n0, temp_mods);
437 u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
438 u8 mf = face_contents(content0, content1);
442 // Floating point conversion of the position vector
443 v3f pf(p.X, p.Y, p.Z);
444 // Center point of face (kind of)
445 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
447 if(translate_dir.X != 0){
448 scale.X = continuous_tiles_count;
450 if(translate_dir.Y != 0){
451 scale.Y = continuous_tiles_count;
453 if(translate_dir.Z != 0){
454 scale.Z = continuous_tiles_count;
459 // If node at sp (tile0) is more solid
462 makeFastFace(tile0, decode_light(light),
464 posRelative_f, dest);
466 // If node at sp is less solid (mf == 2)
469 makeFastFace(tile1, decode_light(light),
470 sp+face_dir_f, -face_dir, scale,
471 posRelative_f, dest);
476 continuous_tiles_count = 0;
489 This is used because CMeshBuffer::append() is very slow
493 video::SMaterial material;
494 core::array<u16> indices;
495 core::array<video::S3DVertex> vertices;
502 video::SMaterial material,
503 const video::S3DVertex* const vertices,
505 const u16* const indices,
509 PreMeshBuffer *p = NULL;
510 for(u32 i=0; i<m_prebuffers.size(); i++)
512 PreMeshBuffer &pp = m_prebuffers[i];
513 if(pp.material != material)
523 pp.material = material;
524 m_prebuffers.push_back(pp);
525 p = &m_prebuffers[m_prebuffers.size()-1];
528 u32 vertex_count = p->vertices.size();
529 for(u32 i=0; i<numIndices; i++)
531 u32 j = indices[i] + vertex_count;
534 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
535 // NOTE: Fix is to just add an another MeshBuffer
537 p->indices.push_back(j);
539 for(u32 i=0; i<numVertices; i++)
541 p->vertices.push_back(vertices[i]);
545 void fillMesh(scene::SMesh *mesh)
547 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
548 <<" meshbuffers"<<std::endl;*/
549 for(u32 i=0; i<m_prebuffers.size(); i++)
551 PreMeshBuffer &p = m_prebuffers[i];
553 /*dstream<<"p.vertices.size()="<<p.vertices.size()
554 <<", p.indices.size()="<<p.indices.size()
559 // This is a "Standard MeshBuffer",
560 // it's a typedeffed CMeshBuffer<video::S3DVertex>
561 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
563 buf->Material = p.material;
564 //((scene::SMeshBuffer*)buf)->Material = p.material;
566 //buf->setHardwareMappingHint(scene::EHM_STATIC);
568 mesh->addMeshBuffer(buf);
572 buf->append(p.vertices.pointer(), p.vertices.size(),
573 p.indices.pointer(), p.indices.size());
578 core::array<PreMeshBuffer> m_prebuffers;
581 void MapBlock::updateMesh(u32 daynight_ratio)
585 DEBUG: If mesh has been generated, don't generate it again
588 JMutexAutoLock meshlock(mesh_mutex);
594 // 4-21ms for MAP_BLOCKSIZE=16
595 // 24-155ms for MAP_BLOCKSIZE=32
596 //TimeTaker timer1("updateMesh()");
598 core::array<FastFace> fastfaces_new;
600 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
601 getPosRelative().Z); // floating point conversion
605 Avoid interlocks by copying m_temp_mods
607 NodeModMap temp_mods;
609 JMutexAutoLock lock(m_temp_mods_mutex);
610 m_temp_mods.copy(temp_mods);
614 We are including the faces of the trailing edges of the block.
615 This means that when something changes, the caller must
616 also update the meshes of the blocks at the leading edges.
618 NOTE: This is the slowest part of this method.
622 // 4-23ms for MAP_BLOCKSIZE=16
623 //TimeTaker timer2("updateMesh() collect");
626 Go through every y,z and get top faces in rows of x+
628 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
629 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
630 updateFastFaceRow(daynight_ratio, posRelative_f,
631 v3s16(0,y,z), MAP_BLOCKSIZE,
634 v3s16(0,1,0), //face dir
641 Go through every x,y and get right faces in rows of z+
643 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
644 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
645 updateFastFaceRow(daynight_ratio, posRelative_f,
646 v3s16(x,y,0), MAP_BLOCKSIZE,
656 Go through every y,z and get back faces in rows of x+
658 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
659 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
660 updateFastFaceRow(daynight_ratio, posRelative_f,
661 v3s16(0,y,z), MAP_BLOCKSIZE,
675 Convert FastFaces to SMesh
678 scene::SMesh *mesh_new = NULL;
680 mesh_new = new scene::SMesh();
682 MeshCollector collector;
684 if(fastfaces_new.size() > 0)
686 // avg 0ms (100ms spikes when loading textures the first time)
687 //TimeTaker timer2("updateMesh() mesh building");
689 video::SMaterial material;
690 material.Lighting = false;
691 material.BackfaceCulling = false;
692 material.setFlag(video::EMF_BILINEAR_FILTER, false);
693 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
694 material.setFlag(video::EMF_FOG_ENABLE, true);
696 for(u32 i=0; i<fastfaces_new.size(); i++)
698 FastFace &f = fastfaces_new[i];
700 const u16 indices[] = {0,1,2,2,3,0};
702 video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
706 material.setTexture(0, texture);
707 if(f.tile.alpha != 255)
708 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
710 material.MaterialType = video::EMT_SOLID;
712 collector.append(material, f.vertices, 4, indices, 6);
717 Add special graphics:
723 //TimeTaker timer2("updateMesh() adding special stuff");
725 // Flowing water material
726 video::SMaterial material_w1;
727 material_w1.setFlag(video::EMF_LIGHTING, false);
728 material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
729 material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
730 material_w1.setFlag(video::EMF_FOG_ENABLE, true);
731 material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
732 material_w1.setTexture(0,
733 g_irrlicht->getTexture("water.png"));
735 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
736 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
737 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
741 MapNode &n = getNodeRef(x,y,z);
746 if(n.d == CONTENT_TORCH)
748 video::SColor c(255,255,255,255);
750 video::S3DVertex vertices[4] =
752 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
753 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
754 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
755 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
758 v3s16 dir = unpackDir(n.dir);
760 for(s32 i=0; i<4; i++)
762 if(dir == v3s16(1,0,0))
763 vertices[i].Pos.rotateXZBy(0);
764 if(dir == v3s16(-1,0,0))
765 vertices[i].Pos.rotateXZBy(180);
766 if(dir == v3s16(0,0,1))
767 vertices[i].Pos.rotateXZBy(90);
768 if(dir == v3s16(0,0,-1))
769 vertices[i].Pos.rotateXZBy(-90);
770 if(dir == v3s16(0,-1,0))
771 vertices[i].Pos.rotateXZBy(45);
772 if(dir == v3s16(0,1,0))
773 vertices[i].Pos.rotateXZBy(-45);
775 vertices[i].Pos += intToFloat(p + getPosRelative());
779 video::SMaterial material;
780 material.setFlag(video::EMF_LIGHTING, false);
781 material.setFlag(video::EMF_BACK_FACE_CULLING, false);
782 material.setFlag(video::EMF_BILINEAR_FILTER, false);
783 //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
784 material.MaterialType
785 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
786 if(dir == v3s16(0,-1,0))
787 material.setTexture(0,
788 g_irrlicht->getTexture("torch_on_floor.png"));
789 else if(dir == v3s16(0,1,0))
790 material.setTexture(0,
791 g_irrlicht->getTexture("torch_on_ceiling.png"));
792 // For backwards compatibility
793 else if(dir == v3s16(0,0,0))
794 material.setTexture(0,
795 g_irrlicht->getTexture("torch_on_floor.png"));
797 material.setTexture(0,
798 g_irrlicht->getTexture("torch.png"));
800 u16 indices[] = {0,1,2,2,3,0};
801 // Add to mesh collector
802 collector.append(material, vertices, 4, indices, 6);
805 Add flowing water to mesh
807 else if(n.d == CONTENT_WATER)
809 bool top_is_water = false;
811 MapNode n = getNodeParent(v3s16(x,y+1,z));
812 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
814 }catch(InvalidPositionException &e){}
816 u8 l = decode_light(n.getLightBlend(daynight_ratio));
817 video::SColor c(WATER_ALPHA,l,l,l);
819 // Neighbor water levels (key = relative position)
820 // Includes current node
821 core::map<v3s16, f32> neighbor_levels;
822 core::map<v3s16, u8> neighbor_contents;
823 core::map<v3s16, u8> neighbor_flags;
824 const u8 neighborflag_top_is_water = 0x01;
825 v3s16 neighbor_dirs[9] = {
836 for(u32 i=0; i<9; i++)
838 u8 content = CONTENT_AIR;
839 float level = -0.5 * BS;
843 v3s16 p2 = p + neighbor_dirs[i];
844 MapNode n2 = getNodeParent(p2);
848 if(n2.d == CONTENT_WATERSOURCE)
850 else if(n2.d == CONTENT_WATER)
851 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
853 // Check node above neighbor.
854 // NOTE: This doesn't get executed if neighbor
857 n2 = getNodeParent(p2);
858 if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
859 flags |= neighborflag_top_is_water;
861 catch(InvalidPositionException &e){}
863 neighbor_levels.insert(neighbor_dirs[i], level);
864 neighbor_contents.insert(neighbor_dirs[i], content);
865 neighbor_flags.insert(neighbor_dirs[i], flags);
868 //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
869 //float water_level = neighbor_levels[v3s16(0,0,0)];
871 // Corner heights (average between four waters)
872 f32 corner_levels[4];
874 v3s16 halfdirs[4] = {
880 for(u32 i=0; i<4; i++)
882 v3s16 cornerdir = halfdirs[i];
883 float cornerlevel = 0;
885 for(u32 j=0; j<4; j++)
887 v3s16 neighbordir = cornerdir - halfdirs[j];
888 u8 content = neighbor_contents[neighbordir];
889 // Special case for source nodes
890 if(content == CONTENT_WATERSOURCE)
892 cornerlevel = 0.5*BS;
896 else if(content == CONTENT_WATER)
898 cornerlevel += neighbor_levels[neighbordir];
901 else if(content == CONTENT_AIR)
903 cornerlevel += -0.5*BS;
908 cornerlevel /= valid_count;
909 corner_levels[i] = cornerlevel;
916 v3s16 side_dirs[4] = {
922 s16 side_corners[4][2] = {
928 for(u32 i=0; i<4; i++)
930 v3s16 dir = side_dirs[i];
933 If our topside is water and neighbor's topside
934 is water, don't draw side face
937 neighbor_flags[dir] & neighborflag_top_is_water)
940 u8 neighbor_content = neighbor_contents[dir];
942 // Don't draw face if neighbor is not air or water
943 if(neighbor_content != CONTENT_AIR
944 && neighbor_content != CONTENT_WATER)
947 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
949 // Don't draw any faces if neighbor is water and top is water
950 if(neighbor_is_water == true && top_is_water == false)
953 video::S3DVertex vertices[4] =
955 /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
956 video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
957 video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
958 video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
959 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
960 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
961 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
962 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
966 If our topside is water, set upper border of face
967 at upper border of node
971 vertices[2].Pos.Y = 0.5*BS;
972 vertices[3].Pos.Y = 0.5*BS;
975 Otherwise upper position of face is corner levels
979 vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
980 vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
984 If neighbor is water, lower border of face is corner
987 if(neighbor_is_water)
989 vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
990 vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
993 If neighbor is not water, lower border of face is
998 vertices[0].Pos.Y = -0.5*BS;
999 vertices[1].Pos.Y = -0.5*BS;
1002 for(s32 j=0; j<4; j++)
1004 if(dir == v3s16(0,0,1))
1005 vertices[j].Pos.rotateXZBy(0);
1006 if(dir == v3s16(0,0,-1))
1007 vertices[j].Pos.rotateXZBy(180);
1008 if(dir == v3s16(-1,0,0))
1009 vertices[j].Pos.rotateXZBy(90);
1010 if(dir == v3s16(1,0,-0))
1011 vertices[j].Pos.rotateXZBy(-90);
1013 vertices[j].Pos += intToFloat(p + getPosRelative());
1016 u16 indices[] = {0,1,2,2,3,0};
1017 // Add to mesh collector
1018 collector.append(material_w1, vertices, 4, indices, 6);
1022 Generate top side, if appropriate
1025 if(top_is_water == false)
1027 video::S3DVertex vertices[4] =
1029 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1030 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1031 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1032 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
1035 for(s32 i=0; i<4; i++)
1037 //vertices[i].Pos.Y += water_level;
1038 //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1039 vertices[i].Pos.Y += corner_levels[i];
1040 vertices[i].Pos += intToFloat(p + getPosRelative());
1043 u16 indices[] = {0,1,2,2,3,0};
1044 // Add to mesh collector
1045 collector.append(material_w1, vertices, 4, indices, 6);
1051 Add stuff from collector to mesh
1054 collector.fillMesh(mesh_new);
1057 Do some stuff to the mesh
1060 mesh_new->recalculateBoundingBox();
1063 Delete new mesh if it is empty
1066 if(mesh_new->getMeshBufferCount() == 0)
1072 // Use VBO for mesh (this just would set this for ever buffer)
1073 // This will lead to infinite memory usage because or irrlicht.
1074 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1076 /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1077 <<"and uses "<<mesh_new->getMeshBufferCount()
1078 <<" materials (meshbuffers)"<<std::endl;*/
1086 //scene::SMesh *mesh_old = mesh[daynight_i];
1087 //mesh[daynight_i] = mesh_new;
1089 scene::SMesh *mesh_old = mesh;
1091 setMeshExpired(false);
1093 if(mesh_old != NULL)
1095 // Remove hardware buffers of meshbuffers of mesh
1096 // NOTE: No way, this runs in a different thread and everything
1097 /*u32 c = mesh_old->getMeshBufferCount();
1098 for(u32 i=0; i<c; i++)
1100 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1103 /*dstream<<"mesh_old->getReferenceCount()="
1104 <<mesh_old->getReferenceCount()<<std::endl;
1105 u32 c = mesh_old->getMeshBufferCount();
1106 for(u32 i=0; i<c; i++)
1108 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1109 dstream<<"buf->getReferenceCount()="
1110 <<buf->getReferenceCount()<<std::endl;
1119 mesh_mutex.Unlock();
1121 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1124 /*void MapBlock::updateMeshes(s32 first_i)
1126 assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1127 updateMesh(first_i);
1128 for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1139 Propagates sunlight down through the block.
1140 Doesn't modify nodes that are not affected by sunlight.
1142 Returns false if sunlight at bottom block is invalid
1143 Returns true if bottom block doesn't exist.
1145 If there is a block above, continues from it.
1146 If there is no block above, assumes there is sunlight, unless
1147 is_underground is set or highest node is water.
1149 At the moment, all sunlighted nodes are added to light_sources.
1150 - SUGG: This could be optimized
1152 Turns sunglighted mud into grass.
1154 if remove_light==true, sets non-sunlighted nodes black.
1156 if black_air_left!=NULL, it is set to true if non-sunlighted
1157 air is left in block.
1159 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1160 bool remove_light, bool *black_air_left,
1163 // Whether the sunlight at the top of the bottom block is valid
1164 bool block_below_is_valid = true;
1166 v3s16 pos_relative = getPosRelative();
1168 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1170 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1173 bool no_sunlight = false;
1174 bool no_top_block = false;
1175 // Check if node above block has sunlight
1177 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1178 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1183 catch(InvalidPositionException &e)
1185 no_top_block = true;
1187 // NOTE: This makes over-ground roofed places sunlighted
1188 // Assume sunlight, unless is_underground==true
1195 MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1196 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1201 // NOTE: As of now, this just would make everything dark.
1203 //no_sunlight = true;
1206 #if 0 // Doesn't work; nothing gets light.
1207 bool no_sunlight = true;
1208 bool no_top_block = false;
1209 // Check if node above block has sunlight
1211 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1212 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1214 no_sunlight = false;
1217 catch(InvalidPositionException &e)
1219 no_top_block = true;
1223 /*std::cout<<"("<<x<<","<<z<<"): "
1224 <<"no_top_block="<<no_top_block
1225 <<", is_underground="<<is_underground
1226 <<", no_sunlight="<<no_sunlight
1229 s16 y = MAP_BLOCKSIZE-1;
1231 // This makes difference to diminishing in water.
1232 bool stopped_to_solid_object = false;
1234 u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1239 MapNode &n = getNodeRef(pos);
1241 if(current_light == 0)
1245 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1247 // Do nothing: Sunlight is continued
1249 else if(n.light_propagates() == false)
1253 bool upper_is_air = false;
1256 if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1257 upper_is_air = true;
1259 catch(InvalidPositionException &e)
1262 // Turn mud into grass
1263 if(upper_is_air && n.d == CONTENT_MUD
1264 && current_light == LIGHT_SUN)
1266 n.d = CONTENT_GRASS;
1270 // A solid object is on the way.
1271 stopped_to_solid_object = true;
1279 current_light = diminish_light(current_light);
1282 u8 old_light = n.getLight(LIGHTBANK_DAY);
1284 if(current_light > old_light || remove_light)
1286 n.setLight(LIGHTBANK_DAY, current_light);
1289 if(diminish_light(current_light) != 0)
1291 light_sources.insert(pos_relative + pos, true);
1294 if(current_light == 0 && stopped_to_solid_object)
1298 *black_air_left = true;
1303 // Whether or not the block below should see LIGHT_SUN
1304 bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1307 If the block below hasn't already been marked invalid:
1309 Check if the node below the block has proper sunlight at top.
1310 If not, the block below is invalid.
1312 Ignore non-transparent nodes as they always have no light
1316 if(block_below_is_valid)
1318 MapNode n = getNodeParent(v3s16(x, -1, z));
1319 if(n.light_propagates())
1321 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1322 && sunlight_should_go_down == false)
1323 block_below_is_valid = false;
1324 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1325 && sunlight_should_go_down == true)
1326 block_below_is_valid = false;
1330 catch(InvalidPositionException &e)
1332 /*std::cout<<"InvalidBlockException for bottom block node"
1334 // Just no block below, no need to panic.
1339 return block_below_is_valid;
1342 void MapBlock::copyTo(VoxelManipulator &dst)
1344 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1345 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1347 // Copy from data to VoxelManipulator
1348 dst.copyFrom(data, data_area, v3s16(0,0,0),
1349 getPosRelative(), data_size);
1352 void MapBlock::copyFrom(VoxelManipulator &dst)
1354 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1355 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1357 // Copy from VoxelManipulator to data
1358 dst.copyTo(data, data_area, v3s16(0,0,0),
1359 getPosRelative(), data_size);
1362 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1367 m_objects.step(dtime, server, daynight_ratio);
1370 Spawn some objects at random.
1372 Use dayNightDiffed() to approximate being near ground level
1374 if(m_spawn_timer < -999)
1378 if(dayNightDiffed() == true && getObjectCount() == 0)
1380 m_spawn_timer -= dtime;
1381 if(m_spawn_timer <= 0.0)
1383 m_spawn_timer += myrand() % 300;
1386 (myrand()%(MAP_BLOCKSIZE-1))+0,
1387 (myrand()%(MAP_BLOCKSIZE-1))+0
1390 s16 y = getGroundLevel(p2d);
1394 v3s16 p(p2d.X, y+1, p2d.Y);
1396 if(getNode(p).d == CONTENT_AIR
1397 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1399 RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1410 void MapBlock::updateDayNightDiff()
1414 m_day_night_differs = false;
1418 bool differs = false;
1421 Check if any lighting value differs
1423 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1425 MapNode &n = data[i];
1426 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1434 If some lighting values differ, check if the whole thing is
1435 just air. If it is, differ = false
1439 bool only_air = true;
1440 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1442 MapNode &n = data[i];
1443 if(n.d != CONTENT_AIR)
1453 // Set member variable
1454 m_day_night_differs = differs;
1457 s16 MapBlock::getGroundLevel(v2s16 p2d)
1463 s16 y = MAP_BLOCKSIZE-1;
1466 //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1467 if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1469 if(y == MAP_BLOCKSIZE-1)
1477 catch(InvalidPositionException &e)
1487 void MapBlock::serialize(std::ostream &os, u8 version)
1489 if(!ser_ver_supported(version))
1490 throw VersionMismatchException("ERROR: MapBlock format not supported");
1494 throw SerializationError("ERROR: Not writing dummy block.");
1497 // These have no compression
1498 if(version <= 3 || version == 5 || version == 6)
1500 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1502 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1503 SharedBuffer<u8> dest(buflen);
1505 dest[0] = is_underground;
1506 for(u32 i=0; i<nodecount; i++)
1508 u32 s = 1 + i * MapNode::serializedLength(version);
1509 data[i].serialize(&dest[s], version);
1512 os.write((char*)*dest, dest.getSize());
1514 else if(version <= 10)
1518 Compress the materials and the params separately.
1522 os.write((char*)&is_underground, 1);
1524 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1526 // Get and compress materials
1527 SharedBuffer<u8> materialdata(nodecount);
1528 for(u32 i=0; i<nodecount; i++)
1530 materialdata[i] = data[i].d;
1532 compress(materialdata, os, version);
1534 // Get and compress lights
1535 SharedBuffer<u8> lightdata(nodecount);
1536 for(u32 i=0; i<nodecount; i++)
1538 lightdata[i] = data[i].param;
1540 compress(lightdata, os, version);
1544 // Get and compress param2
1545 SharedBuffer<u8> param2data(nodecount);
1546 for(u32 i=0; i<nodecount; i++)
1548 param2data[i] = data[i].param2;
1550 compress(param2data, os, version);
1553 // All other versions (newest)
1560 if(m_day_night_differs)
1562 if(m_lighting_expired)
1564 os.write((char*)&flags, 1);
1566 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1572 SharedBuffer<u8> databuf(nodecount*3);
1575 for(u32 i=0; i<nodecount; i++)
1577 databuf[i] = data[i].d;
1581 for(u32 i=0; i<nodecount; i++)
1583 databuf[i+nodecount] = data[i].param;
1587 for(u32 i=0; i<nodecount; i++)
1589 databuf[i+nodecount*2] = data[i].param2;
1593 Compress data to output stream
1596 compress(databuf, os, version);
1600 void MapBlock::deSerialize(std::istream &is, u8 version)
1602 if(!ser_ver_supported(version))
1603 throw VersionMismatchException("ERROR: MapBlock format not supported");
1605 // These have no compression
1606 if(version <= 3 || version == 5 || version == 6)
1608 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1611 if(is.gcount() != 1)
1612 throw SerializationError
1613 ("MapBlock::deSerialize: no enough input data");
1614 is_underground = tmp;
1615 for(u32 i=0; i<nodecount; i++)
1617 s32 len = MapNode::serializedLength(version);
1618 SharedBuffer<u8> d(len);
1619 is.read((char*)*d, len);
1620 if(is.gcount() != len)
1621 throw SerializationError
1622 ("MapBlock::deSerialize: no enough input data");
1623 data[i].deSerialize(*d, version);
1626 else if(version <= 10)
1628 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1631 is.read((char*)&t8, 1);
1632 is_underground = t8;
1635 // Uncompress and set material data
1636 std::ostringstream os(std::ios_base::binary);
1637 decompress(is, os, version);
1638 std::string s = os.str();
1639 if(s.size() != nodecount)
1640 throw SerializationError
1641 ("MapBlock::deSerialize: invalid format");
1642 for(u32 i=0; i<s.size(); i++)
1648 // Uncompress and set param data
1649 std::ostringstream os(std::ios_base::binary);
1650 decompress(is, os, version);
1651 std::string s = os.str();
1652 if(s.size() != nodecount)
1653 throw SerializationError
1654 ("MapBlock::deSerialize: invalid format");
1655 for(u32 i=0; i<s.size(); i++)
1657 data[i].param = s[i];
1663 // Uncompress and set param2 data
1664 std::ostringstream os(std::ios_base::binary);
1665 decompress(is, os, version);
1666 std::string s = os.str();
1667 if(s.size() != nodecount)
1668 throw SerializationError
1669 ("MapBlock::deSerialize: invalid format");
1670 for(u32 i=0; i<s.size(); i++)
1672 data[i].param2 = s[i];
1676 // All other versions (newest)
1679 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1682 is.read((char*)&flags, 1);
1683 is_underground = (flags & 1) ? true : false;
1684 m_day_night_differs = (flags & 2) ? true : false;
1685 m_lighting_expired = (flags & 3) ? true : false;
1688 std::ostringstream os(std::ios_base::binary);
1689 decompress(is, os, version);
1690 std::string s = os.str();
1691 if(s.size() != nodecount*3)
1692 throw SerializationError
1693 ("MapBlock::deSerialize: invalid format");
1696 for(u32 i=0; i<nodecount; i++)
1701 for(u32 i=0; i<nodecount; i++)
1703 data[i].param = s[i+nodecount];
1706 for(u32 i=0; i<nodecount; i++)
1708 data[i].param2 = s[i+nodecount*2];
1713 Translate nodes as specified in the translate_to fields of
1716 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1718 MapNode &n = data[i];
1720 MapNode *translate_to = content_features(n.d).translate_to;
1723 dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
1724 <<translate_to->d<<std::endl;