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
166 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
167 light = diminish_light(diminish_light(light));
168 else if(face_dir.X == -1 || face_dir.Z == -1)
169 light = diminish_light(light);*/
171 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
172 light = diminish_light(diminish_light(light));
173 else if(face_dir.Z == 1 || face_dir.Z == -1)
174 light = diminish_light(light);
178 catch(InvalidPositionException &e)
185 Gets node material from any place relative to block.
186 Returns CONTENT_IGNORE if doesn't exist or should not be drawn.
188 u8 MapBlock::getNodeTile(v3s16 p)
191 MapNode n = getNodeParent(p);
193 return content_tile(n.d);
195 catch(InvalidPositionException &e)
197 return CONTENT_IGNORE;
203 translate_dir: unit vector with only one of x, y or z
204 face_dir: unit vector with only one of x, y or z
206 void MapBlock::updateFastFaceRow(v3s16 startpos,
210 core::list<FastFace*> &dest)
213 Precalculate some variables
215 v3f translate_dir_f(translate_dir.X, translate_dir.Y,
216 translate_dir.Z); // floating point conversion
217 v3f face_dir_f(face_dir.X, face_dir.Y,
218 face_dir.Z); // floating point conversion
219 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
220 getPosRelative().Z); // floating point conversion
224 The light in the air lights the surface is taken from
225 the node that is air.
227 u8 light = getFaceLight(p, face_dir);
229 u16 continuous_tiles_count = 0;
231 u8 tile0 = getNodeTile(p);
232 u8 tile1 = getNodeTile(p + face_dir);
234 for(u16 j=0; j<length; j++)
236 bool next_is_different = true;
244 p_next = p + translate_dir;
245 tile0_next = getNodeTile(p_next);
246 tile1_next = getNodeTile(p_next + face_dir);
247 light_next = getFaceLight(p_next, face_dir);
249 if(tile0_next == tile0
250 && tile1_next == tile1
251 && light_next == light)
253 next_is_different = false;
257 continuous_tiles_count++;
259 if(next_is_different)
262 Create a face if there should be one
264 u8 mf = face_contents(tile0, tile1);
268 // Floating point conversion of the position vector
269 v3f pf(p.X, p.Y, p.Z);
270 // Center point of face (kind of)
271 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
273 if(translate_dir.X != 0){
274 scale.X = continuous_tiles_count;
276 if(translate_dir.Y != 0){
277 scale.Y = continuous_tiles_count;
279 if(translate_dir.Z != 0){
280 scale.Z = continuous_tiles_count;
285 // If node at sp (tile0) is more solid
288 f = makeFastFace(tile0, light,
289 sp, face_dir_f, scale,
292 // If node at sp is less solid (mf == 2)
295 f = makeFastFace(tile1, light,
296 sp+face_dir_f, -1*face_dir_f, scale,
302 continuous_tiles_count = 0;
313 This is used because CMeshBuffer::append() is very slow
317 video::SMaterial material;
318 core::array<u16> indices;
319 core::array<video::S3DVertex> vertices;
326 video::SMaterial material,
327 const video::S3DVertex* const vertices,
329 const u16* const indices,
333 PreMeshBuffer *p = NULL;
334 for(u32 i=0; i<m_prebuffers.size(); i++)
336 PreMeshBuffer &pp = m_prebuffers[i];
337 if(pp.material != material)
347 pp.material = material;
348 m_prebuffers.push_back(pp);
349 p = &m_prebuffers[m_prebuffers.size()-1];
352 u32 vertex_count = p->vertices.size();
353 for(u32 i=0; i<numIndices; i++)
355 u32 j = indices[i] + vertex_count;
358 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
359 // NOTE: Fix is to just add an another MeshBuffer
361 p->indices.push_back(j);
363 for(u32 i=0; i<numVertices; i++)
365 p->vertices.push_back(vertices[i]);
369 void fillMesh(scene::SMesh *mesh)
371 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
372 <<" meshbuffers"<<std::endl;*/
373 for(u32 i=0; i<m_prebuffers.size(); i++)
375 PreMeshBuffer &p = m_prebuffers[i];
377 /*dstream<<"p.vertices.size()="<<p.vertices.size()
378 <<", p.indices.size()="<<p.indices.size()
383 // This is a "Standard MeshBuffer",
384 // it's a typedeffed CMeshBuffer<video::S3DVertex>
385 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
387 buf->Material = p.material;
388 //((scene::SMeshBuffer*)buf)->Material = p.material;
390 //buf->setHardwareMappingHint(scene::EHM_STATIC);
392 mesh->addMeshBuffer(buf);
396 buf->append(p.vertices.pointer(), p.vertices.size(),
397 p.indices.pointer(), p.indices.size());
402 core::array<PreMeshBuffer> m_prebuffers;
405 void MapBlock::updateMesh()
407 /*v3s16 p = getPosRelative();
408 std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
409 <<"::updateMesh(): ";*/
410 //<<"::updateMesh()"<<std::endl;
413 TODO: Change this to directly generate the mesh (and get rid
417 core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
420 We are including the faces of the trailing edges of the block.
421 This means that when something changes, the caller must
422 also update the meshes of the blocks at the leading edges.
426 Go through every y,z and get top faces in rows of x+
428 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
429 //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
430 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
431 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
438 Go through every x,y and get right faces in rows of z+
440 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
441 //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
442 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
443 updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
450 Go through every y,z and get back faces in rows of x+
452 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
453 //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
454 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
455 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
462 scene::SMesh *mesh_new = NULL;
464 mesh_new = new scene::SMesh();
466 if(fastfaces_new->getSize() > 0)
468 MeshCollector collector;
470 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
472 for(; i != fastfaces_new->end(); i++)
476 const u16 indices[] = {0,1,2,2,3,0};
478 collector.append(g_materials[f->material], f->vertices, 4,
482 collector.fillMesh(mesh_new);
484 // Use VBO for mesh (this just would set this for ever buffer)
485 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
487 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
488 <<"and uses "<<mesh_new->getMeshBufferCount()
489 <<" materials (meshbuffers)"<<std::endl;*/
493 Clear temporary FastFaces
496 core::list<FastFace*>::Iterator i;
497 i = fastfaces_new->begin();
498 for(; i != fastfaces_new->end(); i++)
502 fastfaces_new->clear();
503 delete fastfaces_new;
506 Add special graphics:
509 TODO: Optimize by using same meshbuffer for same textures
512 /*scene::ISceneManager *smgr = NULL;
513 video::IVideoDriver* driver = NULL;
516 smgr = g_device->getSceneManager();
517 driver = smgr->getVideoDriver();
520 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
521 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
522 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
526 MapNode &n = getNodeRef(x,y,z);
528 if(n.d == CONTENT_LIGHT)
530 //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
531 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
532 video::SColor c(255,255,255,255);
534 video::S3DVertex vertices[4] =
536 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
537 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
538 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
539 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
542 v3s16 dir = unpackDir(n.dir);
544 for(s32 i=0; i<4; i++)
546 if(dir == v3s16(1,0,0))
547 vertices[i].Pos.rotateXZBy(0);
548 if(dir == v3s16(-1,0,0))
549 vertices[i].Pos.rotateXZBy(180);
550 if(dir == v3s16(0,0,1))
551 vertices[i].Pos.rotateXZBy(90);
552 if(dir == v3s16(0,0,-1))
553 vertices[i].Pos.rotateXZBy(-90);
554 if(dir == v3s16(0,-1,0))
555 vertices[i].Pos.rotateXZBy(45);
556 if(dir == v3s16(0,1,0))
557 vertices[i].Pos.rotateXZBy(-45);
559 vertices[i].Pos += intToFloat(p + getPosRelative());
562 u16 indices[] = {0,1,2,2,3,0};
563 buf->append(vertices, 4, indices, 6);
566 buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
567 buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
568 buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
569 //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
570 buf->getMaterial().MaterialType
571 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
572 if(dir == v3s16(0,-1,0))
573 buf->getMaterial().setTexture(0,
574 g_texturecache.get("torch_on_floor"));
575 else if(dir == v3s16(0,1,0))
576 buf->getMaterial().setTexture(0,
577 g_texturecache.get("torch_on_ceiling"));
578 // For backwards compatibility
579 else if(dir == v3s16(0,0,0))
580 buf->getMaterial().setTexture(0,
581 g_texturecache.get("torch_on_floor"));
583 buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
586 mesh_new->addMeshBuffer(buf);
592 Do some stuff to the mesh
595 mesh_new->recalculateBoundingBox();
598 Delete new mesh if it is empty
601 if(mesh_new->getMeshBufferCount() == 0)
613 scene::SMesh *mesh_old = mesh;
619 // Remove hardware buffers of meshbuffers of mesh
620 // NOTE: No way, this runs in a different thread and everything
621 /*u32 c = mesh_old->getMeshBufferCount();
622 for(u32 i=0; i<c; i++)
624 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
633 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
637 Propagates sunlight down through the block.
638 Doesn't modify nodes that are not affected by sunlight.
640 Returns false if sunlight at bottom block is invalid
641 Returns true if bottom block doesn't exist.
643 If there is a block above, continues from it.
644 If there is no block above, assumes there is sunlight, unless
645 is_underground is set.
647 At the moment, all sunlighted nodes are added to light_sources.
648 TODO: This could be optimized.
650 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
652 // Whether the sunlight at the top of the bottom block is valid
653 bool block_below_is_valid = true;
655 v3s16 pos_relative = getPosRelative();
657 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
659 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
661 bool no_sunlight = false;
662 bool no_top_block = false;
663 // Check if node above block has sunlight
665 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
666 if(n.getLight() != LIGHT_SUN)
675 catch(InvalidPositionException &e)
679 // TODO: This makes over-ground roofed places sunlighted
680 // Assume sunlight, unless is_underground==true
686 // TODO: There has to be some way to allow this behaviour
687 // As of now, it just makes everything dark.
689 //no_sunlight = true;
692 /*std::cout<<"("<<x<<","<<z<<"): "
693 <<"no_top_block="<<no_top_block
694 <<", is_underground="<<is_underground
695 <<", no_sunlight="<<no_sunlight
698 s16 y = MAP_BLOCKSIZE-1;
700 if(no_sunlight == false)
702 // Continue spreading sunlight downwards through transparent
708 MapNode &n = getNodeRef(pos);
710 if(n.sunlight_propagates())
712 n.setLight(LIGHT_SUN);
714 light_sources.insert(pos_relative + pos, true);
722 bool sunlight_should_go_down = (y==-1);
724 // Fill rest with black (only transparent ones)
728 MapNode &n = getNodeRef(pos);
730 if(n.light_propagates())
740 If the block below hasn't already been marked invalid:
742 Check if the node below the block has proper sunlight at top.
743 If not, the block below is invalid.
745 Ignore non-transparent nodes as they always have no light
749 if(block_below_is_valid)
751 MapNode n = getNodeParent(v3s16(x, -1, z));
752 if(n.light_propagates())
754 if(n.getLight() == LIGHT_SUN
755 && sunlight_should_go_down == false)
756 block_below_is_valid = false;
757 else if(n.getLight() != LIGHT_SUN
758 && sunlight_should_go_down == true)
759 block_below_is_valid = false;
763 catch(InvalidPositionException &e)
765 /*std::cout<<"InvalidBlockException for bottom block node"
767 // Just no block below, no need to panic.
772 return block_below_is_valid;
775 void MapBlock::copyTo(VoxelManipulator &dst)
777 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
778 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
780 dst.copyFrom(data, data_area, v3s16(0,0,0),
781 getPosRelative(), data_size);
784 /*void getPseudoObjects(v3f origin, f32 max_d,
785 core::array<DistanceSortedObject> &dest)
793 void MapBlock::serialize(std::ostream &os, u8 version)
795 if(!ser_ver_supported(version))
796 throw VersionMismatchException("ERROR: MapBlock format not supported");
800 throw SerializationError("ERROR: Not writing dummy block.");
803 // These have no compression
804 if(version <= 3 || version == 5 || version == 6)
806 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
808 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
809 SharedBuffer<u8> dest(buflen);
811 dest[0] = is_underground;
812 for(u32 i=0; i<nodecount; i++)
814 u32 s = 1 + i * MapNode::serializedLength(version);
815 data[i].serialize(&dest[s], version);
818 os.write((char*)*dest, dest.getSize());
825 Compress the materials and the params separately.
829 os.write((char*)&is_underground, 1);
831 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
833 // Get and compress materials
834 SharedBuffer<u8> materialdata(nodecount);
835 for(u32 i=0; i<nodecount; i++)
837 materialdata[i] = data[i].d;
839 compress(materialdata, os, version);
841 // Get and compress lights
842 SharedBuffer<u8> lightdata(nodecount);
843 for(u32 i=0; i<nodecount; i++)
845 lightdata[i] = data[i].param;
847 compress(lightdata, os, version);
851 // Get and compress pressure
852 SharedBuffer<u8> pressuredata(nodecount);
853 for(u32 i=0; i<nodecount; i++)
855 pressuredata[i] = data[i].pressure;
857 compress(pressuredata, os, version);
862 void MapBlock::deSerialize(std::istream &is, u8 version)
864 if(!ser_ver_supported(version))
865 throw VersionMismatchException("ERROR: MapBlock format not supported");
867 // These have no compression
868 if(version <= 3 || version == 5 || version == 6)
870 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
874 throw SerializationError
875 ("MapBlock::deSerialize: no enough input data");
876 is_underground = tmp;
877 for(u32 i=0; i<nodecount; i++)
879 s32 len = MapNode::serializedLength(version);
880 SharedBuffer<u8> d(len);
881 is.read((char*)*d, len);
882 if(is.gcount() != len)
883 throw SerializationError
884 ("MapBlock::deSerialize: no enough input data");
885 data[i].deSerialize(*d, version);
888 // All other versions
891 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
894 is.read((char*)&t8, 1);
898 // Uncompress and set material data
899 std::ostringstream os(std::ios_base::binary);
900 decompress(is, os, version);
901 std::string s = os.str();
902 if(s.size() != nodecount)
903 throw SerializationError
904 ("MapBlock::deSerialize: invalid format");
905 for(u32 i=0; i<s.size(); i++)
911 // Uncompress and set param data
912 std::ostringstream os(std::ios_base::binary);
913 decompress(is, os, version);
914 std::string s = os.str();
915 if(s.size() != nodecount)
916 throw SerializationError
917 ("MapBlock::deSerialize: invalid format");
918 for(u32 i=0; i<s.size(); i++)
920 data[i].param = s[i];
926 // Uncompress and set pressure data
927 std::ostringstream os(std::ios_base::binary);
928 decompress(is, os, version);
929 std::string s = os.str();
930 if(s.size() != nodecount)
931 throw SerializationError
932 ("MapBlock::deSerialize: invalid format");
933 for(u32 i=0; i<s.size(); i++)
935 data[i].pressure = s[i];