diff options
author | Perttu Ahola <celeron55@gmail.com> | 2010-11-27 01:02:21 +0200 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2010-11-27 01:02:21 +0200 |
commit | 4e249fb3fbf75f0359758760d88e22aa5b14533c (patch) | |
tree | 323087d05efbd2ace27b316d4f017cf812a31992 /src/mapblock.cpp | |
download | minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.gz minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.tar.bz2 minetest-4e249fb3fbf75f0359758760d88e22aa5b14533c.zip |
Initial files
Diffstat (limited to 'src/mapblock.cpp')
-rw-r--r-- | src/mapblock.cpp | 698 |
1 files changed, 698 insertions, 0 deletions
diff --git a/src/mapblock.cpp b/src/mapblock.cpp new file mode 100644 index 000000000..25561008a --- /dev/null +++ b/src/mapblock.cpp @@ -0,0 +1,698 @@ +/* +(c) 2010 Perttu Ahola <celeron55@gmail.com> +*/ + +#include "mapblock.h" +#include "map.h" +// For g_materials +#include "main.h" +#include "light.h" +#include <sstream> + + +/* + MapBlock +*/ + +bool MapBlock::isValidPositionParent(v3s16 p) +{ + if(isValidPosition(p)) + { + return true; + } + else{ + return m_parent->isValidPosition(getPosRelative() + p); + } +} + +MapNode MapBlock::getNodeParent(v3s16 p) +{ + if(isValidPosition(p) == false) + { + return m_parent->getNode(getPosRelative() + p); + } + else + { + if(data == NULL) + throw InvalidPositionException(); + return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X]; + } +} + +void MapBlock::setNodeParent(v3s16 p, MapNode & n) +{ + if(isValidPosition(p) == false) + { + m_parent->setNode(getPosRelative() + p, n); + } + else + { + if(data == NULL) + throw InvalidPositionException(); + data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n; + } +} + +FastFace * MapBlock::makeFastFace(u8 material, u8 light, v3f p, + v3f dir, v3f scale, v3f posRelative_f) +{ + FastFace *f = new FastFace; + + // Position is at the center of the cube. + v3f pos = p * BS; + posRelative_f *= BS; + + v3f vertex_pos[4]; + // If looking towards z+, this is the face that is behind + // the center point, facing towards z+. + vertex_pos[0] = v3f( BS/2,-BS/2,BS/2); + vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2); + vertex_pos[2] = v3f(-BS/2, BS/2,BS/2); + vertex_pos[3] = v3f( BS/2, BS/2,BS/2); + + /* + TODO: Rotate it the right way (one side comes upside down) + */ + core::CMatrix4<f32> m; + m.buildRotateFromTo(v3f(0,0,1), dir); + + for(u16 i=0; i<4; i++){ + m.rotateVect(vertex_pos[i]); + vertex_pos[i].X *= scale.X; + vertex_pos[i].Y *= scale.Y; + vertex_pos[i].Z *= scale.Z; + vertex_pos[i] += pos + posRelative_f; + } + + f32 abs_scale = 1.; + if (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X; + else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y; + else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z; + + v3f zerovector = v3f(0,0,0); + + u8 li = decode_light(light); + //u8 li = 150; + + u8 alpha = 255; + + if(material == MATERIAL_WATER) + { + alpha = 128; + } + + video::SColor c = video::SColor(alpha,li,li,li); + + /*f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c, + core::vector2d<f32>(0,1)); + f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c, + core::vector2d<f32>(abs_scale,1)); + f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c, + core::vector2d<f32>(abs_scale,0)); + f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c, + core::vector2d<f32>(0,0));*/ + f->vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c, + core::vector2d<f32>(0,1)); + f->vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c, + core::vector2d<f32>(abs_scale,1)); + f->vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c, + core::vector2d<f32>(abs_scale,0)); + f->vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c, + core::vector2d<f32>(0,0)); + + f->material = material; + + return f; +} + +/* + Parameters must consist of air and !air. + Order doesn't matter. + + If either of the nodes doesn't exist, light is 0. +*/ +u8 MapBlock::getFaceLight(v3s16 p, v3s16 face_dir) +{ + try{ + MapNode n = getNodeParent(p); + MapNode n2 = getNodeParent(p + face_dir); + u8 light; + if(n.solidness() < n2.solidness()) + light = n.getLight(); + else + light = n2.getLight(); + + // Make some nice difference to different sides + if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1) + light = diminish_light(diminish_light(light)); + else if(face_dir.X == -1 || face_dir.Z == -1) + light = diminish_light(light); + + return light; + } + catch(InvalidPositionException &e) + { + return 0; + } +} + +/* + Gets node material from any place relative to block. + Returns MATERIAL_AIR if doesn't exist. +*/ +u8 MapBlock::getNodeMaterial(v3s16 p) +{ + try{ + MapNode n = getNodeParent(p); + return n.d; + } + catch(InvalidPositionException &e) + { + return MATERIAL_IGNORE; + } +} + +/* + startpos: + translate_dir: unit vector with only one of x, y or z + face_dir: unit vector with only one of x, y or z +*/ +void MapBlock::updateFastFaceRow(v3s16 startpos, + u16 length, + v3s16 translate_dir, + v3s16 face_dir, + core::list<FastFace*> &dest) +{ + /* + Precalculate some variables + */ + v3f translate_dir_f(translate_dir.X, translate_dir.Y, + translate_dir.Z); // floating point conversion + v3f face_dir_f(face_dir.X, face_dir.Y, + face_dir.Z); // floating point conversion + v3f posRelative_f(getPosRelative().X, getPosRelative().Y, + getPosRelative().Z); // floating point conversion + + v3s16 p = startpos; + /* + The light in the air lights the surface is taken from + the node that is air. + */ + u8 light = getFaceLight(p, face_dir); + + u16 continuous_materials_count = 0; + + u8 material0 = getNodeMaterial(p); + u8 material1 = getNodeMaterial(p + face_dir); + + for(u16 j=0; j<length; j++) + { + bool next_is_different = true; + + v3s16 p_next; + u8 material0_next = 0; + u8 material1_next = 0; + u8 light_next = 0; + + if(j != length - 1){ + p_next = p + translate_dir; + material0_next = getNodeMaterial(p_next); + material1_next = getNodeMaterial(p_next + face_dir); + light_next = getFaceLight(p_next, face_dir); + + if(material0_next == material0 + && material1_next == material1 + && light_next == light) + { + next_is_different = false; + } + } + + continuous_materials_count++; + + if(next_is_different) + { + /* + Create a face if there should be one + */ + u8 mf = face_materials(material0, material1); + + if(mf != 0) + { + // Floating point conversion of the position vector + v3f pf(p.X, p.Y, p.Z); + // Center point of face (kind of) + v3f sp = pf - ((f32)continuous_materials_count / 2. - 0.5) * translate_dir_f; + v3f scale(1,1,1); + if(translate_dir.X != 0){ + scale.X = continuous_materials_count; + } + if(translate_dir.Y != 0){ + scale.Y = continuous_materials_count; + } + if(translate_dir.Z != 0){ + scale.Z = continuous_materials_count; + } + + FastFace *f; + + // If node at sp (material0) is more solid + if(mf == 1) + { + f = makeFastFace(material0, light, + sp, face_dir_f, scale, + posRelative_f); + } + // If node at sp is less solid (mf == 2) + else + { + f = makeFastFace(material1, light, + sp+face_dir_f, -1*face_dir_f, scale, + posRelative_f); + } + dest.push_back(f); + } + + continuous_materials_count = 0; + material0 = material0_next; + material1 = material1_next; + light = light_next; + } + + p = p_next; + } +} + +void MapBlock::updateMesh() +{ + /*v3s16 p = getPosRelative(); + std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")" + <<"::updateMesh(): ";*/ + //<<"::updateMesh()"<<std::endl; + + /* + TODO: Change this to directly generate the mesh (and get rid + of FastFaces) + */ + + core::list<FastFace*> *fastfaces_new = new core::list<FastFace*>; + + /* + We are including the faces of the trailing edges of the block. + This means that when something changes, the caller must + also update the meshes of the blocks at the leading edges. + */ + + /* + Go through every y,z and get top faces in rows of x+ + */ + for(s16 y=0; y<MAP_BLOCKSIZE; y++){ + //for(s16 y=-1; y<MAP_BLOCKSIZE; y++){ + for(s16 z=0; z<MAP_BLOCKSIZE; z++){ + updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE, + v3s16(1,0,0), + v3s16(0,1,0), + *fastfaces_new); + } + } + /* + Go through every x,y and get right faces in rows of z+ + */ + for(s16 x=0; x<MAP_BLOCKSIZE; x++){ + //for(s16 x=-1; x<MAP_BLOCKSIZE; x++){ + for(s16 y=0; y<MAP_BLOCKSIZE; y++){ + updateFastFaceRow(v3s16(x,y,0), MAP_BLOCKSIZE, + v3s16(0,0,1), + v3s16(1,0,0), + *fastfaces_new); + } + } + /* + Go through every y,z and get back faces in rows of x+ + */ + for(s16 z=0; z<MAP_BLOCKSIZE; z++){ + //for(s16 z=-1; z<MAP_BLOCKSIZE; z++){ + for(s16 y=0; y<MAP_BLOCKSIZE; y++){ + updateFastFaceRow(v3s16(0,y,z), MAP_BLOCKSIZE, + v3s16(1,0,0), + v3s16(0,0,1), + *fastfaces_new); + } + } + + scene::SMesh *mesh_new = NULL; + + if(fastfaces_new->getSize() > 0) + { + mesh_new = new scene::SMesh(); + scene::IMeshBuffer *buf = NULL; + + core::list<FastFace*>::Iterator i = fastfaces_new->begin(); + + // MATERIAL_AIR shouldn't be used by any face + u8 material_in_use = MATERIAL_AIR; + + for(; i != fastfaces_new->end(); i++) + { + FastFace *f = *i; + + if(f->material != material_in_use || buf == NULL) + { + // Try to get a meshbuffer associated with the material + buf = mesh_new->getMeshBuffer(g_materials[f->material]); + // If not found, create one + if(buf == NULL) + { + // This is a "Standard MeshBuffer", + // it's a typedeffed CMeshBuffer<video::S3DVertex> + buf = new scene::SMeshBuffer(); + // Set material + ((scene::SMeshBuffer*)buf)->Material = g_materials[f->material]; + // Use VBO + //buf->setHardwareMappingHint(scene::EHM_STATIC); + // Add to mesh + mesh_new->addMeshBuffer(buf); + // Mesh grabbed it + buf->drop(); + } + material_in_use = f->material; + } + + u16 indices[] = {0,1,2,2,3,0}; + buf->append(f->vertices, 4, indices, 6); + } + + // Use VBO for mesh (this just would set this for ever buffer) + //mesh_new->setHardwareMappingHint(scene::EHM_STATIC); + + /*std::cout<<"MapBlock has "<<fastfaces_new->getSize()<<" faces " + <<"and uses "<<mesh_new->getMeshBufferCount() + <<" materials"<<std::endl;*/ + } + + // TODO: Get rid of the FastFace stage + core::list<FastFace*>::Iterator i; + i = fastfaces_new->begin(); + for(; i != fastfaces_new->end(); i++) + { + delete *i; + } + fastfaces_new->clear(); + delete fastfaces_new; + + /* + Replace the mesh + */ + + mesh_mutex.Lock(); + + scene::SMesh *mesh_old = mesh; + + mesh = mesh_new; + + if(mesh_old != NULL) + { + // Remove hardware buffers of meshbuffers of mesh + // NOTE: No way, this runs in a different thread and everything + /*u32 c = mesh_old->getMeshBufferCount(); + for(u32 i=0; i<c; i++) + { + IMeshBuffer *buf = mesh_old->getMeshBuffer(i); + }*/ + // Drop the mesh + mesh_old->drop(); + //delete mesh_old; + } + + mesh_mutex.Unlock(); + + //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl; +} + +/* + Propagates sunlight down through the block. + Doesn't modify nodes that are not affected by sunlight. + + Returns false if sunlight at bottom block is invalid + Returns true if bottom block doesn't exist. + + If there is a block above, continues from it. + If there is no block above, assumes there is sunlight, unless + is_underground is set. + + At the moment, all sunlighted nodes are added to light_sources. + TODO: This could be optimized. +*/ +bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources) +{ + // Whether the sunlight at the top of the bottom block is valid + bool block_below_is_valid = true; + + v3s16 pos_relative = getPosRelative(); + + for(s16 x=0; x<MAP_BLOCKSIZE; x++) + { + for(s16 z=0; z<MAP_BLOCKSIZE; z++) + { + bool no_sunlight = false; + bool no_top_block = false; + // Check if node above block has sunlight + try{ + MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z)); + if(n.getLight() != LIGHT_SUN) + { + /*if(is_underground) + { + no_sunlight = true; + }*/ + no_sunlight = true; + } + } + catch(InvalidPositionException &e) + { + no_top_block = true; + + // TODO: This makes over-ground roofed places sunlighted + // Assume sunlight, unless is_underground==true + if(is_underground) + { + no_sunlight = true; + } + + // TODO: There has to be some way to allow this behaviour + // As of now, it just makes everything dark. + // No sunlight here + //no_sunlight = true; + } + + /*std::cout<<"("<<x<<","<<z<<"): " + <<"no_top_block="<<no_top_block + <<", is_underground="<<is_underground + <<", no_sunlight="<<no_sunlight + <<std::endl;*/ + + s16 y = MAP_BLOCKSIZE-1; + + if(no_sunlight == false) + { + // Continue spreading sunlight downwards through transparent + // nodes + for(; y >= 0; y--) + { + v3s16 pos(x, y, z); + + MapNode &n = getNodeRef(pos); + + if(n.sunlight_propagates()) + { + n.setLight(LIGHT_SUN); + + light_sources.insert(pos_relative + pos, true); + } + else{ + break; + } + } + } + + bool sunlight_should_go_down = (y==-1); + + // Fill rest with black (only transparent ones) + for(; y >= 0; y--){ + v3s16 pos(x, y, z); + + MapNode &n = getNodeRef(pos); + + if(n.light_propagates()) + { + n.setLight(0); + } + else{ + break; + } + } + + /* + If the block below hasn't already been marked invalid: + + Check if the node below the block has proper sunlight at top. + If not, the block below is invalid. + + Ignore non-transparent nodes as they always have no light + */ + try + { + if(block_below_is_valid) + { + MapNode n = getNodeParent(v3s16(x, -1, z)); + if(n.light_propagates()) + { + if(n.getLight() == LIGHT_SUN + && sunlight_should_go_down == false) + block_below_is_valid = false; + else if(n.getLight() != LIGHT_SUN + && sunlight_should_go_down == true) + block_below_is_valid = false; + } + }//if + }//try + catch(InvalidPositionException &e) + { + /*std::cout<<"InvalidBlockException for bottom block node" + <<std::endl;*/ + // Just no block below, no need to panic. + } + } + } + + return block_below_is_valid; +} + +/* + Serialization +*/ + +void MapBlock::serialize(std::ostream &os, u8 version) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapBlock format not supported"); + + if(data == NULL) + { + throw SerializationError("ERROR: Not writing dummy block."); + } + + // These have no compression + if(version <= 3 || version == 5 || version == 6) + { + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + + u32 buflen = 1 + nodecount * MapNode::serializedLength(version); + SharedBuffer<u8> dest(buflen); + + dest[0] = is_underground; + for(u32 i=0; i<nodecount; i++) + { + u32 s = 1 + i * MapNode::serializedLength(version); + data[i].serialize(&dest[s], version); + } + + os.write((char*)*dest, dest.getSize()); + } + // All otherversions + else + { + /* + With compression. + Compress the materials and the params separately. + */ + + // First byte + os.write((char*)&is_underground, 1); + + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + + // Get and compress materials + SharedBuffer<u8> materialdata(nodecount); + for(u32 i=0; i<nodecount; i++) + { + materialdata[i] = data[i].d; + } + compress(materialdata, os, version); + + // Get and compress params + SharedBuffer<u8> paramdata(nodecount); + for(u32 i=0; i<nodecount; i++) + { + paramdata[i] = data[i].param; + } + compress(paramdata, os, version); + } +} + +void MapBlock::deSerialize(std::istream &is, u8 version) +{ + if(!ser_ver_supported(version)) + throw VersionMismatchException("ERROR: MapBlock format not supported"); + + // These have no compression + if(version <= 3 || version == 5 || version == 6) + { + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + char tmp; + is.read(&tmp, 1); + if(is.gcount() != 1) + throw SerializationError + ("MapBlock::deSerialize: no enough input data"); + is_underground = tmp; + for(u32 i=0; i<nodecount; i++) + { + s32 len = MapNode::serializedLength(version); + SharedBuffer<u8> d(len); + is.read((char*)*d, len); + if(is.gcount() != len) + throw SerializationError + ("MapBlock::deSerialize: no enough input data"); + data[i].deSerialize(*d, version); + } + } + // All other versions + else + { + u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; + + u8 t8; + is.read((char*)&t8, 1); + is_underground = t8; + + { + // Uncompress and set material data + std::ostringstream os(std::ios_base::binary); + decompress(is, os, version); + std::string s = os.str(); + if(s.size() != nodecount) + throw SerializationError + ("MapBlock::deSerialize: invalid format"); + for(u32 i=0; i<s.size(); i++) + { + data[i].d = s[i]; + } + } + { + // Uncompress and set param data + std::ostringstream os(std::ios_base::binary); + decompress(is, os, version); + std::string s = os.str(); + if(s.size() != nodecount) + throw SerializationError + ("MapBlock::deSerialize: invalid format"); + for(u32 i=0; i<s.size(); i++) + { + data[i].param = s[i]; + } + } + } +} + + +//END |