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 bool MapBlock::isValidPositionParent(v3s16 p)
34 if(isValidPosition(p))
39 return m_parent->isValidPosition(getPosRelative() + p);
43 MapNode MapBlock::getNodeParent(v3s16 p)
45 if(isValidPosition(p) == false)
47 return m_parent->getNode(getPosRelative() + p);
52 throw InvalidPositionException();
53 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
57 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
59 if(isValidPosition(p) == false)
61 m_parent->setNode(getPosRelative() + p, n);
66 throw InvalidPositionException();
67 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
71 FastFace * MapBlock::makeFastFace(u8 material, u8 light, v3f p,
72 v3f dir, v3f scale, v3f posRelative_f)
74 FastFace *f = new FastFace;
76 // Position is at the center of the cube.
81 // If looking towards z+, this is the face that is behind
82 // the center point, facing towards z+.
83 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
84 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
85 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
86 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
89 TODO: Rotate it the right way (one side comes upside down)
91 core::CMatrix4<f32> m;
92 m.buildRotateFromTo(v3f(0,0,1), dir);
94 for(u16 i=0; i<4; i++){
95 m.rotateVect(vertex_pos[i]);
96 vertex_pos[i].X *= scale.X;
97 vertex_pos[i].Y *= scale.Y;
98 vertex_pos[i].Z *= scale.Z;
99 vertex_pos[i] += pos + posRelative_f;
103 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
104 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
105 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
107 v3f zerovector = v3f(0,0,0);
109 u8 li = decode_light(light);
114 if(material == CONTENT_WATER || material == CONTENT_OCEAN)
119 video::SColor c = video::SColor(alpha,li,li,li);
121 /*f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
122 core::vector2d<f32>(0,1));
123 f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
124 core::vector2d<f32>(abs_scale,1));
125 f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
126 core::vector2d<f32>(abs_scale,0));
127 f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
128 core::vector2d<f32>(0,0));*/
129 f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
130 core::vector2d<f32>(0,1));
131 f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
132 core::vector2d<f32>(abs_scale,1));
133 f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
134 core::vector2d<f32>(abs_scale,0));
135 f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
136 core::vector2d<f32>(0,0));
138 f->material = material;
144 Parameters must consist of air and !air.
145 Order doesn't matter.
147 If either of the nodes doesn't exist, light is 0.
149 u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir)
152 MapNode n = getNodeParent(p);
153 MapNode n2 = getNodeParent(p + face_dir);
155 /*if(n.solidness() < n2.solidness())
156 light = n.getLight();
158 light = n2.getLight();*/
159 if(n.getLight() > n2.getLight())
160 light = n.getLight();
162 light = n2.getLight();
164 // Make some nice difference to different sides
165 if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
166 light = diminish_light(diminish_light(light));
167 else if(face_dir.X == -1 || face_dir.Z == -1)
168 light = diminish_light(light);
172 catch(InvalidPositionException &e)
179 Gets node material from any place relative to block.
180 Returns CONTENT_IGNORE if doesn't exist or should not be drawn.
182 u8 MapBlock::getNodeTile(v3s16 p)
185 MapNode n = getNodeParent(p);
187 return content_tile(n.d);
189 catch(InvalidPositionException &e)
191 return CONTENT_IGNORE;
197 translate_dir: unit vector with only one of x, y or z
198 face_dir: unit vector with only one of x, y or z
200 void MapBlock::updateFastFaceRow(v3s16 startpos,
204 core::list<FastFace*> &dest)
207 Precalculate some variables
209 v3f translate_dir_f(translate_dir.X, translate_dir.Y,
210 translate_dir.Z); // floating point conversion
211 v3f face_dir_f(face_dir.X, face_dir.Y,
212 face_dir.Z); // floating point conversion
213 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
214 getPosRelative().Z); // floating point conversion
218 The light in the air lights the surface is taken from
219 the node that is air.
221 u8 light = getFaceLight(p, face_dir);
223 u16 continuous_tiles_count = 0;
225 u8 tile0 = getNodeTile(p);
226 u8 tile1 = getNodeTile(p + face_dir);
228 for(u16 j=0; j<length; j++)
230 bool next_is_different = true;
238 p_next = p + translate_dir;
239 tile0_next = getNodeTile(p_next);
240 tile1_next = getNodeTile(p_next + face_dir);
241 light_next = getFaceLight(p_next, face_dir);
243 if(tile0_next == tile0
244 && tile1_next == tile1
245 && light_next == light)
247 next_is_different = false;
251 continuous_tiles_count++;
253 if(next_is_different)
256 Create a face if there should be one
258 u8 mf = face_contents(tile0, tile1);
262 // Floating point conversion of the position vector
263 v3f pf(p.X, p.Y, p.Z);
264 // Center point of face (kind of)
265 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
267 if(translate_dir.X != 0){
268 scale.X = continuous_tiles_count;
270 if(translate_dir.Y != 0){
271 scale.Y = continuous_tiles_count;
273 if(translate_dir.Z != 0){
274 scale.Z = continuous_tiles_count;
279 // If node at sp (tile0) is more solid
282 f = makeFastFace(tile0, light,
283 sp, face_dir_f, scale,
286 // If node at sp is less solid (mf == 2)
289 f = makeFastFace(tile1, light,
290 sp+face_dir_f, -1*face_dir_f, scale,
296 continuous_tiles_count = 0;
307 This is used because CMeshBuffer::append() is very slow
311 video::SMaterial material;
312 core::array<u16> indices;
313 core::array<video::S3DVertex> vertices;
320 video::SMaterial material,
321 const video::S3DVertex* const vertices,
323 const u16* const indices,
327 PreMeshBuffer *p = NULL;
328 for(u32 i=0; i<m_prebuffers.size(); i++)
330 PreMeshBuffer &pp = m_prebuffers[i];
331 if(pp.material != material)
341 pp.material = material;
342 m_prebuffers.push_back(pp);
343 p = &m_prebuffers[m_prebuffers.size()-1];
346 u32 vertex_count = p->vertices.size();
347 for(u32 i=0; i<numIndices; i++)
349 u32 j = indices[i] + vertex_count;
352 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
353 // NOTE: Fix is to just add an another MeshBuffer
355 p->indices.push_back(j);
357 for(u32 i=0; i<numVertices; i++)
359 p->vertices.push_back(vertices[i]);
363 void fillMesh(scene::SMesh *mesh)
365 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
366 <<" meshbuffers"<<std::endl;*/
367 for(u32 i=0; i<m_prebuffers.size(); i++)
369 PreMeshBuffer &p = m_prebuffers[i];
371 /*dstream<<"p.vertices.size()="<<p.vertices.size()
372 <<", p.indices.size()="<<p.indices.size()
377 // This is a "Standard MeshBuffer",
378 // it's a typedeffed CMeshBuffer<video::S3DVertex>
379 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
381 buf->Material = p.material;
382 //((scene::SMeshBuffer*)buf)->Material = p.material;
384 //buf->setHardwareMappingHint(scene::EHM_STATIC);
386 mesh->addMeshBuffer(buf);
390 buf->append(p.vertices.pointer(), p.vertices.size(),
391 p.indices.pointer(), p.indices.size());
396 core::array<PreMeshBuffer> m_prebuffers;
399 void MapBlock::updateMesh()
401 /*v3s16 p = getPosRelative();
402 std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
403 <<"::updateMesh(): ";*/
404 //<<"::updateMesh()"<<std::endl;
407 TODO: Change this to directly generate the mesh (and get rid
411 core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
414 We are including the faces of the trailing edges of the block.
415 This means that when something changes, the caller must
416 also update the meshes of the blocks at the leading edges.
420 Go through every y,z and get top faces in rows of x+
422 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
423 //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
424 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
425 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
432 Go through every x,y and get right faces in rows of z+
434 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
435 //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
436 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
437 updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
444 Go through every y,z and get back faces in rows of x+
446 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
447 //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
448 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
449 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
456 scene::SMesh *mesh_new = NULL;
458 mesh_new = new scene::SMesh();
460 if(fastfaces_new->getSize() > 0)
462 MeshCollector collector;
464 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
466 for(; i != fastfaces_new->end(); i++)
470 const u16 indices[] = {0,1,2,2,3,0};
472 collector.append(g_materials[f->material], f->vertices, 4,
476 collector.fillMesh(mesh_new);
478 // Use VBO for mesh (this just would set this for ever buffer)
479 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
481 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
482 <<"and uses "<<mesh_new->getMeshBufferCount()
483 <<" materials (meshbuffers)"<<std::endl;*/
487 Clear temporary FastFaces
490 core::list<FastFace*>::Iterator i;
491 i = fastfaces_new->begin();
492 for(; i != fastfaces_new->end(); i++)
496 fastfaces_new->clear();
497 delete fastfaces_new;
500 Add special graphics:
503 TODO: Optimize by using same meshbuffer for same textures
506 /*scene::ISceneManager *smgr = NULL;
507 video::IVideoDriver* driver = NULL;
510 smgr = g_device->getSceneManager();
511 driver = smgr->getVideoDriver();
514 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
515 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
516 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
520 MapNode &n = getNodeRef(x,y,z);
522 if(n.d == CONTENT_LIGHT)
524 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
525 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
526 video::SColor c(255,255,255,255);
528 video::S3DVertex vertices[4] =
530 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
531 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
532 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
533 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
536 v3s16 dir = unpackDir(n.dir);
538 for(s32 i=0; i<4; i++)
540 if(dir == v3s16(1,0,0))
541 vertices[i].Pos.rotateXZBy(0);
542 if(dir == v3s16(-1,0,0))
543 vertices[i].Pos.rotateXZBy(180);
544 if(dir == v3s16(0,0,1))
545 vertices[i].Pos.rotateXZBy(90);
546 if(dir == v3s16(0,0,-1))
547 vertices[i].Pos.rotateXZBy(-90);
548 if(dir == v3s16(0,-1,0))
549 vertices[i].Pos.rotateXZBy(45);
550 if(dir == v3s16(0,1,0))
551 vertices[i].Pos.rotateXZBy(-45);
553 vertices[i].Pos += intToFloat(p + getPosRelative());
556 u16 indices[] = {0,1,2,2,3,0};
557 buf->append(vertices, 4, indices, 6);
560 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
561 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
562 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
563 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
564 buf->getMaterial().MaterialType
565 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
566 if(dir == v3s16(0,-1,0))
567 buf->getMaterial().setTexture(0,
568 g_texturecache.get("torch_on_floor"));
569 else if(dir == v3s16(0,1,0))
570 buf->getMaterial().setTexture(0,
571 g_texturecache.get("torch_on_ceiling"));
572 // For backwards compatibility
573 else if(dir == v3s16(0,0,0))
574 buf->getMaterial().setTexture(0,
575 g_texturecache.get("torch_on_floor"));
577 buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
580 mesh_new->addMeshBuffer(buf);
586 Do some stuff to the mesh
589 mesh_new->recalculateBoundingBox();
592 Delete new mesh if it is empty
595 if(mesh_new->getMeshBufferCount() == 0)
607 scene::SMesh *mesh_old = mesh;
613 // Remove hardware buffers of meshbuffers of mesh
614 // NOTE: No way, this runs in a different thread and everything
615 /*u32 c = mesh_old->getMeshBufferCount();
616 for(u32 i=0; i<c; i++)
618 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
627 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
631 Propagates sunlight down through the block.
632 Doesn't modify nodes that are not affected by sunlight.
634 Returns false if sunlight at bottom block is invalid
635 Returns true if bottom block doesn't exist.
637 If there is a block above, continues from it.
638 If there is no block above, assumes there is sunlight, unless
639 is_underground is set.
641 At the moment, all sunlighted nodes are added to light_sources.
642 TODO: This could be optimized.
644 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
646 // Whether the sunlight at the top of the bottom block is valid
647 bool block_below_is_valid = true;
649 v3s16 pos_relative = getPosRelative();
651 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
653 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
655 bool no_sunlight = false;
656 bool no_top_block = false;
657 // Check if node above block has sunlight
659 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
660 if(n.getLight() != LIGHT_SUN)
669 catch(InvalidPositionException &e)
673 // TODO: This makes over-ground roofed places sunlighted
674 // Assume sunlight, unless is_underground==true
680 // TODO: There has to be some way to allow this behaviour
681 // As of now, it just makes everything dark.
683 //no_sunlight = true;
686 /*std::cout<<"("<<x<<","<<z<<"): "
687 <<"no_top_block="<<no_top_block
688 <<", is_underground="<<is_underground
689 <<", no_sunlight="<<no_sunlight
692 s16 y = MAP_BLOCKSIZE-1;
694 if(no_sunlight == false)
696 // Continue spreading sunlight downwards through transparent
702 MapNode &n = getNodeRef(pos);
704 if(n.sunlight_propagates())
706 n.setLight(LIGHT_SUN);
708 light_sources.insert(pos_relative + pos, true);
716 bool sunlight_should_go_down = (y==-1);
718 // Fill rest with black (only transparent ones)
722 MapNode &n = getNodeRef(pos);
724 if(n.light_propagates())
734 If the block below hasn't already been marked invalid:
736 Check if the node below the block has proper sunlight at top.
737 If not, the block below is invalid.
739 Ignore non-transparent nodes as they always have no light
743 if(block_below_is_valid)
745 MapNode n = getNodeParent(v3s16(x, -1, z));
746 if(n.light_propagates())
748 if(n.getLight() == LIGHT_SUN
749 && sunlight_should_go_down == false)
750 block_below_is_valid = false;
751 else if(n.getLight() != LIGHT_SUN
752 && sunlight_should_go_down == true)
753 block_below_is_valid = false;
757 catch(InvalidPositionException &e)
759 /*std::cout<<"InvalidBlockException for bottom block node"
761 // Just no block below, no need to panic.
766 return block_below_is_valid;
769 void MapBlock::copyTo(VoxelManipulator &dst)
771 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
772 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
774 dst.copyFrom(data, data_area, v3s16(0,0,0),
775 getPosRelative(), data_size);
778 /*void getPseudoObjects(v3f origin, f32 max_d,
779 core::array<DistanceSortedObject> &dest)
787 void MapBlock::serialize(std::ostream &os, u8 version)
789 if(!ser_ver_supported(version))
790 throw VersionMismatchException("ERROR: MapBlock format not supported");
794 throw SerializationError("ERROR: Not writing dummy block.");
797 // These have no compression
798 if(version <= 3 || version == 5 || version == 6)
800 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
802 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
803 SharedBuffer<u8> dest(buflen);
805 dest[0] = is_underground;
806 for(u32 i=0; i<nodecount; i++)
808 u32 s = 1 + i * MapNode::serializedLength(version);
809 data[i].serialize(&dest[s], version);
812 os.write((char*)*dest, dest.getSize());
819 Compress the materials and the params separately.
823 os.write((char*)&is_underground, 1);
825 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
827 // Get and compress materials
828 SharedBuffer<u8> materialdata(nodecount);
829 for(u32 i=0; i<nodecount; i++)
831 materialdata[i] = data[i].d;
833 compress(materialdata, os, version);
835 // Get and compress params
836 SharedBuffer<u8> paramdata(nodecount);
837 for(u32 i=0; i<nodecount; i++)
839 paramdata[i] = data[i].param;
841 compress(paramdata, os, version);
845 // Get and compress pressure
846 SharedBuffer<u8> pressuredata(nodecount);
847 for(u32 i=0; i<nodecount; i++)
849 pressuredata[i] = data[i].pressure;
851 compress(pressuredata, os, version);
856 void MapBlock::deSerialize(std::istream &is, u8 version)
858 if(!ser_ver_supported(version))
859 throw VersionMismatchException("ERROR: MapBlock format not supported");
861 // These have no compression
862 if(version <= 3 || version == 5 || version == 6)
864 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
868 throw SerializationError
869 ("MapBlock::deSerialize: no enough input data");
870 is_underground = tmp;
871 for(u32 i=0; i<nodecount; i++)
873 s32 len = MapNode::serializedLength(version);
874 SharedBuffer<u8> d(len);
875 is.read((char*)*d, len);
876 if(is.gcount() != len)
877 throw SerializationError
878 ("MapBlock::deSerialize: no enough input data");
879 data[i].deSerialize(*d, version);
882 // All other versions
885 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
888 is.read((char*)&t8, 1);
892 // Uncompress and set material data
893 std::ostringstream os(std::ios_base::binary);
894 decompress(is, os, version);
895 std::string s = os.str();
896 if(s.size() != nodecount)
897 throw SerializationError
898 ("MapBlock::deSerialize: invalid format");
899 for(u32 i=0; i<s.size(); i++)
905 // Uncompress and set param data
906 std::ostringstream os(std::ios_base::binary);
907 decompress(is, os, version);
908 std::string s = os.str();
909 if(s.size() != nodecount)
910 throw SerializationError
911 ("MapBlock::deSerialize: invalid format");
912 for(u32 i=0; i<s.size(); i++)
914 data[i].param = s[i];
920 // Uncompress and set pressure data
921 std::ostringstream os(std::ios_base::binary);
922 decompress(is, os, version);
923 std::string s = os.str();
924 if(s.size() != nodecount)
925 throw SerializationError
926 ("MapBlock::deSerialize: invalid format");
927 for(u32 i=0; i<s.size(); i++)
929 data[i].pressure = s[i];