#include "mapblock.h"
#include "map.h"
-// For g_materials
+// For g_settings
#include "main.h"
#include "light.h"
#include <sstream>
-
/*
MapBlock
*/
+MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy):
+ m_parent(parent),
+ m_pos(pos),
+ m_modified(MOD_STATE_WRITE_NEEDED),
+ is_underground(false),
+ m_lighting_expired(true),
+ m_day_night_differs(false),
+ m_generated(false),
+ m_objects(this),
+ m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
+ m_usage_timer(0)
+{
+ data = NULL;
+ if(dummy == false)
+ reallocate();
+
+ //m_spawn_timer = -10000;
+
+#ifndef SERVER
+ m_mesh_expired = false;
+ mesh_mutex.Init();
+ mesh = NULL;
+ m_temp_mods_mutex.Init();
+#endif
+}
+
+MapBlock::~MapBlock()
+{
+#ifndef SERVER
+ {
+ JMutexAutoLock lock(mesh_mutex);
+
+ if(mesh)
+ {
+ mesh->drop();
+ mesh = NULL;
+ }
+ }
+#endif
+
+ if(data)
+ delete[] data;
+}
+
bool MapBlock::isValidPositionParent(v3s16 p)
{
if(isValidPosition(p))
}
}
-FastFace * MapBlock::makeFastFace(u8 material, u8 light, v3f p,
- v3f dir, v3f scale, v3f posRelative_f)
+MapNode MapBlock::getNodeParentNoEx(v3s16 p)
{
- 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 == CONTENT_WATER || material == CONTENT_OCEAN)
- {
- 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();*/
- if(n.getLight() > n2.getLight())
- 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 CONTENT_IGNORE if doesn't exist or should not be drawn.
-*/
-u8 MapBlock::getNodeTile(v3s16 p)
-{
- try{
- MapNode n = getNodeParent(p);
-
- return content_tile(n.d);
- }
- catch(InvalidPositionException &e)
- {
- return CONTENT_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_tiles_count = 0;
-
- u8 tile0 = getNodeTile(p);
- u8 tile1 = getNodeTile(p + face_dir);
-
- for(u16 j=0; j<length; j++)
+ if(isValidPosition(p) == false)
{
- bool next_is_different = true;
-
- v3s16 p_next;
- u8 tile0_next = 0;
- u8 tile1_next = 0;
- u8 light_next = 0;
-
- if(j != length - 1){
- p_next = p + translate_dir;
- tile0_next = getNodeTile(p_next);
- tile1_next = getNodeTile(p_next + face_dir);
- light_next = getFaceLight(p_next, face_dir);
-
- if(tile0_next == tile0
- && tile1_next == tile1
- && light_next == light)
- {
- next_is_different = false;
- }
- }
-
- continuous_tiles_count++;
-
- if(next_is_different)
- {
- /*
- Create a face if there should be one
- */
- u8 mf = face_contents(tile0, tile1);
-
- 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_tiles_count / 2. - 0.5) * translate_dir_f;
- v3f scale(1,1,1);
- if(translate_dir.X != 0){
- scale.X = continuous_tiles_count;
- }
- if(translate_dir.Y != 0){
- scale.Y = continuous_tiles_count;
- }
- if(translate_dir.Z != 0){
- scale.Z = continuous_tiles_count;
- }
-
- FastFace *f;
-
- // If node at sp (tile0) is more solid
- if(mf == 1)
- {
- f = makeFastFace(tile0, light,
- sp, face_dir_f, scale,
- posRelative_f);
- }
- // If node at sp is less solid (mf == 2)
- else
- {
- f = makeFastFace(tile1, light,
- sp+face_dir_f, -1*face_dir_f, scale,
- posRelative_f);
- }
- dest.push_back(f);
- }
-
- continuous_tiles_count = 0;
- tile0 = tile0_next;
- tile1 = tile1_next;
- light = light_next;
- }
-
- p = p_next;
- }
-}
-
-/*
- This is used because CMeshBuffer::append() is very slow
-*/
-struct PreMeshBuffer
-{
- video::SMaterial material;
- core::array<u16> indices;
- core::array<video::S3DVertex> vertices;
-};
-
-class MeshCollector
-{
-public:
- void append(
- video::SMaterial material,
- const video::S3DVertex* const vertices,
- u32 numVertices,
- const u16* const indices,
- u32 numIndices
- )
- {
- PreMeshBuffer *p = NULL;
- for(u32 i=0; i<m_prebuffers.size(); i++)
- {
- PreMeshBuffer &pp = m_prebuffers[i];
- if(pp.material != material)
- continue;
-
- p = &pp;
- break;
- }
-
- if(p == NULL)
- {
- PreMeshBuffer pp;
- pp.material = material;
- m_prebuffers.push_back(pp);
- p = &m_prebuffers[m_prebuffers.size()-1];
- }
-
- u32 vertex_count = p->vertices.size();
- for(u32 i=0; i<numIndices; i++)
- {
- u32 j = indices[i] + vertex_count;
- if(j > 65535)
- {
- dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
- // NOTE: Fix is to just add an another MeshBuffer
- }
- p->indices.push_back(j);
+ try{
+ return m_parent->getNode(getPosRelative() + p);
}
- for(u32 i=0; i<numVertices; i++)
+ catch(InvalidPositionException &e)
{
- p->vertices.push_back(vertices[i]);
+ return MapNode(CONTENT_IGNORE);
}
}
-
- void fillMesh(scene::SMesh *mesh)
+ else
{
- /*dstream<<"Filling mesh with "<<m_prebuffers.size()
- <<" meshbuffers"<<std::endl;*/
- for(u32 i=0; i<m_prebuffers.size(); i++)
+ if(data == NULL)
{
- PreMeshBuffer &p = m_prebuffers[i];
-
- /*dstream<<"p.vertices.size()="<<p.vertices.size()
- <<", p.indices.size()="<<p.indices.size()
- <<std::endl;*/
-
- // Create meshbuffer
-
- // This is a "Standard MeshBuffer",
- // it's a typedeffed CMeshBuffer<video::S3DVertex>
- scene::SMeshBuffer *buf = new scene::SMeshBuffer();
- // Set material
- buf->Material = p.material;
- //((scene::SMeshBuffer*)buf)->Material = p.material;
- // Use VBO
- //buf->setHardwareMappingHint(scene::EHM_STATIC);
- // Add to mesh
- mesh->addMeshBuffer(buf);
- // Mesh grabbed it
- buf->drop();
-
- buf->append(p.vertices.pointer(), p.vertices.size(),
- p.indices.pointer(), p.indices.size());
+ return MapNode(CONTENT_IGNORE);
}
+ return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
}
+}
-private:
- core::array<PreMeshBuffer> m_prebuffers;
-};
+#ifndef SERVER
-void MapBlock::updateMesh()
+#if 1
+void MapBlock::updateMesh(u32 daynight_ratio)
{
- /*v3s16 p = getPosRelative();
- std::cout<<"MapBlock("<<p.X<<","<<p.Y<<","<<p.Z<<")"
- <<"::updateMesh(): ";*/
- //<<"::updateMesh()"<<std::endl;
-
+#if 0
/*
- TODO: Change this to directly generate the mesh (and get rid
- of FastFaces)
+ DEBUG: If mesh has been generated, don't generate it again
*/
-
- 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);
- }
+ {
+ JMutexAutoLock meshlock(mesh_mutex);
+ if(mesh != NULL)
+ return;
}
+#endif
- scene::SMesh *mesh_new = NULL;
-
- mesh_new = new scene::SMesh();
+ MeshMakeData data;
+ data.fill(daynight_ratio, this);
- if(fastfaces_new->getSize() > 0)
- {
- MeshCollector collector;
-
- core::list<FastFace*>::Iterator i = fastfaces_new->begin();
-
- for(; i != fastfaces_new->end(); i++)
- {
- FastFace *f = *i;
-
- const u16 indices[] = {0,1,2,2,3,0};
-
- collector.append(g_materials[f->material], f->vertices, 4,
- indices, 6);
- }
-
- collector.fillMesh(mesh_new);
-
- // 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 (meshbuffers)"<<std::endl;*/
- }
+ scene::SMesh *mesh_new = makeMapBlockMesh(&data);
/*
- Clear temporary FastFaces
- */
-
- core::list<FastFace*>::Iterator i;
- i = fastfaces_new->begin();
- for(; i != fastfaces_new->end(); i++)
- {
- delete *i;
- }
- fastfaces_new->clear();
- delete fastfaces_new;
-
- /*
- Add special graphics:
- - torches
-
- TODO: Optimize by using same meshbuffer for same textures
- */
-
- /*scene::ISceneManager *smgr = NULL;
- video::IVideoDriver* driver = NULL;
- if(g_device)
- {
- smgr = g_device->getSceneManager();
- driver = smgr->getVideoDriver();
- }*/
-
- for(s16 z=0; z<MAP_BLOCKSIZE; z++)
- for(s16 y=0; y<MAP_BLOCKSIZE; y++)
- for(s16 x=0; x<MAP_BLOCKSIZE; x++)
- {
- v3s16 p(x,y,z);
-
- MapNode &n = getNodeRef(x,y,z);
-
- if(n.d == CONTENT_LIGHT)
- {
- //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
- scene::SMeshBuffer *buf = new scene::SMeshBuffer();
- video::SColor c(255,255,255,255);
-
- video::S3DVertex vertices[4] =
- {
- video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
- video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
- video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
- video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
- };
-
- v3s16 dir = unpackDir(n.dir);
-
- for(s32 i=0; i<4; i++)
- {
- if(dir == v3s16(1,0,0))
- vertices[i].Pos.rotateXZBy(0);
- if(dir == v3s16(-1,0,0))
- vertices[i].Pos.rotateXZBy(180);
- if(dir == v3s16(0,0,1))
- vertices[i].Pos.rotateXZBy(90);
- if(dir == v3s16(0,0,-1))
- vertices[i].Pos.rotateXZBy(-90);
- if(dir == v3s16(0,-1,0))
- vertices[i].Pos.rotateXZBy(45);
- if(dir == v3s16(0,1,0))
- vertices[i].Pos.rotateXZBy(-45);
-
- vertices[i].Pos += intToFloat(p + getPosRelative());
- }
-
- u16 indices[] = {0,1,2,2,3,0};
- buf->append(vertices, 4, indices, 6);
-
- // Set material
- buf->getMaterial().setFlag(video::EMF_LIGHTING, false);
- buf->getMaterial().setFlag(video::EMF_BACK_FACE_CULLING, false);
- buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false);
- //buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
- buf->getMaterial().MaterialType
- = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
- if(dir == v3s16(0,-1,0))
- buf->getMaterial().setTexture(0,
- g_texturecache.get("torch_on_floor"));
- else if(dir == v3s16(0,1,0))
- buf->getMaterial().setTexture(0,
- g_texturecache.get("torch_on_ceiling"));
- // For backwards compatibility
- else if(dir == v3s16(0,0,0))
- buf->getMaterial().setTexture(0,
- g_texturecache.get("torch_on_floor"));
- else
- buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
-
- // Add to mesh
- mesh_new->addMeshBuffer(buf);
- buf->drop();
- }
- }
-
- /*
- Do some stuff to the mesh
- */
-
- mesh_new->recalculateBoundingBox();
-
- /*
- Delete new mesh if it is empty
+ Replace the mesh
*/
- if(mesh_new->getMeshBufferCount() == 0)
- {
- mesh_new->drop();
- mesh_new = NULL;
- }
+ replaceMesh(mesh_new);
- /*
- Replace the mesh
- */
+}
+#endif
+void MapBlock::replaceMesh(scene::SMesh *mesh_new)
+{
mesh_mutex.Lock();
- scene::SMesh *mesh_old = mesh;
+ //scene::SMesh *mesh_old = mesh[daynight_i];
+ //mesh[daynight_i] = mesh_new;
+ scene::SMesh *mesh_old = mesh;
mesh = mesh_new;
+ setMeshExpired(false);
if(mesh_old != NULL)
{
{
IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
}*/
+
+ /*dstream<<"mesh_old->getReferenceCount()="
+ <<mesh_old->getReferenceCount()<<std::endl;
+ u32 c = mesh_old->getMeshBufferCount();
+ for(u32 i=0; i<c; i++)
+ {
+ scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
+ dstream<<"buf->getReferenceCount()="
+ <<buf->getReferenceCount()<<std::endl;
+ }*/
+
// Drop the mesh
mesh_old->drop();
+
//delete mesh_old;
}
mesh_mutex.Unlock();
-
- //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
}
+
+#endif // !SERVER
/*
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 false if sunlight at bottom block is invalid.
+ Returns true if sunlight at bottom block is valid.
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.
+ is_underground is set or highest node is water.
+
+ All sunlighted nodes are added to light_sources.
- At the moment, all sunlighted nodes are added to light_sources.
- TODO: This could be optimized.
+ if remove_light==true, sets non-sunlighted nodes black.
+
+ if black_air_left!=NULL, it is set to true if non-sunlighted
+ air is left in block.
*/
-bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources)
+bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
+ bool remove_light, bool *black_air_left)
{
// Whether the sunlight at the top of the bottom block is valid
bool block_below_is_valid = true;
{
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
{
+#if 1
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(n.d == CONTENT_IGNORE || n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
{
- /*if(is_underground)
- {
- no_sunlight = true;
- }*/
no_sunlight = true;
}
}
{
no_top_block = true;
- // TODO: This makes over-ground roofed places sunlighted
+ // NOTE: 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.
+ else
+ {
+ MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
+ //if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+ if(content_features(n.d).sunlight_propagates == false)
+ {
+ no_sunlight = true;
+ }
+ }
+ // NOTE: As of now, this just would make everything dark.
// No sunlight here
//no_sunlight = true;
}
+#endif
+#if 0 // Doesn't work; nothing gets light.
+ bool no_sunlight = true;
+ bool no_top_block = false;
+ // Check if node above block has sunlight
+ try{
+ MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
+ if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
+ {
+ no_sunlight = false;
+ }
+ }
+ catch(InvalidPositionException &e)
+ {
+ no_top_block = true;
+ }
+#endif
/*std::cout<<"("<<x<<","<<z<<"): "
<<"no_top_block="<<no_top_block
s16 y = MAP_BLOCKSIZE-1;
- if(no_sunlight == false)
+ // This makes difference to diminishing in water.
+ bool stopped_to_solid_object = false;
+
+ u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
+
+ for(; y >= 0; y--)
{
- // Continue spreading sunlight downwards through transparent
- // nodes
- for(; y >= 0; y--)
+ v3s16 pos(x, y, z);
+ MapNode &n = getNodeRef(pos);
+
+ if(current_light == 0)
{
- v3s16 pos(x, y, z);
-
- MapNode &n = getNodeRef(pos);
-
- if(n.sunlight_propagates())
+ // Do nothing
+ }
+ else if(current_light == LIGHT_SUN && n.sunlight_propagates())
+ {
+ // Do nothing: Sunlight is continued
+ }
+ else if(n.light_propagates() == false)
+ {
+ /*// DEPRECATED TODO: REMOVE
+ if(grow_grass)
{
- n.setLight(LIGHT_SUN);
+ bool upper_is_air = false;
+ try
+ {
+ if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
+ upper_is_air = true;
+ }
+ catch(InvalidPositionException &e)
+ {
+ }
+ // Turn mud into grass
+ if(upper_is_air && n.d == CONTENT_MUD
+ && current_light == LIGHT_SUN)
+ {
+ n.d = CONTENT_GRASS;
+ }
+ }*/
- light_sources.insert(pos_relative + pos, true);
- }
- else{
- break;
- }
+ // A solid object is on the way.
+ stopped_to_solid_object = true;
+
+ // Light stops.
+ current_light = 0;
+ }
+ else
+ {
+ // Diminish light
+ current_light = diminish_light(current_light);
}
- }
- bool sunlight_should_go_down = (y==-1);
+ u8 old_light = n.getLight(LIGHTBANK_DAY);
- // Fill rest with black (only transparent ones)
- for(; y >= 0; y--){
- v3s16 pos(x, y, z);
+ if(current_light > old_light || remove_light)
+ {
+ n.setLight(LIGHTBANK_DAY, current_light);
+ }
- MapNode &n = getNodeRef(pos);
-
- if(n.light_propagates())
+ if(diminish_light(current_light) != 0)
{
- n.setLight(0);
+ light_sources.insert(pos_relative + pos, true);
}
- else{
- break;
+
+ if(current_light == 0 && stopped_to_solid_object)
+ {
+ if(black_air_left)
+ {
+ *black_air_left = true;
+ }
}
}
+ // Whether or not the block below should see LIGHT_SUN
+ bool sunlight_should_go_down = (current_light == LIGHT_SUN);
+
/*
If the block below hasn't already been marked invalid:
MapNode n = getNodeParent(v3s16(x, -1, z));
if(n.light_propagates())
{
- if(n.getLight() == LIGHT_SUN
+ if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
&& sunlight_should_go_down == false)
block_below_is_valid = false;
- else if(n.getLight() != LIGHT_SUN
+ else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
&& sunlight_should_go_down == true)
block_below_is_valid = false;
}
return block_below_is_valid;
}
+
void MapBlock::copyTo(VoxelManipulator &dst)
{
v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
+ // Copy from data to VoxelManipulator
dst.copyFrom(data, data_area, v3s16(0,0,0),
getPosRelative(), data_size);
}
-/*void getPseudoObjects(v3f origin, f32 max_d,
- core::array<DistanceSortedObject> &dest)
+void MapBlock::copyFrom(VoxelManipulator &dst)
{
-}*/
+ v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
+ VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
+
+ // Copy from VoxelManipulator to data
+ dst.copyTo(data, data_area, v3s16(0,0,0),
+ getPosRelative(), data_size);
+}
+
+void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
+{
+ /*
+ Step objects
+ */
+ m_objects.step(dtime, server, daynight_ratio);
+
+ setChangedFlag();
+}
+
+
+void MapBlock::updateDayNightDiff()
+{
+ if(data == NULL)
+ {
+ m_day_night_differs = false;
+ return;
+ }
+
+ bool differs = false;
+
+ /*
+ Check if any lighting value differs
+ */
+ for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+ {
+ MapNode &n = data[i];
+ if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
+ {
+ differs = true;
+ break;
+ }
+ }
+
+ /*
+ If some lighting values differ, check if the whole thing is
+ just air. If it is, differ = false
+ */
+ if(differs)
+ {
+ bool only_air = true;
+ for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+ {
+ MapNode &n = data[i];
+ if(n.d != CONTENT_AIR)
+ {
+ only_air = false;
+ break;
+ }
+ }
+ if(only_air)
+ differs = false;
+ }
+
+ // Set member variable
+ m_day_night_differs = differs;
+}
+
+s16 MapBlock::getGroundLevel(v2s16 p2d)
+{
+ if(isDummy())
+ return -3;
+ try
+ {
+ s16 y = MAP_BLOCKSIZE-1;
+ for(; y>=0; y--)
+ {
+ //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
+ if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
+ {
+ if(y == MAP_BLOCKSIZE-1)
+ return -2;
+ else
+ return y;
+ }
+ }
+ return -1;
+ }
+ catch(InvalidPositionException &e)
+ {
+ return -3;
+ }
+}
/*
Serialization
os.write((char*)*dest, dest.getSize());
}
- // All otherversions
- else
+ else if(version <= 10)
{
/*
With compression.
}
compress(materialdata, os, version);
- // Get and compress params
- SharedBuffer<u8> paramdata(nodecount);
+ // Get and compress lights
+ SharedBuffer<u8> lightdata(nodecount);
for(u32 i=0; i<nodecount; i++)
{
- paramdata[i] = data[i].param;
+ lightdata[i] = data[i].param;
}
- compress(paramdata, os, version);
+ compress(lightdata, os, version);
if(version >= 10)
{
- // Get and compress pressure
- SharedBuffer<u8> pressuredata(nodecount);
+ // Get and compress param2
+ SharedBuffer<u8> param2data(nodecount);
for(u32 i=0; i<nodecount; i++)
{
- pressuredata[i] = data[i].pressure;
+ param2data[i] = data[i].param2;
+ }
+ compress(param2data, os, version);
+ }
+ }
+ // All other versions (newest)
+ else
+ {
+ // First byte
+ u8 flags = 0;
+ if(is_underground)
+ flags |= 0x01;
+ if(m_day_night_differs)
+ flags |= 0x02;
+ if(m_lighting_expired)
+ flags |= 0x04;
+ if(version >= 18)
+ {
+ if(m_generated == false)
+ flags |= 0x08;
+ }
+ os.write((char*)&flags, 1);
+
+ u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
+
+ /*
+ Get data
+ */
+
+ SharedBuffer<u8> databuf(nodecount*3);
+
+ // Get contents
+ for(u32 i=0; i<nodecount; i++)
+ {
+ databuf[i] = data[i].d;
+ }
+
+ // Get params
+ for(u32 i=0; i<nodecount; i++)
+ {
+ databuf[i+nodecount] = data[i].param;
+ }
+
+ // Get param2
+ for(u32 i=0; i<nodecount; i++)
+ {
+ databuf[i+nodecount*2] = data[i].param2;
+ }
+
+ /*
+ Compress data to output stream
+ */
+
+ compress(databuf, os, version);
+
+ /*
+ NodeMetadata
+ */
+ if(version >= 14)
+ {
+ if(version <= 15)
+ {
+ try{
+ std::ostringstream oss(std::ios_base::binary);
+ m_node_metadata.serialize(oss);
+ os<<serializeString(oss.str());
+ }
+ // This will happen if the string is longer than 65535
+ catch(SerializationError &e)
+ {
+ // Use an empty string
+ os<<serializeString("");
+ }
+ }
+ else
+ {
+ std::ostringstream oss(std::ios_base::binary);
+ m_node_metadata.serialize(oss);
+ compressZlib(oss.str(), os);
+ //os<<serializeLongString(oss.str());
}
- compress(pressuredata, os, version);
}
}
}
if(!ser_ver_supported(version))
throw VersionMismatchException("ERROR: MapBlock format not supported");
+ // These have no lighting info
+ if(version <= 1)
+ {
+ setLightingExpired(true);
+ }
+
+ // These have no "generated" field
+ if(version < 18)
+ {
+ m_generated = true;
+ }
+
// These have no compression
if(version <= 3 || version == 5 || version == 6)
{
data[i].deSerialize(*d, version);
}
}
- // All other versions
- else
+ else if(version <= 10)
{
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
if(version >= 10)
{
- // Uncompress and set pressure data
+ // Uncompress and set param2 data
std::ostringstream os(std::ios_base::binary);
decompress(is, os, version);
std::string s = os.str();
("MapBlock::deSerialize: invalid format");
for(u32 i=0; i<s.size(); i++)
{
- data[i].pressure = s[i];
+ data[i].param2 = s[i];
+ }
+ }
+ }
+ // All other versions (newest)
+ else
+ {
+ u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
+
+ u8 flags;
+ is.read((char*)&flags, 1);
+ is_underground = (flags & 0x01) ? true : false;
+ m_day_night_differs = (flags & 0x02) ? true : false;
+ m_lighting_expired = (flags & 0x04) ? true : false;
+ if(version >= 18)
+ m_generated = (flags & 0x08) ? false : true;
+
+ // Uncompress data
+ std::ostringstream os(std::ios_base::binary);
+ decompress(is, os, version);
+ std::string s = os.str();
+ if(s.size() != nodecount*3)
+ throw SerializationError
+ ("MapBlock::deSerialize: decompress resulted in size"
+ " other than nodecount*3");
+
+ // Set contents
+ for(u32 i=0; i<nodecount; i++)
+ {
+ data[i].d = s[i];
+ }
+ // Set params
+ for(u32 i=0; i<nodecount; i++)
+ {
+ data[i].param = s[i+nodecount];
+ }
+ // Set param2
+ for(u32 i=0; i<nodecount; i++)
+ {
+ data[i].param2 = s[i+nodecount*2];
+ }
+
+ /*
+ NodeMetadata
+ */
+ if(version >= 14)
+ {
+ // Ignore errors
+ try{
+ if(version <= 15)
+ {
+ std::string data = deSerializeString(is);
+ std::istringstream iss(data, std::ios_base::binary);
+ m_node_metadata.deSerialize(iss);
+ }
+ else
+ {
+ //std::string data = deSerializeLongString(is);
+ std::ostringstream oss(std::ios_base::binary);
+ decompressZlib(is, oss);
+ std::istringstream iss(oss.str(), std::ios_base::binary);
+ m_node_metadata.deSerialize(iss);
+ }
+ }
+ catch(SerializationError &e)
+ {
+ dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
+ <<" while deserializing node metadata"<<std::endl;
}
}
}
+
+ /*
+ Translate nodes as specified in the translate_to fields of
+ node features
+
+ NOTE: This isn't really used. Should it be removed?
+ */
+ for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
+ {
+ MapNode &n = data[i];
+
+ MapNode *translate_to = content_features(n.d).translate_to;
+ if(translate_to)
+ {
+ dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
+ <<translate_to->d<<std::endl;
+ n = *translate_to;
+ }
+ }
+}
+
+void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
+{
+ // Versions up from 9 have block objects.
+ if(version >= 9)
+ {
+ //serializeObjects(os, version); // DEPRECATED
+ // count=0
+ writeU16(os, 0);
+ }
+
+ // Versions up from 15 have static objects.
+ if(version >= 15)
+ {
+ m_static_objects.serialize(os);
+ }
+
+ // Timestamp
+ if(version >= 17)
+ {
+ writeU32(os, getTimestamp());
+ }
+}
+
+void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
+{
+ /*
+ Versions up from 9 have block objects.
+ */
+ if(version >= 9)
+ {
+ updateObjects(is, version, NULL, 0);
+ }
+
+ /*
+ Versions up from 15 have static objects.
+ */
+ if(version >= 15)
+ {
+ m_static_objects.deSerialize(is);
+ }
+
+ // Timestamp
+ if(version >= 17)
+ {
+ setTimestamp(readU32(is));
+ }
+ else
+ {
+ setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
+ }
}