2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
17 bool MapBlock::isValidPositionParent(v3s16 p)
19 if(isValidPosition(p))
24 return m_parent->isValidPosition(getPosRelative() + p);
28 MapNode MapBlock::getNodeParent(v3s16 p)
30 if(isValidPosition(p) == false)
32 return m_parent->getNode(getPosRelative() + p);
37 throw InvalidPositionException();
38 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
42 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
44 if(isValidPosition(p) == false)
46 m_parent->setNode(getPosRelative() + p, n);
51 throw InvalidPositionException();
52 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
56 FastFace * MapBlock::makeFastFace(u8 material, u8 light, v3f p,
57 v3f dir, v3f scale, v3f posRelative_f)
59 FastFace *f = new FastFace;
61 // Position is at the center of the cube.
66 // If looking towards z+, this is the face that is behind
67 // the center point, facing towards z+.
68 vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
69 vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
70 vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
71 vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
74 TODO: Rotate it the right way (one side comes upside down)
76 core::CMatrix4<f32> m;
77 m.buildRotateFromTo(v3f(0,0,1), dir);
79 for(u16 i=0; i<4; i++){
80 m.rotateVect(vertex_pos[i]);
81 vertex_pos[i].X *= scale.X;
82 vertex_pos[i].Y *= scale.Y;
83 vertex_pos[i].Z *= scale.Z;
84 vertex_pos[i] += pos + posRelative_f;
88 if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
89 else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
90 else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
92 v3f zerovector = v3f(0,0,0);
94 u8 li = decode_light(light);
99 if(material == MATERIAL_WATER)
104 video::SColor c = video::SColor(alpha,li,li,li);
106 /*f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
107 core::vector2d<f32>(0,1));
108 f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
109 core::vector2d<f32>(abs_scale,1));
110 f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
111 core::vector2d<f32>(abs_scale,0));
112 f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
113 core::vector2d<f32>(0,0));*/
114 f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
115 core::vector2d<f32>(0,1));
116 f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
117 core::vector2d<f32>(abs_scale,1));
118 f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
119 core::vector2d<f32>(abs_scale,0));
120 f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
121 core::vector2d<f32>(0,0));
123 f->material = material;
129 Parameters must consist of air and !air.
130 Order doesn't matter.
132 If either of the nodes doesn't exist, light is 0.
134 u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir)
137 MapNode n = getNodeParent(p);
138 MapNode n2 = getNodeParent(p + face_dir);
140 if(n.solidness() < n2.solidness())
141 light = n.getLight();
143 light = n2.getLight();
145 // Make some nice difference to different sides
146 if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
147 light = diminish_light(diminish_light(light));
148 else if(face_dir.X == -1 || face_dir.Z == -1)
149 light = diminish_light(light);
153 catch(InvalidPositionException &e)
160 Gets node material from any place relative to block.
161 Returns MATERIAL_AIR if doesn't exist.
163 u8 MapBlock::getNodeMaterial(v3s16 p)
166 MapNode n = getNodeParent(p);
169 catch(InvalidPositionException &e)
171 return MATERIAL_IGNORE;
177 translate_dir: unit vector with only one of x, y or z
178 face_dir: unit vector with only one of x, y or z
180 void MapBlock::updateFastFaceRow(v3s16 startpos,
184 core::list<FastFace*> &dest)
187 Precalculate some variables
189 v3f translate_dir_f(translate_dir.X, translate_dir.Y,
190 translate_dir.Z); // floating point conversion
191 v3f face_dir_f(face_dir.X, face_dir.Y,
192 face_dir.Z); // floating point conversion
193 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
194 getPosRelative().Z); // floating point conversion
198 The light in the air lights the surface is taken from
199 the node that is air.
201 u8 light = getFaceLight(p, face_dir);
203 u16 continuous_materials_count = 0;
205 u8 material0 = getNodeMaterial(p);
206 u8 material1 = getNodeMaterial(p + face_dir);
208 for(u16 j=0; j<length; j++)
210 bool next_is_different = true;
213 u8 material0_next = 0;
214 u8 material1_next = 0;
218 p_next = p + translate_dir;
219 material0_next = getNodeMaterial(p_next);
220 material1_next = getNodeMaterial(p_next + face_dir);
221 light_next = getFaceLight(p_next, face_dir);
223 if(material0_next == material0
224 && material1_next == material1
225 && light_next == light)
227 next_is_different = false;
231 continuous_materials_count++;
233 if(next_is_different)
236 Create a face if there should be one
238 u8 mf = face_materials(material0, material1);
242 // Floating point conversion of the position vector
243 v3f pf(p.X, p.Y, p.Z);
244 // Center point of face (kind of)
245 v3f sp = pf - ((f32)continuous_materials_count / 2. - 0.5) * translate_dir_f;
247 if(translate_dir.X != 0){
248 scale.X = continuous_materials_count;
250 if(translate_dir.Y != 0){
251 scale.Y = continuous_materials_count;
253 if(translate_dir.Z != 0){
254 scale.Z = continuous_materials_count;
259 // If node at sp (material0) is more solid
262 f = makeFastFace(material0, light,
263 sp, face_dir_f, scale,
266 // If node at sp is less solid (mf == 2)
269 f = makeFastFace(material1, light,
270 sp+face_dir_f, -1*face_dir_f, scale,
276 continuous_materials_count = 0;
277 material0 = material0_next;
278 material1 = material1_next;
287 This is used because CMeshBuffer::append() is very slow
291 video::SMaterial material;
292 core::array<u16> indices;
293 core::array<video::S3DVertex> vertices;
300 video::SMaterial material,
301 const video::S3DVertex* const vertices,
303 const u16* const indices,
307 PreMeshBuffer *p = NULL;
308 for(u32 i=0; i<m_prebuffers.size(); i++)
310 PreMeshBuffer &pp = m_prebuffers[i];
311 if(pp.material != material)
321 pp.material = material;
322 m_prebuffers.push_back(pp);
323 p = &m_prebuffers[m_prebuffers.size()-1];
326 u32 vertex_count = p->vertices.size();
327 for(u32 i=0; i<numIndices; i++)
329 u32 j = indices[i] + vertex_count;
332 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
333 // NOTE: Fix is to just add an another MeshBuffer
335 p->indices.push_back(j);
337 for(u32 i=0; i<numVertices; i++)
339 p->vertices.push_back(vertices[i]);
343 void fillMesh(scene::SMesh *mesh)
345 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
346 <<" meshbuffers"<<std::endl;*/
347 for(u32 i=0; i<m_prebuffers.size(); i++)
349 PreMeshBuffer &p = m_prebuffers[i];
351 /*dstream<<"p.vertices.size()="<<p.vertices.size()
352 <<", p.indices.size()="<<p.indices.size()
357 // This is a "Standard MeshBuffer",
358 // it's a typedeffed CMeshBuffer<video::S3DVertex>
359 scene::IMeshBuffer *buf = new scene::SMeshBuffer();
361 ((scene::SMeshBuffer*)buf)->Material = p.material;
363 //buf->setHardwareMappingHint(scene::EHM_STATIC);
365 mesh->addMeshBuffer(buf);
369 buf->append(p.vertices.pointer(), p.vertices.size(),
370 p.indices.pointer(), p.indices.size());
375 core::array<PreMeshBuffer> m_prebuffers;
378 void MapBlock::updateMesh()
380 /*v3s16 p = getPosRelative();
381 std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
382 <<"::updateMesh(): ";*/
383 //<<"::updateMesh()"<<std::endl;
386 TODO: Change this to directly generate the mesh (and get rid
390 core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
393 We are including the faces of the trailing edges of the block.
394 This means that when something changes, the caller must
395 also update the meshes of the blocks at the leading edges.
399 Go through every y,z and get top faces in rows of x+
401 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
402 //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
403 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
404 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
411 Go through every x,y and get right faces in rows of z+
413 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
414 //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
415 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
416 updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
423 Go through every y,z and get back faces in rows of x+
425 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
426 //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
427 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
428 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
435 scene::SMesh *mesh_new = NULL;
437 if(fastfaces_new->getSize() > 0)
439 MeshCollector collector;
441 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
443 for(; i != fastfaces_new->end(); i++)
447 const u16 indices[] = {0,1,2,2,3,0};
449 collector.append(g_materials[f->material], f->vertices, 4,
453 mesh_new = new scene::SMesh();
455 collector.fillMesh(mesh_new);
458 scene::IMeshBuffer *buf = NULL;
460 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
462 // MATERIAL_AIR shouldn't be used by any face
463 u8 material_in_use = MATERIAL_AIR;
465 for(; i != fastfaces_new->end(); i++)
469 if(f->material != material_in_use || buf == NULL)
471 // Try to get a meshbuffer associated with the material
472 buf = mesh_new->getMeshBuffer(g_materials[f->material]);
473 // If not found, create one
476 // This is a "Standard MeshBuffer",
477 // it's a typedeffed CMeshBuffer<video::S3DVertex>
478 buf = new scene::SMeshBuffer();
480 ((scene::SMeshBuffer*)buf)->Material = g_materials[f->material];
482 //buf->setHardwareMappingHint(scene::EHM_STATIC);
484 mesh_new->addMeshBuffer(buf);
488 material_in_use = f->material;
491 u16 new_indices[] = {0,1,2,2,3,0};
493 //buf->append(f->vertices, 4, indices, 6);
497 // Use VBO for mesh (this just would set this for ever buffer)
498 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
500 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
501 <<"and uses "<<mesh_new->getMeshBufferCount()
502 <<" materials (meshbuffers)"<<std::endl;*/
505 // TODO: Get rid of the FastFace stage
506 core::list<FastFace*>::Iterator i;
507 i = fastfaces_new->begin();
508 for(; i != fastfaces_new->end(); i++)
512 fastfaces_new->clear();
513 delete fastfaces_new;
521 scene::SMesh *mesh_old = mesh;
527 // Remove hardware buffers of meshbuffers of mesh
528 // NOTE: No way, this runs in a different thread and everything
529 /*u32 c = mesh_old->getMeshBufferCount();
530 for(u32 i=0; i<c; i++)
532 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
541 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
545 Propagates sunlight down through the block.
546 Doesn't modify nodes that are not affected by sunlight.
548 Returns false if sunlight at bottom block is invalid
549 Returns true if bottom block doesn't exist.
551 If there is a block above, continues from it.
552 If there is no block above, assumes there is sunlight, unless
553 is_underground is set.
555 At the moment, all sunlighted nodes are added to light_sources.
556 TODO: This could be optimized.
558 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
560 // Whether the sunlight at the top of the bottom block is valid
561 bool block_below_is_valid = true;
563 v3s16 pos_relative = getPosRelative();
565 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
567 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
569 bool no_sunlight = false;
570 bool no_top_block = false;
571 // Check if node above block has sunlight
573 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
574 if(n.getLight() != LIGHT_SUN)
583 catch(InvalidPositionException &e)
587 // TODO: This makes over-ground roofed places sunlighted
588 // Assume sunlight, unless is_underground==true
594 // TODO: There has to be some way to allow this behaviour
595 // As of now, it just makes everything dark.
597 //no_sunlight = true;
600 /*std::cout<<"("<<x<<","<<z<<"): "
601 <<"no_top_block="<<no_top_block
602 <<", is_underground="<<is_underground
603 <<", no_sunlight="<<no_sunlight
606 s16 y = MAP_BLOCKSIZE-1;
608 if(no_sunlight == false)
610 // Continue spreading sunlight downwards through transparent
616 MapNode &n = getNodeRef(pos);
618 if(n.sunlight_propagates())
620 n.setLight(LIGHT_SUN);
622 light_sources.insert(pos_relative + pos, true);
630 bool sunlight_should_go_down = (y==-1);
632 // Fill rest with black (only transparent ones)
636 MapNode &n = getNodeRef(pos);
638 if(n.light_propagates())
648 If the block below hasn't already been marked invalid:
650 Check if the node below the block has proper sunlight at top.
651 If not, the block below is invalid.
653 Ignore non-transparent nodes as they always have no light
657 if(block_below_is_valid)
659 MapNode n = getNodeParent(v3s16(x, -1, z));
660 if(n.light_propagates())
662 if(n.getLight() == LIGHT_SUN
663 && sunlight_should_go_down == false)
664 block_below_is_valid = false;
665 else if(n.getLight() != LIGHT_SUN
666 && sunlight_should_go_down == true)
667 block_below_is_valid = false;
671 catch(InvalidPositionException &e)
673 /*std::cout<<"InvalidBlockException for bottom block node"
675 // Just no block below, no need to panic.
680 return block_below_is_valid;
687 void MapBlock::serialize(std::ostream &os, u8 version)
689 if(!ser_ver_supported(version))
690 throw VersionMismatchException("ERROR: MapBlock format not supported");
694 throw SerializationError("ERROR: Not writing dummy block.");
697 // These have no compression
698 if(version <= 3 || version == 5 || version == 6)
700 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
702 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
703 SharedBuffer<u8> dest(buflen);
705 dest[0] = is_underground;
706 for(u32 i=0; i<nodecount; i++)
708 u32 s = 1 + i * MapNode::serializedLength(version);
709 data[i].serialize(&dest[s], version);
712 os.write((char*)*dest, dest.getSize());
719 Compress the materials and the params separately.
723 os.write((char*)&is_underground, 1);
725 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
727 // Get and compress materials
728 SharedBuffer<u8> materialdata(nodecount);
729 for(u32 i=0; i<nodecount; i++)
731 materialdata[i] = data[i].d;
733 compress(materialdata, os, version);
735 // Get and compress params
736 SharedBuffer<u8> paramdata(nodecount);
737 for(u32 i=0; i<nodecount; i++)
739 paramdata[i] = data[i].param;
741 compress(paramdata, os, version);
745 void MapBlock::deSerialize(std::istream &is, u8 version)
747 if(!ser_ver_supported(version))
748 throw VersionMismatchException("ERROR: MapBlock format not supported");
750 // These have no compression
751 if(version <= 3 || version == 5 || version == 6)
753 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
757 throw SerializationError
758 ("MapBlock::deSerialize: no enough input data");
759 is_underground = tmp;
760 for(u32 i=0; i<nodecount; i++)
762 s32 len = MapNode::serializedLength(version);
763 SharedBuffer<u8> d(len);
764 is.read((char*)*d, len);
765 if(is.gcount() != len)
766 throw SerializationError
767 ("MapBlock::deSerialize: no enough input data");
768 data[i].deSerialize(*d, version);
771 // All other versions
774 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
777 is.read((char*)&t8, 1);
781 // Uncompress and set material data
782 std::ostringstream os(std::ios_base::binary);
783 decompress(is, os, version);
784 std::string s = os.str();
785 if(s.size() != nodecount)
786 throw SerializationError
787 ("MapBlock::deSerialize: invalid format");
788 for(u32 i=0; i<s.size(); i++)
794 // Uncompress and set param data
795 std::ostringstream os(std::ios_base::binary);
796 decompress(is, os, version);
797 std::string s = os.str();
798 if(s.size() != nodecount)
799 throw SerializationError
800 ("MapBlock::deSerialize: invalid format");
801 for(u32 i=0; i<s.size(); i++)
803 data[i].param = s[i];