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 || material == MATERIAL_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();
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_IGNORE if doesn't exist or should not be drawn.
178 u8 MapBlock::getNodeMaterial(v3s16 p)
181 MapNode n = getNodeParent(p);
183 return content_cube_material(n.d);
185 catch(InvalidPositionException &e)
187 return MATERIAL_IGNORE;
193 translate_dir: unit vector with only one of x, y or z
194 face_dir: unit vector with only one of x, y or z
196 void MapBlock::updateFastFaceRow(v3s16 startpos,
200 core::list<FastFace*> &dest)
203 Precalculate some variables
205 v3f translate_dir_f(translate_dir.X, translate_dir.Y,
206 translate_dir.Z); // floating point conversion
207 v3f face_dir_f(face_dir.X, face_dir.Y,
208 face_dir.Z); // floating point conversion
209 v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
210 getPosRelative().Z); // floating point conversion
214 The light in the air lights the surface is taken from
215 the node that is air.
217 u8 light = getFaceLight(p, face_dir);
219 u16 continuous_materials_count = 0;
221 u8 material0 = getNodeMaterial(p);
222 u8 material1 = getNodeMaterial(p + face_dir);
224 for(u16 j=0; j<length; j++)
226 bool next_is_different = true;
229 u8 material0_next = 0;
230 u8 material1_next = 0;
234 p_next = p + translate_dir;
235 material0_next = getNodeMaterial(p_next);
236 material1_next = getNodeMaterial(p_next + face_dir);
237 light_next = getFaceLight(p_next, face_dir);
239 if(material0_next == material0
240 && material1_next == material1
241 && light_next == light)
243 next_is_different = false;
247 continuous_materials_count++;
249 if(next_is_different)
252 Create a face if there should be one
254 u8 mf = face_materials(material0, material1);
258 // Floating point conversion of the position vector
259 v3f pf(p.X, p.Y, p.Z);
260 // Center point of face (kind of)
261 v3f sp = pf - ((f32)continuous_materials_count / 2. - 0.5) * translate_dir_f;
263 if(translate_dir.X != 0){
264 scale.X = continuous_materials_count;
266 if(translate_dir.Y != 0){
267 scale.Y = continuous_materials_count;
269 if(translate_dir.Z != 0){
270 scale.Z = continuous_materials_count;
275 // If node at sp (material0) is more solid
278 f = makeFastFace(material0, light,
279 sp, face_dir_f, scale,
282 // If node at sp is less solid (mf == 2)
285 f = makeFastFace(material1, light,
286 sp+face_dir_f, -1*face_dir_f, scale,
292 continuous_materials_count = 0;
293 material0 = material0_next;
294 material1 = material1_next;
303 This is used because CMeshBuffer::append() is very slow
307 video::SMaterial material;
308 core::array<u16> indices;
309 core::array<video::S3DVertex> vertices;
316 video::SMaterial material,
317 const video::S3DVertex* const vertices,
319 const u16* const indices,
323 PreMeshBuffer *p = NULL;
324 for(u32 i=0; i<m_prebuffers.size(); i++)
326 PreMeshBuffer &pp = m_prebuffers[i];
327 if(pp.material != material)
337 pp.material = material;
338 m_prebuffers.push_back(pp);
339 p = &m_prebuffers[m_prebuffers.size()-1];
342 u32 vertex_count = p->vertices.size();
343 for(u32 i=0; i<numIndices; i++)
345 u32 j = indices[i] + vertex_count;
348 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
349 // NOTE: Fix is to just add an another MeshBuffer
351 p->indices.push_back(j);
353 for(u32 i=0; i<numVertices; i++)
355 p->vertices.push_back(vertices[i]);
359 void fillMesh(scene::SMesh *mesh)
361 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
362 <<" meshbuffers"<<std::endl;*/
363 for(u32 i=0; i<m_prebuffers.size(); i++)
365 PreMeshBuffer &p = m_prebuffers[i];
367 /*dstream<<"p.vertices.size()="<<p.vertices.size()
368 <<", p.indices.size()="<<p.indices.size()
373 // This is a "Standard MeshBuffer",
374 // it's a typedeffed CMeshBuffer<video::S3DVertex>
375 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
377 buf->Material = p.material;
378 //((scene::SMeshBuffer*)buf)->Material = p.material;
380 //buf->setHardwareMappingHint(scene::EHM_STATIC);
382 mesh->addMeshBuffer(buf);
386 buf->append(p.vertices.pointer(), p.vertices.size(),
387 p.indices.pointer(), p.indices.size());
392 core::array<PreMeshBuffer> m_prebuffers;
395 void MapBlock::updateMesh()
397 /*v3s16 p = getPosRelative();
398 std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
399 <<"::updateMesh(): ";*/
400 //<<"::updateMesh()"<<std::endl;
403 TODO: Change this to directly generate the mesh (and get rid
407 core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>;
410 We are including the faces of the trailing edges of the block.
411 This means that when something changes, the caller must
412 also update the meshes of the blocks at the leading edges.
416 Go through every y,z and get top faces in rows of x+
418 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
419 //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){
420 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
421 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
428 Go through every x,y and get right faces in rows of z+
430 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
431 //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){
432 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
433 updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE,
440 Go through every y,z and get back faces in rows of x+
442 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
443 //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){
444 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
445 updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE,
452 scene::SMesh *mesh_new = NULL;
454 if(fastfaces_new->getSize() > 0)
456 MeshCollector collector;
458 core::list<FastFace*>::Iterator i = fastfaces_new->begin();
460 for(; i != fastfaces_new->end(); i++)
464 const u16 indices[] = {0,1,2,2,3,0};
466 collector.append(g_materials[f->material], f->vertices, 4,
470 mesh_new = new scene::SMesh();
472 collector.fillMesh(mesh_new);
474 // Use VBO for mesh (this just would set this for ever buffer)
475 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
477 /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces "
478 <<"and uses "<<mesh_new->getMeshBufferCount()
479 <<" materials (meshbuffers)"<<std::endl;*/
483 Clear temporary FastFaces
486 core::list<FastFace*>::Iterator i;
487 i = fastfaces_new->begin();
488 for(; i != fastfaces_new->end(); i++)
492 fastfaces_new->clear();
493 delete fastfaces_new;
496 Add special graphics:
500 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
501 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
502 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
513 scene::SMesh *mesh_old = mesh;
519 // Remove hardware buffers of meshbuffers of mesh
520 // NOTE: No way, this runs in a different thread and everything
521 /*u32 c = mesh_old->getMeshBufferCount();
522 for(u32 i=0; i<c; i++)
524 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
533 //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
537 Propagates sunlight down through the block.
538 Doesn't modify nodes that are not affected by sunlight.
540 Returns false if sunlight at bottom block is invalid
541 Returns true if bottom block doesn't exist.
543 If there is a block above, continues from it.
544 If there is no block above, assumes there is sunlight, unless
545 is_underground is set.
547 At the moment, all sunlighted nodes are added to light_sources.
548 TODO: This could be optimized.
550 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
552 // Whether the sunlight at the top of the bottom block is valid
553 bool block_below_is_valid = true;
555 v3s16 pos_relative = getPosRelative();
557 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
559 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
561 bool no_sunlight = false;
562 bool no_top_block = false;
563 // Check if node above block has sunlight
565 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
566 if(n.getLight() != LIGHT_SUN)
575 catch(InvalidPositionException &e)
579 // TODO: This makes over-ground roofed places sunlighted
580 // Assume sunlight, unless is_underground==true
586 // TODO: There has to be some way to allow this behaviour
587 // As of now, it just makes everything dark.
589 //no_sunlight = true;
592 /*std::cout<<"("<<x<<","<<z<<"): "
593 <<"no_top_block="<<no_top_block
594 <<", is_underground="<<is_underground
595 <<", no_sunlight="<<no_sunlight
598 s16 y = MAP_BLOCKSIZE-1;
600 if(no_sunlight == false)
602 // Continue spreading sunlight downwards through transparent
608 MapNode &n = getNodeRef(pos);
610 if(n.sunlight_propagates())
612 n.setLight(LIGHT_SUN);
614 light_sources.insert(pos_relative + pos, true);
622 bool sunlight_should_go_down = (y==-1);
624 // Fill rest with black (only transparent ones)
628 MapNode &n = getNodeRef(pos);
630 if(n.light_propagates())
640 If the block below hasn't already been marked invalid:
642 Check if the node below the block has proper sunlight at top.
643 If not, the block below is invalid.
645 Ignore non-transparent nodes as they always have no light
649 if(block_below_is_valid)
651 MapNode n = getNodeParent(v3s16(x, -1, z));
652 if(n.light_propagates())
654 if(n.getLight() == LIGHT_SUN
655 && sunlight_should_go_down == false)
656 block_below_is_valid = false;
657 else if(n.getLight() != LIGHT_SUN
658 && sunlight_should_go_down == true)
659 block_below_is_valid = false;
663 catch(InvalidPositionException &e)
665 /*std::cout<<"InvalidBlockException for bottom block node"
667 // Just no block below, no need to panic.
672 return block_below_is_valid;
675 void MapBlock::copyTo(VoxelManipulator &dst)
677 v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
678 VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
680 dst.copyFrom(data, data_area, v3s16(0,0,0),
681 getPosRelative(), data_size);
688 void MapBlock::serialize(std::ostream &os, u8 version)
690 if(!ser_ver_supported(version))
691 throw VersionMismatchException("ERROR: MapBlock format not supported");
695 throw SerializationError("ERROR: Not writing dummy block.");
698 // These have no compression
699 if(version <= 3 || version == 5 || version == 6)
701 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
703 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
704 SharedBuffer<u8> dest(buflen);
706 dest[0] = is_underground;
707 for(u32 i=0; i<nodecount; i++)
709 u32 s = 1 + i * MapNode::serializedLength(version);
710 data[i].serialize(&dest[s], version);
713 os.write((char*)*dest, dest.getSize());
720 Compress the materials and the params separately.
724 os.write((char*)&is_underground, 1);
726 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
728 // Get and compress materials
729 SharedBuffer<u8> materialdata(nodecount);
730 for(u32 i=0; i<nodecount; i++)
732 materialdata[i] = data[i].d;
734 compress(materialdata, os, version);
736 // Get and compress params
737 SharedBuffer<u8> paramdata(nodecount);
738 for(u32 i=0; i<nodecount; i++)
740 paramdata[i] = data[i].param;
742 compress(paramdata, os, version);
746 // Get and compress pressure
747 SharedBuffer<u8> pressuredata(nodecount);
748 for(u32 i=0; i<nodecount; i++)
750 pressuredata[i] = data[i].pressure;
752 compress(pressuredata, os, version);
757 void MapBlock::deSerialize(std::istream &is, u8 version)
759 if(!ser_ver_supported(version))
760 throw VersionMismatchException("ERROR: MapBlock format not supported");
762 // These have no compression
763 if(version <= 3 || version == 5 || version == 6)
765 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
769 throw SerializationError
770 ("MapBlock::deSerialize: no enough input data");
771 is_underground = tmp;
772 for(u32 i=0; i<nodecount; i++)
774 s32 len = MapNode::serializedLength(version);
775 SharedBuffer<u8> d(len);
776 is.read((char*)*d, len);
777 if(is.gcount() != len)
778 throw SerializationError
779 ("MapBlock::deSerialize: no enough input data");
780 data[i].deSerialize(*d, version);
783 // All other versions
786 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
789 is.read((char*)&t8, 1);
793 // Uncompress and set material data
794 std::ostringstream os(std::ios_base::binary);
795 decompress(is, os, version);
796 std::string s = os.str();
797 if(s.size() != nodecount)
798 throw SerializationError
799 ("MapBlock::deSerialize: invalid format");
800 for(u32 i=0; i<s.size(); i++)
806 // Uncompress and set param data
807 std::ostringstream os(std::ios_base::binary);
808 decompress(is, os, version);
809 std::string s = os.str();
810 if(s.size() != nodecount)
811 throw SerializationError
812 ("MapBlock::deSerialize: invalid format");
813 for(u32 i=0; i<s.size(); i++)
815 data[i].param = s[i];
821 // Uncompress and set pressure data
822 std::ostringstream os(std::ios_base::binary);
823 decompress(is, os, version);
824 std::string s = os.str();
825 if(s.size() != nodecount)
826 throw SerializationError
827 ("MapBlock::deSerialize: invalid format");
828 for(u32 i=0; i<s.size(); i++)
830 data[i].pressure = s[i];