#include "mapblock.h"
#include "map.h"
-// For g_materials
+// For g_settings and g_irrlicht
#include "main.h"
#include "light.h"
#include <sstream>
m_pos(pos),
changed(true),
is_underground(false),
- m_mesh_expired(false),
m_day_night_differs(false),
m_objects(this)
{
data = NULL;
if(dummy == false)
reallocate();
+
+ m_spawn_timer = -10000;
+#ifndef SERVER
+ m_mesh_expired = false;
mesh_mutex.Init();
-
mesh = NULL;
- /*for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
- {
- mesh[i] = NULL;
- }*/
+ m_temp_mods_mutex.Init();
+#endif
}
MapBlock::~MapBlock()
{
+#ifndef SERVER
{
JMutexAutoLock lock(mesh_mutex);
mesh->drop();
mesh = NULL;
}
- /*for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
- {
- if(mesh[i] != NULL)
- {
- mesh[i]->drop();
- mesh[i] = NULL;
- }
- }*/
}
+#endif
if(data)
delete[] data;
}
}
+/*
+ Parameters must consist of air and !air.
+ Order doesn't matter.
+
+ If either of the nodes doesn't exist, light is 0.
+
+ parameters:
+ daynight_ratio: 0...1000
+ n: getNodeParent(p)
+ n2: getNodeParent(p + face_dir)
+ face_dir: axis oriented unit vector from p to p2
+
+ returns encoded light value.
+*/
+u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
+ v3s16 face_dir)
+{
+ try{
+ // DEBUG
+ /*{
+ if(n.d == CONTENT_WATER)
+ {
+ u8 l = n.param2*2;
+ if(l > LIGHT_MAX)
+ l = LIGHT_MAX;
+ return l;
+ }
+ if(n2.d == CONTENT_WATER)
+ {
+ u8 l = n2.param2*2;
+ if(l > LIGHT_MAX)
+ l = LIGHT_MAX;
+ return l;
+ }
+ }*/
+
+
+ u8 light;
+ u8 l1 = n.getLightBlend(daynight_ratio);
+ u8 l2 = n2.getLightBlend(daynight_ratio);
+ if(l1 > l2)
+ light = l1;
+ else
+ light = l2;
+
+ // 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);*/
+
+ if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
+ light = diminish_light(diminish_light(light));
+ else if(face_dir.Z == 1 || face_dir.Z == -1)
+ light = diminish_light(light);
+
+ return light;
+ }
+ catch(InvalidPositionException &e)
+ {
+ return 0;
+ }
+}
+
+#ifndef SERVER
+
void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
v3s16 dir, v3f scale, v3f posRelative_f,
core::array<FastFace> &dest)
v3f zerovector = v3f(0,0,0);
- u8 li = decode_light(light);
- //u8 li = 150;
-
- u8 alpha = 255;
+ //u8 li = decode_light(light);
+ u8 li = light;
+ //u8 li = 255; //DEBUG
+ u8 alpha = tile.alpha;
+ /*u8 alpha = 255;
if(tile.id == TILE_WATER)
- {
- alpha = 128;
- }
+ alpha = WATER_ALPHA;*/
video::SColor c = video::SColor(alpha,li,li,li);
//return f;
}
-/*
- Parameters must consist of air and !air.
- Order doesn't matter.
-
- If either of the nodes doesn't exist, light is 0.
-
- parameters:
- daynight_ratio: 0...1000
- n: getNodeParent(p)
- n2: getNodeParent(p + face_dir)
- face_dir: axis oriented unit vector from p to p2
-*/
-u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
- v3s16 face_dir)
-{
- try{
- u8 light;
- u8 l1 = n.getLightBlend(daynight_ratio);
- u8 l2 = n2.getLightBlend(daynight_ratio);
- if(l1 > l2)
- light = l1;
- else
- light = l2;
-
- // 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);*/
-
- if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
- light = diminish_light(diminish_light(light));
- else if(face_dir.Z == 1 || face_dir.Z == -1)
- light = diminish_light(light);
-
- return light;
- }
- catch(InvalidPositionException &e)
- {
- return 0;
- }
-}
-
/*
Gets node tile from any place relative to block.
Returns TILE_NODE if doesn't exist or should not be drawn.
*/
-TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
+TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
+ NodeModMap &temp_mods)
{
TileSpec spec;
-
- /*//DEBUG
- {
- spec.id = TILE_STONE;
- return spec;
- }*/
-
- spec.feature = TILEFEAT_NONE;
- //spec.id = TILE_STONE;
- spec.id = mn.getTile(face_dir);
-
+ spec = mn.getTile(face_dir);
+
/*
Check temporary modifications on this node
*/
- core::map<v3s16, NodeMod>::Node *n;
+ /*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
-
// If modified
if(n != NULL)
{
- struct NodeMod mod = n->getValue();
+ struct NodeMod mod = n->getValue();*/
+ NodeMod mod;
+ if(temp_mods.get(p, &mod))
+ {
if(mod.type == NODEMOD_CHANGECONTENT)
{
- spec.id = content_tile(mod.param, face_dir);
+ MapNode mn2(mod.param);
+ spec = mn2.getTile(face_dir);
}
if(mod.type == NODEMOD_CRACK)
{
+ std::ostringstream os;
+ os<<"[crack"<<mod.param;
+
+ textureid_t tid = g_irrlicht->getTextureId(os.str());
+ spec.spec.addTid(tid);
}
}
return spec;
}
-u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
+u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
{
/*
Check temporary modifications on this node
*/
- core::map<v3s16, NodeMod>::Node *n;
+ /*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
-
// If modified
if(n != NULL)
{
- struct NodeMod mod = n->getValue();
+ struct NodeMod mod = n->getValue();*/
+ NodeMod mod;
+ if(temp_mods.get(p, &mod))
+ {
if(mod.type == NODEMOD_CHANGECONTENT)
{
// Overrides content
v3f translate_dir_f,
v3s16 face_dir,
v3f face_dir_f,
- core::array<FastFace> &dest)
+ core::array<FastFace> &dest,
+ NodeModMap &temp_mods)
{
v3s16 p = startpos;
u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
- TileSpec tile0 = getNodeTile(n0, p, face_dir);
- TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
+ TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
+ TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
for(u16 j=0; j<length; j++)
{
p_next = p + translate_dir;
n0_next = getNodeParentNoEx(p_next);
n1_next = getNodeParentNoEx(p_next + face_dir);
- tile0_next = getNodeTile(n0_next, p_next, face_dir);
- tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
+ tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
+ tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
if(tile0_next == tile0
*/
//u8 mf = face_contents(tile0, tile1);
// This is hackish
- u8 content0 = getNodeContent(p, n0);
- u8 content1 = getNodeContent(p + face_dir, n1);
+ u8 content0 = getNodeContent(p, n0, temp_mods);
+ u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
u8 mf = face_contents(content0, content1);
if(mf != 0)
// If node at sp (tile0) is more solid
if(mf == 1)
{
- makeFastFace(tile0, light,
+ makeFastFace(tile0, decode_light(light),
sp, face_dir, scale,
posRelative_f, dest);
}
// If node at sp is less solid (mf == 2)
else
{
- makeFastFace(tile1, light,
+ makeFastFace(tile1, decode_light(light),
sp+face_dir_f, -face_dir, scale,
posRelative_f, dest);
}
}
#endif
- // 4-21ms
- //TimeTaker timer1("updateMesh()", g_device);
+ // 4-21ms for MAP_BLOCKSIZE=16
+ // 24-155ms for MAP_BLOCKSIZE=32
+ //TimeTaker timer1("updateMesh()");
core::array<FastFace> fastfaces_new;
v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
getPosRelative().Z); // floating point conversion
+ /*
+ Avoid interlocks by copying m_temp_mods
+ */
+ NodeModMap temp_mods;
+ {
+ JMutexAutoLock lock(m_temp_mods_mutex);
+ m_temp_mods.copy(temp_mods);
+ }
+
+ bool new_style_water = g_settings.getBool("new_style_water");
+ float node_water_level = 1.0;
+ if(new_style_water)
+ node_water_level = 0.8;
+
/*
We are including the faces of the trailing edges of the block.
This means that when something changes, the caller must
NOTE: This is the slowest part of this method.
*/
+
+ {
+ // 4-23ms for MAP_BLOCKSIZE=16
+ //TimeTaker timer2("updateMesh() collect");
- /*
- Go through every y,z and get top faces in rows of x+
- */
- for(s16 y=0; y<MAP_BLOCKSIZE; y++){
- for(s16 z=0; z<MAP_BLOCKSIZE; z++){
- updateFastFaceRow(daynight_ratio, posRelative_f,
- v3s16(0,y,z), MAP_BLOCKSIZE,
- v3s16(1,0,0), //dir
- v3f (1,0,0),
- v3s16(0,1,0), //face dir
- v3f (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++){
+ /*
+ Go through every y,z and get top faces in rows of x+
+ */
for(s16 y=0; y<MAP_BLOCKSIZE; y++){
- updateFastFaceRow(daynight_ratio, posRelative_f,
- v3s16(x,y,0), MAP_BLOCKSIZE,
- v3s16(0,0,1),
- v3f (0,0,1),
- v3s16(1,0,0),
- v3f (1,0,0),
- fastfaces_new);
+ for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+ updateFastFaceRow(daynight_ratio, posRelative_f,
+ v3s16(0,y,z), MAP_BLOCKSIZE,
+ v3s16(1,0,0), //dir
+ v3f (1,0,0),
+ v3s16(0,1,0), //face dir
+ v3f (0,1,0),
+ fastfaces_new,
+ temp_mods);
+ }
}
- }
- /*
- Go through every y,z and get back faces in rows of x+
- */
- for(s16 z=0; z<MAP_BLOCKSIZE; z++){
- for(s16 y=0; y<MAP_BLOCKSIZE; y++){
- updateFastFaceRow(daynight_ratio, posRelative_f,
- v3s16(0,y,z), MAP_BLOCKSIZE,
- v3s16(1,0,0),
- v3f (1,0,0),
- v3s16(0,0,1),
- v3f (0,0,1),
- 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 y=0; y<MAP_BLOCKSIZE; y++){
+ updateFastFaceRow(daynight_ratio, posRelative_f,
+ v3s16(x,y,0), MAP_BLOCKSIZE,
+ v3s16(0,0,1),
+ v3f (0,0,1),
+ v3s16(1,0,0),
+ v3f (1,0,0),
+ fastfaces_new,
+ temp_mods);
+ }
+ }
+ /*
+ Go through every y,z and get back faces in rows of x+
+ */
+ for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+ for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+ updateFastFaceRow(daynight_ratio, posRelative_f,
+ v3s16(0,y,z), MAP_BLOCKSIZE,
+ v3s16(1,0,0),
+ v3f (1,0,0),
+ v3s16(0,0,1),
+ v3f (0,0,1),
+ fastfaces_new,
+ temp_mods);
+ }
}
}
mesh_new = new scene::SMesh();
+ MeshCollector collector;
+
if(fastfaces_new.size() > 0)
{
- MeshCollector collector;
+ // avg 0ms (100ms spikes when loading textures the first time)
+ //TimeTaker timer2("updateMesh() mesh building");
+
+ video::SMaterial material;
+ material.Lighting = false;
+ material.BackfaceCulling = false;
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
+ material.setFlag(video::EMF_FOG_ENABLE, true);
for(u32 i=0; i<fastfaces_new.size(); i++)
{
FastFace &f = fastfaces_new[i];
const u16 indices[] = {0,1,2,2,3,0};
-
- if(f.tile.feature == TILEFEAT_NONE)
- {
- collector.append(g_tile_materials[f.tile.id], f.vertices, 4,
- indices, 6);
- }
- else
- {
- // Not implemented
- assert(0);
- }
- }
-
- 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;*/
- }
- /*
- Clear temporary FastFaces
- */
+ video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
+ if(texture == NULL)
+ continue;
- /*core::list<FastFace*>::Iterator i;
- i = fastfaces_new->begin();
- for(; i != fastfaces_new->end(); i++)
- {
- delete *i;
+ material.setTexture(0, texture);
+ if(f.tile.alpha != 255)
+ material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+ else
+ material.MaterialType = video::EMT_SOLID;
+
+ collector.append(material, f.vertices, 4, indices, 6);
+ }
}
- fastfaces_new->clear();
- delete fastfaces_new;*/
/*
Add special graphics:
- torches
-
- TODO: Optimize by using same meshbuffer for same textures
+ - flowing water
*/
- /*scene::ISceneManager *smgr = NULL;
- video::IVideoDriver* driver = NULL;
- if(g_device)
- {
- smgr = g_device->getSceneManager();
- driver = smgr->getVideoDriver();
- }*/
-
+ // 0ms
+ //TimeTaker timer2("updateMesh() adding special stuff");
+
+ // Flowing water material
+ video::SMaterial material_w1;
+ material_w1.setFlag(video::EMF_LIGHTING, false);
+ material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
+ material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material_w1.setFlag(video::EMF_FOG_ENABLE, true);
+ material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
+ material_w1.setTexture(0,
+ g_irrlicht->getTexture("water.png"));
+
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
for(s16 y=0; y<MAP_BLOCKSIZE; y++)
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
MapNode &n = getNodeRef(x,y,z);
+ /*
+ Add torches to mesh
+ */
if(n.d == CONTENT_TORCH)
{
- //scene::IMeshBuffer *buf = new scene::SMeshBuffer();
- scene::SMeshBuffer *buf = new scene::SMeshBuffer();
video::SColor c(255,255,255,255);
video::S3DVertex vertices[4] =
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::SMaterial material;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, false);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+ material.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
if(dir == v3s16(0,-1,0))
- buf->getMaterial().setTexture(0,
- g_texturecache.get("torch_on_floor"));
+ material.setTexture(0,
+ g_irrlicht->getTexture("torch_on_floor.png"));
else if(dir == v3s16(0,1,0))
- buf->getMaterial().setTexture(0,
- g_texturecache.get("torch_on_ceiling"));
+ material.setTexture(0,
+ g_irrlicht->getTexture("torch_on_ceiling.png"));
// For backwards compatibility
else if(dir == v3s16(0,0,0))
- buf->getMaterial().setTexture(0,
- g_texturecache.get("torch_on_floor"));
+ material.setTexture(0,
+ g_irrlicht->getTexture("torch_on_floor.png"));
else
- buf->getMaterial().setTexture(0, g_texturecache.get("torch"));
+ material.setTexture(0,
+ g_irrlicht->getTexture("torch.png"));
- // Add to mesh
- mesh_new->addMeshBuffer(buf);
- buf->drop();
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material, vertices, 4, indices, 6);
+ }
+ /*
+ Add flowing water to mesh
+ */
+ else if(n.d == CONTENT_WATER)
+ {
+ bool top_is_water = false;
+ try{
+ MapNode n = getNodeParent(v3s16(x,y+1,z));
+ if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+ top_is_water = true;
+ }catch(InvalidPositionException &e){}
+
+ u8 l = decode_light(n.getLightBlend(daynight_ratio));
+ video::SColor c(WATER_ALPHA,l,l,l);
+
+ // Neighbor water levels (key = relative position)
+ // Includes current node
+ core::map<v3s16, f32> neighbor_levels;
+ core::map<v3s16, u8> neighbor_contents;
+ core::map<v3s16, u8> neighbor_flags;
+ const u8 neighborflag_top_is_water = 0x01;
+ v3s16 neighbor_dirs[9] = {
+ v3s16(0,0,0),
+ v3s16(0,0,1),
+ v3s16(0,0,-1),
+ v3s16(1,0,0),
+ v3s16(-1,0,0),
+ v3s16(1,0,1),
+ v3s16(-1,0,-1),
+ v3s16(1,0,-1),
+ v3s16(-1,0,1),
+ };
+ for(u32 i=0; i<9; i++)
+ {
+ u8 content = CONTENT_AIR;
+ float level = -0.5 * BS;
+ u8 flags = 0;
+ try{
+ // Check neighbor
+ v3s16 p2 = p + neighbor_dirs[i];
+ MapNode n2 = getNodeParent(p2);
+
+ content = n2.d;
+
+ if(n2.d == CONTENT_WATERSOURCE)
+ level = (-0.5+node_water_level) * BS;
+ else if(n2.d == CONTENT_WATER)
+ level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
+ * node_water_level) * BS;
+
+ // Check node above neighbor.
+ // NOTE: This doesn't get executed if neighbor
+ // doesn't exist
+ p2.Y += 1;
+ n2 = getNodeParent(p2);
+ if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
+ flags |= neighborflag_top_is_water;
+ }
+ catch(InvalidPositionException &e){}
+
+ neighbor_levels.insert(neighbor_dirs[i], level);
+ neighbor_contents.insert(neighbor_dirs[i], content);
+ neighbor_flags.insert(neighbor_dirs[i], flags);
+ }
+
+ //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
+ //float water_level = neighbor_levels[v3s16(0,0,0)];
+
+ // Corner heights (average between four waters)
+ f32 corner_levels[4];
+
+ v3s16 halfdirs[4] = {
+ v3s16(0,0,0),
+ v3s16(1,0,0),
+ v3s16(1,0,1),
+ v3s16(0,0,1),
+ };
+ for(u32 i=0; i<4; i++)
+ {
+ v3s16 cornerdir = halfdirs[i];
+ float cornerlevel = 0;
+ u32 valid_count = 0;
+ for(u32 j=0; j<4; j++)
+ {
+ v3s16 neighbordir = cornerdir - halfdirs[j];
+ u8 content = neighbor_contents[neighbordir];
+ // Special case for source nodes
+ if(content == CONTENT_WATERSOURCE)
+ {
+ cornerlevel = (-0.5+node_water_level)*BS;
+ valid_count = 1;
+ break;
+ }
+ else if(content == CONTENT_WATER)
+ {
+ cornerlevel += neighbor_levels[neighbordir];
+ valid_count++;
+ }
+ else if(content == CONTENT_AIR)
+ {
+ cornerlevel += -0.5*BS;
+ valid_count++;
+ }
+ }
+ if(valid_count > 0)
+ cornerlevel /= valid_count;
+ corner_levels[i] = cornerlevel;
+ }
+
+ /*
+ Generate sides
+ */
+
+ v3s16 side_dirs[4] = {
+ v3s16(1,0,0),
+ v3s16(-1,0,0),
+ v3s16(0,0,1),
+ v3s16(0,0,-1),
+ };
+ s16 side_corners[4][2] = {
+ {1, 2},
+ {3, 0},
+ {2, 3},
+ {0, 1},
+ };
+ for(u32 i=0; i<4; i++)
+ {
+ v3s16 dir = side_dirs[i];
+
+ /*
+ If our topside is water and neighbor's topside
+ is water, don't draw side face
+ */
+ if(top_is_water &&
+ neighbor_flags[dir] & neighborflag_top_is_water)
+ continue;
+
+ u8 neighbor_content = neighbor_contents[dir];
+
+ // Don't draw face if neighbor is not air or water
+ if(neighbor_content != CONTENT_AIR
+ && neighbor_content != CONTENT_WATER)
+ continue;
+
+ bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
+
+ // Don't draw any faces if neighbor is water and top is water
+ if(neighbor_is_water == true && top_is_water == false)
+ continue;
+
+ video::S3DVertex vertices[4] =
+ {
+ /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+ };
+
+ /*
+ If our topside is water, set upper border of face
+ at upper border of node
+ */
+ if(top_is_water)
+ {
+ vertices[2].Pos.Y = 0.5*BS;
+ vertices[3].Pos.Y = 0.5*BS;
+ }
+ /*
+ Otherwise upper position of face is corner levels
+ */
+ else
+ {
+ vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
+ vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
+ }
+
+ /*
+ If neighbor is water, lower border of face is corner
+ water levels
+ */
+ if(neighbor_is_water)
+ {
+ vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
+ vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
+ }
+ /*
+ If neighbor is not water, lower border of face is
+ lower border of node
+ */
+ else
+ {
+ vertices[0].Pos.Y = -0.5*BS;
+ vertices[1].Pos.Y = -0.5*BS;
+ }
+
+ for(s32 j=0; j<4; j++)
+ {
+ if(dir == v3s16(0,0,1))
+ vertices[j].Pos.rotateXZBy(0);
+ if(dir == v3s16(0,0,-1))
+ vertices[j].Pos.rotateXZBy(180);
+ if(dir == v3s16(-1,0,0))
+ vertices[j].Pos.rotateXZBy(90);
+ if(dir == v3s16(1,0,-0))
+ vertices[j].Pos.rotateXZBy(-90);
+
+ vertices[j].Pos += intToFloat(p + getPosRelative());
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_w1, vertices, 4, indices, 6);
+ }
+
+ /*
+ Generate top side, if appropriate
+ */
+
+ if(top_is_water == false)
+ {
+ video::S3DVertex vertices[4] =
+ {
+ video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+ };
+
+ for(s32 i=0; i<4; i++)
+ {
+ //vertices[i].Pos.Y += water_level;
+ //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
+ vertices[i].Pos.Y += corner_levels[i];
+ vertices[i].Pos += intToFloat(p + getPosRelative());
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_w1, vertices, 4, indices, 6);
+ }
+ }
+ /*
+ Add water sources to mesh
+ */
+ else if(n.d == CONTENT_WATERSOURCE && new_style_water)
+ {
+ //bool top_is_water = false;
+ bool top_is_air = false;
+ try{
+ MapNode n = getNodeParent(v3s16(x,y+1,z));
+ /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
+ top_is_water = true;*/
+ if(n.d == CONTENT_AIR)
+ top_is_air = true;
+ }catch(InvalidPositionException &e){}
+
+ /*if(top_is_water == true)
+ continue;*/
+ if(top_is_air == false)
+ continue;
+
+ u8 l = decode_light(n.getLightBlend(daynight_ratio));
+ video::SColor c(WATER_ALPHA,l,l,l);
+
+ video::S3DVertex vertices[4] =
+ {
+ video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
+ video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
+ video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
+ video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
+ };
+
+ for(s32 i=0; i<4; i++)
+ {
+ vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
+ vertices[i].Pos += intToFloat(p + getPosRelative());
+ }
+
+ u16 indices[] = {0,1,2,2,3,0};
+ // Add to mesh collector
+ collector.append(material_w1, vertices, 4, indices, 6);
}
}
+
+ /*
+ Add stuff from collector to mesh
+ */
+ collector.fillMesh(mesh_new);
+
/*
Do some stuff to the mesh
*/
mesh_new = NULL;
}
+ // Use VBO for mesh (this just would set this for ever buffer)
+ // This will lead to infinite memory usage because or irrlicht.
+ //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
+
+ /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
+ <<"and uses "<<mesh_new->getMeshBufferCount()
+ <<" materials (meshbuffers)"<<std::endl;*/
+
/*
Replace the mesh
*/
}
}*/
+#endif // !SERVER
+
/*
Propagates sunlight down through the block.
Doesn't modify nodes that are not affected by sunlight.
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.
At the moment, all sunlighted nodes are added to light_sources.
- TODO: This could be optimized.
+ - SUGG: This could be optimized
+
+ Turns sunglighted mud into grass.
+
+ 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,
+ bool grow_grass)
{
// 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
MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
if(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)
+ {
+ 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)
+ {
+ if(grow_grass)
{
- n.setLight(LIGHTBANK_DAY, LIGHT_SUN);
-
- light_sources.insert(pos_relative + pos, true);
- }
- else{
- break;
+ 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;
+ }
}
+
+ // 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(LIGHTBANK_DAY, 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:
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);
+
+ /*
+ Spawn some objects at random.
+
+ Use dayNightDiffed() to approximate being near ground level
+ */
+ if(m_spawn_timer < -999)
+ {
+ m_spawn_timer = 60;
+ }
+ if(dayNightDiffed() == true && getObjectCount() == 0)
+ {
+ m_spawn_timer -= dtime;
+ if(m_spawn_timer <= 0.0)
+ {
+ m_spawn_timer += myrand() % 300;
+
+ v2s16 p2d(
+ (myrand()%(MAP_BLOCKSIZE-1))+0,
+ (myrand()%(MAP_BLOCKSIZE-1))+0
+ );
+
+ s16 y = getGroundLevel(p2d);
+
+ if(y >= 0)
+ {
+ v3s16 p(p2d.X, y+1, p2d.Y);
+
+ if(getNode(p).d == CONTENT_AIR
+ && getNode(p).getLightBlend(daynight_ratio) <= 11)
+ {
+ RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
+ addObject(obj);
+ }
+ }
+ }
+ }
+
+ setChangedFlag();
+}
void MapBlock::updateDayNightDiff()
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
*/
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(pressuredata, os, version);
+ compress(param2data, os, version);
}
}
// All other versions (newest)
flags |= 1;
if(m_day_night_differs)
flags |= 2;
+ if(m_lighting_expired)
+ flags |= 3;
os.write((char*)&flags, 1);
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
databuf[i+nodecount] = data[i].param;
}
- // Get pressure
+ // Get param2
for(u32 i=0; i<nodecount; i++)
{
- databuf[i+nodecount*2] = data[i].pressure;
+ databuf[i+nodecount*2] = data[i].param2;
}
/*
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];
}
}
}
is.read((char*)&flags, 1);
is_underground = (flags & 1) ? true : false;
m_day_night_differs = (flags & 2) ? true : false;
+ m_lighting_expired = (flags & 3) ? true : false;
// Uncompress data
std::ostringstream os(std::ios_base::binary);
{
data[i].param = s[i+nodecount];
}
- // Set pressure
+ // Set param2
for(u32 i=0; i<nodecount; i++)
{
- data[i].pressure = s[i+nodecount*2];
+ data[i].param2 = s[i+nodecount*2];
+ }
+ }
+
+ /*
+ Translate nodes as specified in the translate_to fields of
+ node features
+ */
+ 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;
}
}
}