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 == MATERIAL_WATER)
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();
160 // Make some nice difference to different sides
161 if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
162 light = diminish_light(diminish_light(light));
163 else if(face_dir.X == -1 || face_dir.Z == -1)
164 light = diminish_light(light);
168 catch(InvalidPositionException &e)
175 Gets node material from any place relative to block.
176 Returns MATERIAL_AIR if doesn't exist.
178 u8 MapBlock::getNodeMaterial(v3s16 p)
181 MapNode n = getNodeParent(p);
184 catch(InvalidPositionException &e)
186 return MATERIAL_IGNORE;
192 translate_dir: unit vector with only one of x, y or z
193 face_dir: unit vector with only one of x, y or z
195 void MapBlock::updateFastFaceRow(v3s16 startpos,
199 core::list<FastFace*> &dest)
202 Precalculate some variables
204 v3f translate_dir_f(translate_dir.X, translate_dir.Y,
205 translate_dir.Z); // floating point conversion
206 v3f face_dir_f(face_dir.X, face_dir.Y,
207 face_dir.Z); // floating point conversion
208 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
209 getPosRelative().Z); // floating point conversion
213 The light in the air lights the surface is taken from
214 the node that is air.
216 u8 light = getFaceLight(p, face_dir);
218 u16 continuous_materials_count = 0;
220 u8 material0 = getNodeMaterial(p);
221 u8 material1 = getNodeMaterial(p + face_dir);
223 for(u16 j=0; j<length; j++)
225 bool next_is_different = true;
228 u8 material0_next = 0;
229 u8 material1_next = 0;
233 p_next = p + translate_dir;
234 material0_next = getNodeMaterial(p_next);
235 material1_next = getNodeMaterial(p_next + face_dir);
236 light_next = getFaceLight(p_next, face_dir);
238 if(material0_next == material0
239 && material1_next == material1
240 && light_next == light)
242 next_is_different = false;
246 continuous_materials_count++;
248 if(next_is_different)
251 Create a face if there should be one
253 u8 mf = face_materials(material0, material1);
257 // Floating point conversion of the position vector
258 v3f pf(p.X, p.Y, p.Z);
259 // Center point of face (kind of)
260 v3f sp = pf - ((f32)continuous_materials_count / 2. - 0.5) * translate_dir_f;
262 if(translate_dir.X != 0){
263 scale.X = continuous_materials_count;
265 if(translate_dir.Y != 0){
266 scale.Y = continuous_materials_count;
268 if(translate_dir.Z != 0){
269 scale.Z = continuous_materials_count;
274 // If node at sp (material0) is more solid
277 f = makeFastFace(material0, light,
278 sp, face_dir_f, scale,
281 // If node at sp is less solid (mf == 2)
284 f = makeFastFace(material1, light,
285 sp+face_dir_f, -1*face_dir_f, scale,
291 continuous_materials_count = 0;
292 material0 = material0_next;
293 material1 = material1_next;
302 This is used because CMeshBuffer::append() is very slow
306 video::SMaterial material;
307 core::array<u16> indices;
308 core::array<video::S3DVertex> vertices;
315 video::SMaterial material,
316 const video::S3DVertex* const vertices,
318 const u16* const indices,
322 PreMeshBuffer *p = NULL;
323 for(u32 i=0; i<m_prebuffers.size(); i++)
325 PreMeshBuffer &pp = m_prebuffers[i];
326 if(pp.material != material)
336 pp.material = material;
337 m_prebuffers.push_back(pp);
338 p = &m_prebuffers[m_prebuffers.size()-1];
341 u32 vertex_count = p->vertices.size();
342 for(u32 i=0; i<numIndices; i++)
344 u32 j = indices[i] + vertex_count;
347 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
348 // NOTE: Fix is to just add an another MeshBuffer
350 p->indices.push_back(j);
352 for(u32 i=0; i<numVertices; i++)
354 p->vertices.push_back(vertices[i]);
358 void fillMesh(scene::SMesh *mesh)
360 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
361 <<" meshbuffers"<<std::endl;*/
362 for(u32 i=0; i<m_prebuffers.size(); i++)
364 PreMeshBuffer &p = m_prebuffers[i];
366 /*dstream<<"p.vertices.size()="<<p.vertices.size()
367 <<", p.indices.size()="<<p.indices.size()
372 // This is a "Standard MeshBuffer",
373 // it's a typedeffed CMeshBuffer<video::S3DVertex>
374 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
376 buf->Material = p.material;
377 //((scene::SMeshBuffer*)buf)->Material = p.material;
379 //buf->setHardwareMappingHint(scene::EHM_STATIC);
381 mesh->addMeshBuffer(buf);
385 buf->append(p.vertices.pointer(), p.vertices.size(),
386 p.indices.pointer(), p.indices.size());
391 core::array<PreMeshBuffer> m_prebuffers;
394 void MapBlock::updateMesh()
396 /*v3s16 p = getPosRelative();
397 std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
398 <<"::updateMesh(): ";*/
399 //<<"::updateMesh()"<<std::endl;
402 TODO: Change this to directly generate the mesh (and get rid
406 core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
409 We are including the faces of the trailing edges of the block.
410 This means that when something changes, the caller must
411 also update the meshes of the blocks at the leading edges.
415 Go through every y,z and get top faces in rows of x+
417 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
418 //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
419 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
420 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
427 Go through every x,y and get right faces in rows of z+
429 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
430 //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
431 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
432 updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
439 Go through every y,z and get back faces in rows of x+
441 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
442 //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
443 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
444 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
451 scene::SMesh *mesh_new = NULL;
453 if(fastfaces_new->getSize() > 0)
455 MeshCollector collector;
457 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
459 for(; i != fastfaces_new->end(); i++)
463 const u16 indices[] = {0,1,2,2,3,0};
465 collector.append(g_materials[f->material], f->vertices, 4,
469 mesh_new = new scene::SMesh();
471 collector.fillMesh(mesh_new);
474 scene::IMeshBuffer *buf = NULL;
476 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
478 // MATERIAL_AIR shouldn't be used by any face
479 u8 material_in_use = MATERIAL_AIR;
481 for(; i != fastfaces_new->end(); i++)
485 if(f->material != material_in_use || buf == NULL)
487 // Try to get a meshbuffer associated with the material
488 buf = mesh_new->getMeshBuffer(g_materials[f->material]);
489 // If not found, create one
492 // This is a "Standard MeshBuffer",
493 // it's a typedeffed CMeshBuffer<video::S3DVertex>
494 buf = new scene::SMeshBuffer();
496 ((scene::SMeshBuffer*)buf)->Material = g_materials[f->material];
498 //buf->setHardwareMappingHint(scene::EHM_STATIC);
500 mesh_new->addMeshBuffer(buf);
504 material_in_use = f->material;
507 u16 new_indices[] = {0,1,2,2,3,0};
509 //buf->append(f->vertices, 4, indices, 6);
513 // Use VBO for mesh (this just would set this for ever buffer)
514 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
516 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
517 <<"and uses "<<mesh_new->getMeshBufferCount()
518 <<" materials (meshbuffers)"<<std::endl;*/
521 // TODO: Get rid of the FastFace stage
522 core::list<FastFace*>::Iterator i;
523 i = fastfaces_new->begin();
524 for(; i != fastfaces_new->end(); i++)
528 fastfaces_new->clear();
529 delete fastfaces_new;
537 scene::SMesh *mesh_old = mesh;
543 // Remove hardware buffers of meshbuffers of mesh
544 // NOTE: No way, this runs in a different thread and everything
545 /*u32 c = mesh_old->getMeshBufferCount();
546 for(u32 i=0; i<c; i++)
548 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
557 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
561 Propagates sunlight down through the block.
562 Doesn't modify nodes that are not affected by sunlight.
564 Returns false if sunlight at bottom block is invalid
565 Returns true if bottom block doesn't exist.
567 If there is a block above, continues from it.
568 If there is no block above, assumes there is sunlight, unless
569 is_underground is set.
571 At the moment, all sunlighted nodes are added to light_sources.
572 TODO: This could be optimized.
574 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
576 // Whether the sunlight at the top of the bottom block is valid
577 bool block_below_is_valid = true;
579 v3s16 pos_relative = getPosRelative();
581 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
583 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
585 bool no_sunlight = false;
586 bool no_top_block = false;
587 // Check if node above block has sunlight
589 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
590 if(n.getLight() != LIGHT_SUN)
599 catch(InvalidPositionException &e)
603 // TODO: This makes over-ground roofed places sunlighted
604 // Assume sunlight, unless is_underground==true
610 // TODO: There has to be some way to allow this behaviour
611 // As of now, it just makes everything dark.
613 //no_sunlight = true;
616 /*std::cout<<"("<<x<<","<<z<<"): "
617 <<"no_top_block="<<no_top_block
618 <<", is_underground="<<is_underground
619 <<", no_sunlight="<<no_sunlight
622 s16 y = MAP_BLOCKSIZE-1;
624 if(no_sunlight == false)
626 // Continue spreading sunlight downwards through transparent
632 MapNode &n = getNodeRef(pos);
634 if(n.sunlight_propagates())
636 n.setLight(LIGHT_SUN);
638 light_sources.insert(pos_relative + pos, true);
646 bool sunlight_should_go_down = (y==-1);
648 // Fill rest with black (only transparent ones)
652 MapNode &n = getNodeRef(pos);
654 if(n.light_propagates())
664 If the block below hasn't already been marked invalid:
666 Check if the node below the block has proper sunlight at top.
667 If not, the block below is invalid.
669 Ignore non-transparent nodes as they always have no light
673 if(block_below_is_valid)
675 MapNode n = getNodeParent(v3s16(x, -1, z));
676 if(n.light_propagates())
678 if(n.getLight() == LIGHT_SUN
679 && sunlight_should_go_down == false)
680 block_below_is_valid = false;
681 else if(n.getLight() != LIGHT_SUN
682 && sunlight_should_go_down == true)
683 block_below_is_valid = false;
687 catch(InvalidPositionException &e)
689 /*std::cout<<"InvalidBlockException for bottom block node"
691 // Just no block below, no need to panic.
696 return block_below_is_valid;
699 void MapBlock::copyTo(VoxelManipulator &dst)
701 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
702 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
704 dst.copyFrom(data, data_area, v3s16(0,0,0),
705 getPosRelative(), data_size);
712 void MapBlock::serialize(std::ostream &os, u8 version)
714 if(!ser_ver_supported(version))
715 throw VersionMismatchException("ERROR: MapBlock format not supported");
719 throw SerializationError("ERROR: Not writing dummy block.");
722 // These have no compression
723 if(version <= 3 || version == 5 || version == 6)
725 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
727 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
728 SharedBuffer<u8> dest(buflen);
730 dest[0] = is_underground;
731 for(u32 i=0; i<nodecount; i++)
733 u32 s = 1 + i * MapNode::serializedLength(version);
734 data[i].serialize(&dest[s], version);
737 os.write((char*)*dest, dest.getSize());
744 Compress the materials and the params separately.
748 os.write((char*)&is_underground, 1);
750 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
752 // Get and compress materials
753 SharedBuffer<u8> materialdata(nodecount);
754 for(u32 i=0; i<nodecount; i++)
756 materialdata[i] = data[i].d;
758 compress(materialdata, os, version);
760 // Get and compress params
761 SharedBuffer<u8> paramdata(nodecount);
762 for(u32 i=0; i<nodecount; i++)
764 paramdata[i] = data[i].param;
766 compress(paramdata, os, version);
770 // Get and compress pressure
771 SharedBuffer<u8> pressuredata(nodecount);
772 for(u32 i=0; i<nodecount; i++)
774 pressuredata[i] = data[i].pressure;
776 compress(pressuredata, os, version);
781 void MapBlock::deSerialize(std::istream &is, u8 version)
783 if(!ser_ver_supported(version))
784 throw VersionMismatchException("ERROR: MapBlock format not supported");
786 // These have no compression
787 if(version <= 3 || version == 5 || version == 6)
789 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
793 throw SerializationError
794 ("MapBlock::deSerialize: no enough input data");
795 is_underground = tmp;
796 for(u32 i=0; i<nodecount; i++)
798 s32 len = MapNode::serializedLength(version);
799 SharedBuffer<u8> d(len);
800 is.read((char*)*d, len);
801 if(is.gcount() != len)
802 throw SerializationError
803 ("MapBlock::deSerialize: no enough input data");
804 data[i].deSerialize(*d, version);
807 // All other versions
810 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
813 is.read((char*)&t8, 1);
817 // Uncompress and set material data
818 std::ostringstream os(std::ios_base::binary);
819 decompress(is, os, version);
820 std::string s = os.str();
821 if(s.size() != nodecount)
822 throw SerializationError
823 ("MapBlock::deSerialize: invalid format");
824 for(u32 i=0; i<s.size(); i++)
830 // Uncompress and set param data
831 std::ostringstream os(std::ios_base::binary);
832 decompress(is, os, version);
833 std::string s = os.str();
834 if(s.size() != nodecount)
835 throw SerializationError
836 ("MapBlock::deSerialize: invalid format");
837 for(u32 i=0; i<s.size(); i++)
839 data[i].param = s[i];
845 // Uncompress and set pressure data
846 std::ostringstream os(std::ios_base::binary);
847 decompress(is, os, version);
848 std::string s = os.str();
849 if(s.size() != nodecount)
850 throw SerializationError
851 ("MapBlock::deSerialize: invalid format");
852 for(u32 i=0; i<s.size(); i++)
854 data[i].pressure = s[i];