MapBlockMesh, mesh animation system, urgent mesh updates, athmospheric light, removed footprints

This commit is contained in:
Kahrl 2012-03-13 18:56:12 +01:00 committed by Perttu Ahola
parent f9a66c5d46
commit 807a0d313b
22 changed files with 1216 additions and 1745 deletions

@ -82,8 +82,9 @@ MeshUpdateQueue::~MeshUpdateQueue()
{
JMutexAutoLock lock(m_mutex);
core::list<QueuedMeshUpdate*>::Iterator i;
for(i=m_queue.begin(); i!=m_queue.end(); i++)
for(std::vector<QueuedMeshUpdate*>::iterator
i = m_queue.begin();
i != m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
delete q;
@ -93,7 +94,7 @@ MeshUpdateQueue::~MeshUpdateQueue()
/*
peer_id=0 adds with nobody to send to
*/
void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server)
void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent)
{
DSTACK(__FUNCTION_NAME);
@ -101,12 +102,16 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se
JMutexAutoLock lock(m_mutex);
if(urgent)
m_urgents.insert(p);
/*
Find if block is already in queue.
If it is, update the data and quit.
*/
core::list<QueuedMeshUpdate*>::Iterator i;
for(i=m_queue.begin(); i!=m_queue.end(); i++)
for(std::vector<QueuedMeshUpdate*>::iterator
i = m_queue.begin();
i != m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
if(q->p == p)
@ -136,12 +141,19 @@ QueuedMeshUpdate * MeshUpdateQueue::pop()
{
JMutexAutoLock lock(m_mutex);
core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
if(i == m_queue.end())
return NULL;
bool must_be_urgent = !m_urgents.empty();
for(std::vector<QueuedMeshUpdate*>::iterator
i = m_queue.begin();
i != m_queue.end(); i++)
{
QueuedMeshUpdate *q = *i;
if(must_be_urgent && m_urgents.count(q->p) == 0)
continue;
m_queue.erase(i);
m_urgents.erase(q->p);
return q;
}
return NULL;
}
/*
@ -178,8 +190,12 @@ void * MeshUpdateThread::Thread()
ScopeProfiler sp(g_profiler, "Client: Mesh making");
scene::SMesh *mesh_new = NULL;
mesh_new = makeMapBlockMesh(q->data, m_gamedef);
MapBlockMesh *mesh_new = new MapBlockMesh(q->data);
if(mesh_new->getMesh()->getMeshBufferCount() == 0)
{
delete mesh_new;
mesh_new = NULL;
}
MeshUpdateResult r;
r.p = q->p;
@ -227,6 +243,9 @@ Client::Client(
m_inventory_updated(false),
m_inventory_from_server(NULL),
m_inventory_from_server_age(0.0),
m_animation_time(0),
m_crack_level(-1),
m_crack_pos(0,0,0),
m_time_of_day(0),
m_map_seed(0),
m_password(password),
@ -309,6 +328,10 @@ void Client::step(float dtime)
else
m_ignore_damage_timer = 0.0;
m_animation_time += dtime;
if(m_animation_time > 60.0)
m_animation_time -= 60.0;
//infostream<<"Client steps "<<dtime<<std::endl;
{
@ -635,7 +658,18 @@ void Client::step(float dtime)
MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(r.p);
if(block)
{
block->replaceMesh(r.mesh);
//JMutexAutoLock lock(block->mesh_mutex);
// Delete the old mesh
if(block->mesh != NULL)
{
// TODO: Remove hardware buffers of meshbuffers of block->mesh
delete block->mesh;
block->mesh = NULL;
}
// Replace with the new mesh
block->mesh = r.mesh;
}
if(r.ack_block_to_server)
{
@ -868,9 +902,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
//TimeTaker t1("TOCLIENT_REMOVENODE");
// This will clear the cracking animation after digging
((ClientMap&)m_env.getMap()).clearTempMod(p);
removeNode(p);
}
else if(command == TOCLIENT_ADDNODE)
@ -960,13 +991,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
m_con.Send(PEER_ID_SERVER, 1, reply, true);
#endif
/*
Update Mesh of this block and blocks at x-, y- and z-.
Environment should not be locked as it interlocks with the
main thread, from which is will want to retrieve textures.
*/
//m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio());
/*
Add it to mesh update queue and set it to be acknowledged after update.
*/
@ -1837,12 +1861,14 @@ void Client::removeNode(v3s16 p)
{
}
// add urgent task to update the modified node
addUpdateMeshTaskForNode(p, false, true);
for(core::map<v3s16, MapBlock * >::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
//m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
addUpdateMeshTaskWithEdge(p);
}
}
@ -1863,14 +1889,13 @@ void Client::addNode(v3s16 p, MapNode n)
catch(InvalidPositionException &e)
{}
//TimeTaker timer2("Client::addNode(): updateMeshes");
//TimeTaker timer2("Client::addNode(): addUpdateMeshTaskWithEdge");
for(core::map<v3s16, MapBlock * >::Iterator
i = modified_blocks.getIterator();
i.atEnd() == false; i++)
{
v3s16 p = i.getNode()->getKey();
//m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio());
addUpdateMeshTaskWithEdge(p);
}
}
@ -2051,6 +2076,36 @@ core::list<std::wstring> Client::getConnectedPlayerNames()
return playerNames;
}
float Client::getAnimationTime()
{
return m_animation_time;
}
int Client::getCrackLevel()
{
return m_crack_level;
}
void Client::setCrack(int level, v3s16 pos)
{
int old_crack_level = m_crack_level;
v3s16 old_crack_pos = m_crack_pos;
m_crack_level = level;
m_crack_pos = pos;
if(old_crack_level >= 0 && (level < 0 || pos != old_crack_pos))
{
// remove old crack
addUpdateMeshTaskForNode(old_crack_pos, false, true);
}
if(level >= 0 && (old_crack_level < 0 || pos != old_crack_pos))
{
// add new crack
addUpdateMeshTaskForNode(pos, false, true);
}
}
u32 Client::getDayNightRatio()
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
@ -2064,40 +2119,6 @@ u16 Client::getHP()
return player->hp;
}
void Client::setTempMod(v3s16 p, NodeMod mod)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
core::map<v3s16, MapBlock*> affected_blocks;
((ClientMap&)m_env.getMap()).setTempMod(p, mod,
&affected_blocks);
for(core::map<v3s16, MapBlock*>::Iterator
i = affected_blocks.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
}
}
void Client::clearTempMod(v3s16 p)
{
//JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
core::map<v3s16, MapBlock*> affected_blocks;
((ClientMap&)m_env.getMap()).clearTempMod(p,
&affected_blocks);
for(core::map<v3s16, MapBlock*>::Iterator
i = affected_blocks.getIterator();
i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
}
}
bool Client::getChatMessage(std::wstring &message)
{
if(m_chat_queue.size() == 0)
@ -2131,10 +2152,12 @@ void Client::typeChatMessage(const std::wstring &message)
}
}
void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent)
{
/*infostream<<"Client::addUpdateMeshTask(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<" ack_to_server="<<ack_to_server
<<" urgent="<<urgent
<<std::endl;*/
MapBlock *b = m_env.getMap().getBlockNoCreateNoEx(p);
@ -2145,45 +2168,29 @@ void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server)
Create a task to update the mesh of the block
*/
MeshMakeData *data = new MeshMakeData;
MeshMakeData *data = new MeshMakeData(this);
{
//TimeTaker timer("data fill");
// Release: ~0ms
// Debug: 1-6ms, avg=2ms
data->fill(getDayNightRatio(), b);
data->fill(b);
data->setCrack(m_crack_level, m_crack_pos);
data->setSmoothLighting(g_settings->getBool("smooth_lighting"));
}
// Debug wait
//while(m_mesh_update_thread.m_queue_in.size() > 0) sleep_ms(10);
// Add task to queue
m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server);
m_mesh_update_thread.m_queue_in.addBlock(p, data, ack_to_server, urgent);
/*infostream<<"Mesh update input queue size is "
<<m_mesh_update_thread.m_queue_in.size()
<<std::endl;*/
#if 0
// Temporary test: make mesh directly in here
{
//TimeTaker timer("make mesh");
// 10ms
scene::SMesh *mesh_new = NULL;
mesh_new = makeMapBlockMesh(data);
b->replaceMesh(mesh_new);
delete data;
}
#endif
/*
Mark mesh as non-expired at this point so that it can already
be marked as expired again if the data changes
*/
b->setMeshExpired(false);
}
void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent)
{
/*{
v3s16 p = blockpos;
@ -2195,27 +2202,68 @@ void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server)
try{
v3s16 p = blockpos + v3s16(0,0,0);
//MapBlock *b = m_env.getMap().getBlockNoCreate(p);
addUpdateMeshTask(p, ack_to_server);
addUpdateMeshTask(p, ack_to_server, urgent);
}
catch(InvalidPositionException &e){}
// Leading edge
try{
v3s16 p = blockpos + v3s16(-1,0,0);
addUpdateMeshTask(p);
addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,-1,0);
addUpdateMeshTask(p);
addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,0,-1);
addUpdateMeshTask(p);
addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
}
void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent)
{
{
v3s16 p = nodepos;
infostream<<"Client::addUpdateMeshTaskForNode(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;
}
v3s16 blockpos = getNodeBlockPos(nodepos);
v3s16 blockpos_relative = blockpos * MAP_BLOCKSIZE;
try{
v3s16 p = blockpos + v3s16(0,0,0);
addUpdateMeshTask(p, ack_to_server, urgent);
}
catch(InvalidPositionException &e){}
// Leading edge
if(nodepos.X == blockpos_relative.X){
try{
v3s16 p = blockpos + v3s16(-1,0,0);
addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
}
if(nodepos.Y == blockpos_relative.Y){
try{
v3s16 p = blockpos + v3s16(0,-1,0);
addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
}
if(nodepos.Z == blockpos_relative.Z){
try{
v3s16 p = blockpos + v3s16(0,0,-1);
addUpdateMeshTask(p, false, urgent);
}
catch(InvalidPositionException &e){}
}
}
ClientEvent Client::getClientEvent()
{
if(m_client_event_queue.size() == 0)

@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common_irrlicht.h"
#include "jmutex.h"
#include <ostream>
#include <set>
#include <vector>
#include "clientobject.h"
#include "utility.h" // For IntervalLimiter
#include "gamedef.h"
@ -34,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "filesys.h"
struct MeshMakeData;
class MapBlockMesh;
class IGameDef;
class IWritableTextureSource;
class IWritableItemDefManager;
@ -71,7 +74,8 @@ public:
/*
peer_id=0 adds with nobody to send to
*/
void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server);
void addBlock(v3s16 p, MeshMakeData *data,
bool ack_block_to_server, bool urgent);
// Returned pointer must be deleted
// Returns NULL if queue is empty
@ -84,14 +88,15 @@ public:
}
private:
core::list<QueuedMeshUpdate*> m_queue;
std::vector<QueuedMeshUpdate*> m_queue;
std::set<v3s16> m_urgents;
JMutex m_mutex;
};
struct MeshUpdateResult
{
v3s16 p;
scene::SMesh *mesh;
MapBlockMesh *mesh;
bool ack_block_to_server;
MeshUpdateResult():
@ -260,13 +265,15 @@ public:
core::list<std::wstring> getConnectedPlayerNames();
float getAnimationTime();
int getCrackLevel();
void setCrack(int level, v3s16 pos);
u32 getDayNightRatio();
u16 getHP();
void setTempMod(v3s16 p, NodeMod mod);
void clearTempMod(v3s16 p);
float getAvgRtt()
{
try{
@ -281,9 +288,10 @@ public:
u64 getMapSeed(){ return m_map_seed; }
void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false);
void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false, bool urgent=false);
// Including blocks at appropriate edges
void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false);
void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false, bool urgent=false);
void addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server=false, bool urgent=false);
// Get event from queue. CE_NONE is returned if queue is empty.
ClientEvent getClientEvent();
@ -352,6 +360,10 @@ private:
float m_inventory_from_server_age;
core::map<v3s16, bool> m_active_blocks;
PacketCounter m_packetcounter;
// Block mesh animation parameters
float m_animation_time;
int m_crack_level;
v3s16 m_crack_pos;
// Received from the server. 0-23999
u32 m_time_of_day;
// 0 <= m_daynight_i < DAYNIGHT_CACHE_COUNT

@ -29,9 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// Create a cuboid.
// collector - the MeshCollector for the resulting polygons
// box - the position and size of the box
// materials - the materials to use (for all 6 faces)
// pa - texture atlas pointers for the materials
// matcount - number of entries in "materials" and "pa", 1<=matcount<=6
// tiles - the tiles (materials) to use (for all 6 faces)
// tilecount - number of entries in tiles, 1<=tilecount<=6
// c - vertex colour - used for all
// txc - texture coordinates - this is a list of texture coordinates
// for the opposite corners of each face - therefore, there
@ -41,10 +40,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// (compatible with ContentFeatures). If you specified 0,0,1,1
// for each face, that would be the same as passing NULL.
void makeCuboid(MeshCollector *collector, const aabb3f &box,
const video::SMaterial *materials, const AtlasPointer *pa, int matcount,
const TileSpec *tiles, int tilecount,
video::SColor &c, const f32* txc)
{
assert(matcount >= 1);
assert(tilecount >= 1 && tilecount <= 6);
v3f min = box.MinEdge;
v3f max = box.MaxEdge;
@ -98,9 +97,9 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
for(s32 j=0; j<24; j++)
{
int matindex = MYMIN(j/4, matcount-1);
vertices[j].TCoords *= pa[matindex].size;
vertices[j].TCoords += pa[matindex].pos;
int tileindex = MYMIN(j/4, tilecount-1);
vertices[j].TCoords *= tiles[tileindex].texture.size;
vertices[j].TCoords += tiles[tileindex].texture.pos;
}
u16 indices[] = {0,1,2,2,3,0};
@ -108,17 +107,16 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box,
// Add to mesh collector
for(s32 j=0; j<24; j+=4)
{
int matindex = MYMIN(j/4, matcount-1);
collector->append(materials[matindex],
int tileindex = MYMIN(j/4, tilecount-1);
collector->append(tiles[tileindex],
vertices+j, 4, indices, 6);
}
}
void mapblock_mesh_generate_special(MeshMakeData *data,
MeshCollector &collector, IGameDef *gamedef)
MeshCollector &collector)
{
INodeDefManager *nodedef = gamedef->ndef();
ITextureSource *tsrc = gamedef->getTextureSource();
INodeDefManager *nodedef = data->m_gamedef->ndef();
// 0ms
//TimeTaker timer("mapblock_mesh_generate_special()");
@ -134,14 +132,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
/*// General ground material for special output
// Texture is modified just before usage
video::SMaterial material_general;
material_general.setFlag(video::EMF_LIGHTING, false);
material_general.setFlag(video::EMF_BILINEAR_FILTER, false);
material_general.setFlag(video::EMF_FOG_ENABLE, true);
material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;*/
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
for(s16 y=0; y<MAP_BLOCKSIZE; y++)
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
@ -167,16 +157,8 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
/*
Add water sources to mesh if using new style
*/
assert(nodedef->get(n).special_materials[0]);
//assert(nodedef->get(n).special_materials[1]);
assert(nodedef->get(n).special_aps[0]);
video::SMaterial &liquid_material =
*nodedef->get(n).special_materials[0];
/*video::SMaterial &liquid_material_bfculled =
*nodedef->get(n).special_materials[1];*/
AtlasPointer &pa_liquid1 =
*nodedef->get(n).special_aps[0];
TileSpec tile_liquid = f.special_tiles[0];
AtlasPointer &pa_liquid = tile_liquid.texture;
bool top_is_air = false;
MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
@ -186,64 +168,55 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
if(top_is_air == false)
continue;
u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
video::SColor c = MapBlock_LightColor(
nodedef->get(n).alpha, l);
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(f.alpha, l);
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x0(), pa_liquid1.y1()),
pa_liquid.x0(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x1(), pa_liquid1.y1()),
pa_liquid.x1(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
pa_liquid1.x1(), pa_liquid1.y0()),
pa_liquid.x1(), pa_liquid.y0()),
video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
pa_liquid1.x0(), pa_liquid1.y0()),
pa_liquid.x0(), pa_liquid.y0()),
};
v3f offset(p.X, p.Y + (-0.5+node_liquid_level)*BS, p.Z);
for(s32 i=0; i<4; i++)
{
vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[i].Pos += offset;
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(liquid_material, vertices, 4, indices, 6);
collector.append(tile_liquid, vertices, 4, indices, 6);
break;}
case NDT_FLOWINGLIQUID:
{
/*
Add flowing liquid to mesh
*/
assert(nodedef->get(n).special_materials[0]);
assert(nodedef->get(n).special_materials[1]);
assert(nodedef->get(n).special_aps[0]);
video::SMaterial &liquid_material =
*nodedef->get(n).special_materials[0];
video::SMaterial &liquid_material_bfculled =
*nodedef->get(n).special_materials[1];
AtlasPointer &pa_liquid1 =
*nodedef->get(n).special_aps[0];
TileSpec tile_liquid = f.special_tiles[0];
TileSpec tile_liquid_bfculled = f.special_tiles[1];
AtlasPointer &pa_liquid = tile_liquid.texture;
bool top_is_same_liquid = false;
MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
content_t c_flowing = nodedef->getId(nodedef->get(n).liquid_alternative_flowing);
content_t c_source = nodedef->getId(nodedef->get(n).liquid_alternative_source);
content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
content_t c_source = nodedef->getId(f.liquid_alternative_source);
if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
top_is_same_liquid = true;
u8 l = 0;
u16 l = 0;
// Use the light of the node on top if possible
if(nodedef->get(ntop).param_type == CPT_LIGHT)
l = decode_light(ntop.getLightBlend(data->m_daynight_ratio, nodedef));
l = getInteriorLight(ntop, 0, data);
// Otherwise use the light of this node (the liquid)
else
l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
video::SColor c = MapBlock_LightColor(
nodedef->get(n).alpha, l);
l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(f.alpha, l);
// Neighbor liquid levels (key = relative position)
// Includes current node
@ -393,20 +366,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
// Use backface culled material if neighbor doesn't have a
// solidness of 0
video::SMaterial *current_material = &liquid_material;
const TileSpec *current_tile = &tile_liquid;
if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
current_material = &liquid_material_bfculled;
current_tile = &tile_liquid_bfculled;
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x0(), pa_liquid1.y1()),
pa_liquid.x0(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x1(), pa_liquid1.y1()),
pa_liquid.x1(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x1(), pa_liquid1.y0()),
pa_liquid.x1(), pa_liquid.y0()),
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x0(), pa_liquid1.y0()),
pa_liquid.x0(), pa_liquid.y0()),
};
/*
@ -464,12 +437,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
vertices[j].Pos.Z *= 0.98;
}*/
vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[j].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(*current_material, vertices, 4, indices, 6);
collector.append(*current_tile, vertices, 4, indices, 6);
}
/*
@ -481,13 +454,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x0(), pa_liquid1.y1()),
pa_liquid.x0(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
pa_liquid1.x1(), pa_liquid1.y1()),
pa_liquid.x1(), pa_liquid.y1()),
video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
pa_liquid1.x1(), pa_liquid1.y0()),
pa_liquid.x1(), pa_liquid.y0()),
video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
pa_liquid1.x0(), pa_liquid1.y0()),
pa_liquid.x0(), pa_liquid.y0()),
};
// This fixes a strange bug
@ -499,27 +472,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
//vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
s32 j = corner_resolve[i];
vertices[i].Pos.Y += corner_levels[j];
vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[i].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(liquid_material, vertices, 4, indices, 6);
collector.append(tile_liquid, vertices, 4, indices, 6);
}
break;}
case NDT_GLASSLIKE:
{
video::SMaterial material_glass;
material_glass.setFlag(video::EMF_LIGHTING, false);
material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
material_glass.setFlag(video::EMF_FOG_ENABLE, true);
material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
TileSpec tile_glass = getNodeTile(n, p, v3s16(0,0,0),
&data->m_temp_mods, tsrc, nodedef);
AtlasPointer pa_glass = tile_glass.texture;
material_glass.setTexture(0, pa_glass.atlas);
TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
AtlasPointer ap = tile.texture;
u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
for(u32 j=0; j<6; j++)
@ -535,13 +501,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
pa_glass.x0(), pa_glass.y1()),
ap.x0(), ap.y1()),
video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
pa_glass.x1(), pa_glass.y1()),
ap.x1(), ap.y1()),
video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
pa_glass.x1(), pa_glass.y0()),
ap.x1(), ap.y0()),
video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
pa_glass.x0(), pa_glass.y0()),
ap.x0(), ap.y0()),
};
// Rotations in the g_6dirs format
@ -565,36 +531,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
vertices[i].Pos.rotateXZBy(90);
for(u16 i=0; i<4; i++){
vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[i].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material_glass, vertices, 4, indices, 6);
collector.append(tile, vertices, 4, indices, 6);
}
break;}
case NDT_ALLFACES:
{
video::SMaterial material_leaves1;
material_leaves1.setFlag(video::EMF_LIGHTING, false);
material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
TileSpec tile_leaves1 = getNodeTile(n, p, v3s16(0,0,0),
&data->m_temp_mods, tsrc, nodedef);
AtlasPointer pa_leaves1 = tile_leaves1.texture;
material_leaves1.setTexture(0, pa_leaves1.atlas);
TileSpec tile_leaves = getNodeTile(n, p,
v3s16(0,0,0), data);
AtlasPointer pa_leaves = tile_leaves.texture;
u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
v3f pos = intToFloat(p+blockpos_nodes, BS);
v3f pos = intToFloat(p, BS);
aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
box.MinEdge += pos;
box.MaxEdge += pos;
makeCuboid(&collector, box,
&material_leaves1, &pa_leaves1, 1,
c, NULL);
makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
break;}
case NDT_ALLFACES_OPTIONAL:
// This is always pre-converted to something else
@ -604,28 +562,23 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
{
v3s16 dir = n.getWallMountedDir(nodedef);
AtlasPointer ap(0);
u8 tileindex = 0;
if(dir == v3s16(0,-1,0)){
ap = f.tiles[0].texture; // floor
tileindex = 0; // floor
} else if(dir == v3s16(0,1,0)){
ap = f.tiles[1].texture; // ceiling
tileindex = 1; // ceiling
// For backwards compatibility
} else if(dir == v3s16(0,0,0)){
ap = f.tiles[0].texture; // floor
tileindex = 0; // floor
} else {
ap = f.tiles[2].texture; // side
tileindex = 2; // side
}
// Set material
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.setFlag(video::EMF_FOG_ENABLE, true);
//material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.setTexture(0, ap.atlas);
TileSpec tile = getNodeTileN(n, p, tileindex, data);
tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
AtlasPointer ap = tile.texture;
video::SColor c(255,255,255,255);
@ -657,27 +610,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
if(dir == v3s16(0,1,0))
vertices[i].Pos.rotateXZBy(-45);
vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[i].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material, vertices, 4, indices, 6);
collector.append(tile, vertices, 4, indices, 6);
break;}
case NDT_SIGNLIKE:
{
// Set material
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.setFlag(video::EMF_FOG_ENABLE, true);
material.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
AtlasPointer ap = f.tiles[0].texture;
material.setTexture(0, ap.atlas);
TileSpec tile = getNodeTileN(n, p, 0, data);
tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
AtlasPointer ap = tile.texture;
u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(255, l);
float d = (float)BS/16;
@ -711,24 +658,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
if(dir == v3s16(0,1,0))
vertices[i].Pos.rotateXYBy(90);
vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[i].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material, vertices, 4, indices, 6);
collector.append(tile, vertices, 4, indices, 6);
break;}
case NDT_PLANTLIKE:
{
video::SMaterial material_papyrus;
material_papyrus.setFlag(video::EMF_LIGHTING, false);
material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
material_papyrus.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
AtlasPointer pa_papyrus = f.tiles[0].texture;
material_papyrus.setTexture(0, pa_papyrus.atlas);
TileSpec tile = getNodeTileN(n, p, 0, data);
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
AtlasPointer ap = tile.texture;
u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
for(u32 j=0; j<4; j++)
@ -736,15 +679,15 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
video::S3DVertex vertices[4] =
{
video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
pa_papyrus.x0(), pa_papyrus.y1()),
ap.x0(), ap.y1()),
video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
pa_papyrus.x1(), pa_papyrus.y1()),
ap.x1(), ap.y1()),
video::S3DVertex( BS/2*f.visual_scale,
-BS/2 + f.visual_scale*BS,0, 0,0,0, c,
pa_papyrus.x1(), pa_papyrus.y0()),
ap.x1(), ap.y0()),
video::S3DVertex(-BS/2*f.visual_scale,
-BS/2 + f.visual_scale*BS,0, 0,0,0, c,
pa_papyrus.x0(), pa_papyrus.y0()),
ap.x0(), ap.y0()),
};
if(j == 0)
@ -771,45 +714,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
for(u16 i=0; i<4; i++)
{
vertices[i].Pos *= f.visual_scale;
vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[i].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material_papyrus, vertices, 4, indices, 6);
collector.append(tile, vertices, 4, indices, 6);
}
break;}
case NDT_FENCELIKE:
{
video::SMaterial material_wood;
material_wood.setFlag(video::EMF_LIGHTING, false);
material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
material_wood.setFlag(video::EMF_FOG_ENABLE, true);
material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
TileSpec tile_wood = getNodeTile(n, p, v3s16(0,0,0),
&data->m_temp_mods, tsrc, nodedef);
AtlasPointer pa_wood = tile_wood.texture;
material_wood.setTexture(0, pa_wood.atlas);
TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
TileSpec tile_nocrack = tile;
tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
video::SMaterial material_wood_nomod;
material_wood_nomod.setFlag(video::EMF_LIGHTING, false);
material_wood_nomod.setFlag(video::EMF_BILINEAR_FILTER, false);
material_wood_nomod.setFlag(video::EMF_FOG_ENABLE, true);
material_wood_nomod.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
TileSpec tile_wood_nomod = getNodeTile(n, p, v3s16(0,0,0),
NULL, tsrc, nodedef);
AtlasPointer pa_wood_nomod = tile_wood_nomod.texture;
material_wood_nomod.setTexture(0, pa_wood_nomod.atlas);
u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
const f32 post_rad=(f32)BS/10;
const f32 bar_rad=(f32)BS/20;
const f32 bar_len=(f32)(BS/2)-post_rad;
v3f pos = intToFloat(p+blockpos_nodes, BS);
v3f pos = intToFloat(p, BS);
// The post - always present
aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
@ -822,8 +748,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
0.35,0,0.65,1,
0.35,0,0.65,1,
0.35,0,0.65,1};
makeCuboid(&collector, post, &material_wood,
&pa_wood, 1, c, postuv);
makeCuboid(&collector, post, &tile, 1, c, postuv);
// Now a section of fence, +X, if there's a post there
v3s16 p2 = p;
@ -843,12 +768,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
0,0.4,1,0.6,
0,0.4,1,0.6,
0,0.4,1,0.6};
makeCuboid(&collector, bar, &material_wood_nomod,
&pa_wood_nomod, 1, c, xrailuv);
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, xrailuv);
bar.MinEdge.Y -= BS/2;
bar.MaxEdge.Y -= BS/2;
makeCuboid(&collector, bar, &material_wood_nomod,
&pa_wood_nomod, 1, c, xrailuv);
// TODO: no crack
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, xrailuv);
}
// Now a section of fence, +Z, if there's a post there
@ -870,12 +796,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
0,0.4,1,0.6,
0,0.4,1,0.6};
makeCuboid(&collector, bar, &material_wood_nomod,
&pa_wood_nomod, 1, c, zrailuv);
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, zrailuv);
bar.MinEdge.Y -= BS/2;
bar.MaxEdge.Y -= BS/2;
makeCuboid(&collector, bar, &material_wood_nomod,
&pa_wood_nomod, 1, c, zrailuv);
makeCuboid(&collector, bar, &tile_nocrack, 1,
c, zrailuv);
}
break;}
case NDT_RAILLIKE:
@ -948,31 +874,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
}
// Assign textures
AtlasPointer ap = f.tiles[0].texture; // straight
u8 tileindex = 0; // straight
if(adjacencies < 2)
ap = f.tiles[0].texture; // straight
tileindex = 0; // straight
else if(adjacencies == 2)
{
if(is_straight)
ap = f.tiles[0].texture; // straight
tileindex = 0; // straight
else
ap = f.tiles[1].texture; // curved
tileindex = 1; // curved
}
else if(adjacencies == 3)
ap = f.tiles[2].texture; // t-junction
tileindex = 2; // t-junction
else if(adjacencies == 4)
ap = f.tiles[3].texture; // crossing
tileindex = 3; // crossing
video::SMaterial material_rail;
material_rail.setFlag(video::EMF_LIGHTING, false);
material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
material_rail.setFlag(video::EMF_FOG_ENABLE, true);
material_rail.MaterialType
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material_rail.setTexture(0, ap.atlas);
TileSpec tile = getNodeTileN(n, p, tileindex, data);
tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
AtlasPointer ap = tile.texture;
u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(255, l);
float d = (float)BS/64;
@ -1048,11 +971,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data,
for(s32 i=0; i<4; i++)
{
vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
vertices[i].Pos += intToFloat(p, BS);
}
u16 indices[] = {0,1,2,2,3,0};
collector.append(material_rail, vertices, 4, indices, 6);
collector.append(tile, vertices, 4, indices, 6);
break;}
}
}

@ -20,13 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef CONTENT_MAPBLOCK_HEADER
#define CONTENT_MAPBLOCK_HEADER
#ifndef SERVER
#include "mapblock_mesh.h"
#include "utility.h"
class IGameDef;
struct MeshMakeData;
struct MeshCollector;
void mapblock_mesh_generate_special(MeshMakeData *data,
MeshCollector &collector, IGameDef *gamedef);
#endif
MeshCollector &collector);
#endif

@ -890,9 +890,6 @@ void ServerEnvironment::step(float dtime)
//TimeTaker timer("ServerEnv step");
// Get some settings
bool footprints = g_settings->getBool("footprints");
/*
Increment game time
*/
@ -921,26 +918,6 @@ void ServerEnvironment::step(float dtime)
// Move
player->move(dtime, *m_map, 100*BS);
/*
Add footsteps to grass
*/
if(footprints)
{
// Get node that is at BS/4 under player
v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
try{
MapNode n = m_map->getNode(bottompos);
if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
{
n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
m_map->setNode(bottompos, n);
}
}
catch(InvalidPositionException &e)
{
}
}
}
}
@ -1873,7 +1850,6 @@ void ClientEnvironment::step(float dtime)
// Get some settings
bool free_move = g_settings->getBool("free_move");
bool footprints = g_settings->getBool("footprints");
// Get local player
LocalPlayer *lplayer = getLocalPlayer();
@ -2058,34 +2034,6 @@ void ClientEnvironment::step(float dtime)
light = blend_light(getDayNightRatio(), LIGHT_SUN, 0);
}
player->updateLight(light);
/*
Add footsteps to grass
*/
if(footprints)
{
// Get node that is at BS/4 under player
v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS);
try{
MapNode n = m_map->getNode(bottompos);
if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS"))
{
n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS"));
m_map->setNode(bottompos, n);
// Update mesh on client
if(m_map->mapType() == MAPTYPE_CLIENT)
{
v3s16 p_blocks = getNodeBlockPos(bottompos);
MapBlock *b = m_map->getBlockNoCreate(p_blocks);
//b->updateMesh(getDayNightRatio());
b->setMeshExpired(true);
}
}
}
catch(InvalidPositionException &e)
{
}
}
}
/*
@ -2134,16 +2082,6 @@ void ClientEnvironment::step(float dtime)
}
}
void ClientEnvironment::updateMeshes(v3s16 blockpos)
{
m_map->updateMeshes(blockpos, getDayNightRatio());
}
void ClientEnvironment::expireMeshes(bool only_daynight_diffed)
{
m_map->expireMeshes(only_daynight_diffed);
}
void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple)
{
m_simple_objects.push_back(simple);

@ -407,24 +407,6 @@ public:
virtual void addPlayer(Player *player);
LocalPlayer * getLocalPlayer();
// Slightly deprecated
void updateMeshes(v3s16 blockpos);
void expireMeshes(bool only_daynight_diffed);
void setTimeOfDay(u32 time)
{
u32 old_dr = getDayNightRatio();
Environment::setTimeOfDay(time);
if(getDayNightRatio() != old_dr)
{
/*infostream<<"ClientEnvironment: DayNightRatio changed"
<<" -> expiring meshes"<<std::endl;*/
expireMeshes(true);
}
}
/*
ClientSimpleObjects
*/

@ -1038,8 +1038,6 @@ void the_game(
float object_hit_delay_timer = 0.0;
float time_from_last_punch = 10;
float crack_update_timer = 0.0;
bool invert_mouse = g_settings->getBool("invert_mouse");
bool respawn_menu_active = false;
@ -1185,7 +1183,6 @@ void the_game(
if(object_hit_delay_timer >= 0)
object_hit_delay_timer -= dtime;
time_from_last_punch += dtime;
crack_update_timer += dtime;
g_profiler->add("Elapsed time", dtime);
g_profiler->avg("FPS", 1./dtime);
@ -1908,7 +1905,7 @@ void the_game(
if(!digging)
{
client.interact(1, pointed_old);
client.clearTempMod(pointed_old.node_undersurface);
client.setCrack(-1, v3s16(0,0,0));
dig_time = 0.0;
}
}
@ -2003,20 +2000,15 @@ void the_game(
}
else if(dig_index < CRACK_ANIMATION_LENGTH)
{
// Limit crack update speed
if(crack_update_timer >= 0.1){
crack_update_timer = 0.0;
//infostream<<"dig_index="<<dig_index<<std::endl;
//TimeTaker timer("client.setTempMod");
client.setTempMod(nodepos,
NodeMod(NODEMOD_CRACK, dig_index));
}
//infostream<<"dig_index="<<dig_index<<std::endl;
client.setCrack(dig_index, nodepos);
}
else
{
infostream<<"Digging completed"<<std::endl;
client.interact(2, pointed);
client.clearTempMod(nodepos);
client.setCrack(-1, v3s16(0,0,0));
client.removeNode(nodepos);
dig_time = 0;
@ -2171,8 +2163,6 @@ void the_game(
u32 daynight_ratio = client.getDayNightRatio();
u8 light8 = decode_light((daynight_ratio * LIGHT_SUN) / 1000);
brightness = (float)light8/255.0;
// Make night look good
brightness = brightness * 1.15 - 0.15;
video::SColor bgcolor;
if(brightness >= 0.2 && brightness < 0.7)
bgcolor = video::SColor(

@ -381,17 +381,20 @@ public:
content_t id = nodedef->getId(def->name);
const ContentFeatures &f = nodedef->get(id);
u8 param1 = 0;
if(f.param_type == CPT_LIGHT)
param1 = 0xee;
/*
Make a mesh from the node
*/
MeshMakeData mesh_make_data;
MapNode mesh_make_node(
id,
(f.param_type == CPT_LIGHT) ? 0xee : 0,
0);
mesh_make_data.fillSingleNode(1000, &mesh_make_node);
scene::IMesh *node_mesh =
makeMapBlockMesh(&mesh_make_data, gamedef);
MeshMakeData mesh_make_data(gamedef);
MapNode mesh_make_node(id, param1, 0);
mesh_make_data.fillSingleNode(&mesh_make_node);
MapBlockMesh mapblock_mesh(&mesh_make_data);
scene::IMesh *node_mesh = mapblock_mesh.getMesh();
assert(node_mesh);
setMeshColor(node_mesh, video::SColor(255, 255, 255, 255));
/*
@ -404,7 +407,7 @@ public:
/*
Draw node mesh into a render target texture
*/
if(def->inventory_texture == NULL && node_mesh != NULL)
if(def->inventory_texture == NULL)
{
core::dimension2d<u32> dim(64,64);
std::string rtt_texture_name = "INVENTORY_"
@ -443,7 +446,7 @@ public:
/*
Use the node mesh as the wield mesh
*/
if(def->wield_mesh == NULL && node_mesh != NULL)
if(def->wield_mesh == NULL)
{
// Scale to proper wield mesh proportions
scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0)
@ -452,9 +455,7 @@ public:
def->wield_mesh->grab();
}
if(node_mesh != NULL)
node_mesh->drop();
// falling outside of here deletes node_mesh
}
}
#endif

@ -21,23 +21,22 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapsector.h"
#include "mapblock.h"
#include "main.h"
#ifndef SERVER
#include "client.h"
#endif
#include "filesys.h"
#include "utility.h"
#include "voxel.h"
#include "porting.h"
#include "mapgen.h"
#include "nodemetadata.h"
#ifndef SERVER
#include <IMaterialRenderer.h>
#endif
#include "settings.h"
#include "log.h"
#include "profiler.h"
#include "nodedef.h"
#include "gamedef.h"
#ifndef SERVER
#include "client.h"
#include "mapblock_mesh.h"
#include <IMaterialRenderer.h>
#endif
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
@ -3676,7 +3675,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
*/
int time1 = time(0);
//u32 daynight_ratio = m_client->getDayNightRatio();
/*
Get animation parameters
*/
float animation_time = m_client->getAnimationTime();
int crack = m_client->getCrackLevel();
u32 daynight_ratio = m_client->getDayNightRatio();
m_camera_mutex.Lock();
v3f camera_position = m_camera_position;
@ -3709,8 +3713,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
u32 vertex_count = 0;
u32 meshbuffer_count = 0;
// For limiting number of mesh updates per frame
u32 mesh_update_count = 0;
// For limiting number of mesh animations per frame
u32 mesh_animate_count = 0;
u32 mesh_animate_count_far = 0;
// Number of blocks in rendering range
u32 blocks_in_range = 0;
@ -3791,57 +3796,18 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
blocks_in_range++;
#if 1
/*
Update expired mesh (used for day/night change)
It doesn't work exactly like it should now with the
tasked mesh update but whatever.
Ignore if mesh doesn't exist
*/
bool mesh_expired = false;
{
JMutexAutoLock lock(block->mesh_mutex);
//JMutexAutoLock lock(block->mesh_mutex);
mesh_expired = block->getMeshExpired();
// Mesh has not been expired and there is no mesh:
// block has no content
if(block->mesh == NULL && mesh_expired == false){
if(block->mesh == NULL){
blocks_in_range_without_mesh++;
continue;
}
}
f32 faraway = BS*50;
//f32 faraway = m_control.wanted_range * BS;
/*
This has to be done with the mesh_mutex unlocked
*/
// Pretty random but this should work somewhat nicely
if(mesh_expired && (
(mesh_update_count < 3
&& (d < faraway || mesh_update_count < 2)
)
||
(m_control.range_all && mesh_update_count < 20)
)
)
/*if(mesh_expired && mesh_update_count < 6
&& (d < faraway || mesh_update_count < 3))*/
{
mesh_update_count++;
// Mesh has been expired: generate new mesh
//block->updateMesh(daynight_ratio);
m_client->addUpdateMeshTask(block->getPos());
mesh_expired = false;
}
#endif
/*
Occlusion culling
*/
@ -3883,20 +3849,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
// This block is in range. Reset usage timer.
block->resetUsageTimer();
/*
Ignore if mesh doesn't exist
*/
{
JMutexAutoLock lock(block->mesh_mutex);
scene::SMesh *mesh = block->mesh;
if(mesh == NULL){
blocks_in_range_without_mesh++;
continue;
}
}
// Limit block count in case of a sudden increase
blocks_would_have_drawn++;
if(blocks_drawn >= m_control.wanted_max_blocks
@ -3904,6 +3856,33 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
&& d > m_control.wanted_min_range * BS)
continue;
// Mesh animation
{
//JMutexAutoLock lock(block->mesh_mutex);
MapBlockMesh *mapBlockMesh = block->mesh;
// Pretty random but this should work somewhat nicely
bool faraway = d >= BS*50;
//bool faraway = d >= m_control.wanted_range * BS;
if(mapBlockMesh->isAnimationForced() ||
!faraway ||
mesh_animate_count_far < (m_control.range_all ? 200 : 50))
{
bool animated = mapBlockMesh->animate(
faraway,
animation_time,
crack,
daynight_ratio);
if(animated)
mesh_animate_count++;
if(animated && faraway)
mesh_animate_count_far++;
}
else
{
mapBlockMesh->decreaseAnimationForceTimer();
}
}
// Add to set
drawset[block->getPos()] = block;
@ -3951,9 +3930,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
Draw the faces of the block
*/
{
JMutexAutoLock lock(block->mesh_mutex);
//JMutexAutoLock lock(block->mesh_mutex);
scene::SMesh *mesh = block->mesh;
MapBlockMesh *mapBlockMesh = block->mesh;
assert(mapBlockMesh);
scene::SMesh *mesh = mapBlockMesh->getMesh();
assert(mesh);
u32 c = mesh->getMeshBufferCount();
@ -3999,6 +3981,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
g_profiler->avg("CM: blocks in range without mesh (frac)",
(float)blocks_in_range_without_mesh/blocks_in_range);
g_profiler->avg("CM: blocks drawn", blocks_drawn);
g_profiler->avg("CM: animated meshes", mesh_animate_count);
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
}
g_profiler->avg(prefix+"vertices drawn", vertex_count);
@ -4047,205 +4031,6 @@ void ClientMap::renderPostFx()
}
}
bool ClientMap::setTempMod(v3s16 p, NodeMod mod,
core::map<v3s16, MapBlock*> *affected_blocks)
{
bool changed = false;
/*
Add it to all blocks touching it
*/
v3s16 dirs[7] = {
v3s16(0,0,0), // this
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
for(u16 i=0; i<7; i++)
{
v3s16 p2 = p + dirs[i];
// Block position of neighbor (or requested) node
v3s16 blockpos = getNodeBlockPos(p2);
MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
if(blockref == NULL)
continue;
// Relative position of requested node
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
if(blockref->setTempMod(relpos, mod))
{
changed = true;
}
}
if(changed && affected_blocks!=NULL)
{
for(u16 i=0; i<7; i++)
{
v3s16 p2 = p + dirs[i];
// Block position of neighbor (or requested) node
v3s16 blockpos = getNodeBlockPos(p2);
MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
if(blockref == NULL)
continue;
affected_blocks->insert(blockpos, blockref);
}
}
return changed;
}
bool ClientMap::clearTempMod(v3s16 p,
core::map<v3s16, MapBlock*> *affected_blocks)
{
bool changed = false;
v3s16 dirs[7] = {
v3s16(0,0,0), // this
v3s16(0,0,1), // back
v3s16(0,1,0), // top
v3s16(1,0,0), // right
v3s16(0,0,-1), // front
v3s16(0,-1,0), // bottom
v3s16(-1,0,0), // left
};
for(u16 i=0; i<7; i++)
{
v3s16 p2 = p + dirs[i];
// Block position of neighbor (or requested) node
v3s16 blockpos = getNodeBlockPos(p2);
MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
if(blockref == NULL)
continue;
// Relative position of requested node
v3s16 relpos = p - blockpos*MAP_BLOCKSIZE;
if(blockref->clearTempMod(relpos))
{
changed = true;
}
}
if(changed && affected_blocks!=NULL)
{
for(u16 i=0; i<7; i++)
{
v3s16 p2 = p + dirs[i];
// Block position of neighbor (or requested) node
v3s16 blockpos = getNodeBlockPos(p2);
MapBlock * blockref = getBlockNoCreateNoEx(blockpos);
if(blockref == NULL)
continue;
affected_blocks->insert(blockpos, blockref);
}
}
return changed;
}
void ClientMap::expireMeshes(bool only_daynight_diffed)
{
TimeTaker timer("expireMeshes()");
core::map<v2s16, MapSector*>::Iterator si;
si = m_sectors.getIterator();
for(; si.atEnd() == false; si++)
{
MapSector *sector = si.getNode()->getValue();
core::list< MapBlock * > sectorblocks;
sector->getBlocks(sectorblocks);
core::list< MapBlock * >::Iterator i;
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
{
MapBlock *block = *i;
if(only_daynight_diffed && dayNightDiffed(block->getPos()) == false)
{
continue;
}
{
JMutexAutoLock lock(block->mesh_mutex);
if(block->mesh != NULL)
{
/*block->mesh->drop();
block->mesh = NULL;*/
block->setMeshExpired(true);
}
}
}
}
}
void ClientMap::updateMeshes(v3s16 blockpos, u32 daynight_ratio)
{
assert(mapType() == MAPTYPE_CLIENT);
try{
v3s16 p = blockpos + v3s16(0,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
//b->setMeshExpired(true);
}
catch(InvalidPositionException &e){}
// Leading edge
try{
v3s16 p = blockpos + v3s16(-1,0,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
//b->setMeshExpired(true);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,-1,0);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
//b->setMeshExpired(true);
}
catch(InvalidPositionException &e){}
try{
v3s16 p = blockpos + v3s16(0,0,-1);
MapBlock *b = getBlockNoCreate(p);
b->updateMesh(daynight_ratio);
//b->setMeshExpired(true);
}
catch(InvalidPositionException &e){}
}
#if 0
/*
Update mesh of block in which the node is, and if the node is at the
leading edge, update the appropriate leading blocks too.
*/
void ClientMap::updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio)
{
v3s16 dirs[4] = {
v3s16(0,0,0),
v3s16(-1,0,0),
v3s16(0,-1,0),
v3s16(0,0,-1),
};
v3s16 blockposes[4];
for(u32 i=0; i<4; i++)
{
v3s16 np = nodepos + dirs[i];
blockposes[i] = getNodeBlockPos(np);
// Don't update mesh of block if it has been done already
bool already_updated = false;
for(u32 j=0; j<i; j++)
{
if(blockposes[j] == blockposes[i])
{
already_updated = true;
break;
}
}
if(already_updated)
continue;
// Update mesh
MapBlock *b = getBlockNoCreate(blockposes[i]);
b->updateMesh(daynight_ratio);
}
}
#endif
void ClientMap::PrintInfo(std::ostream &out)
{
out<<"ClientMap: ";

@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common_irrlicht.h"
#include "mapnode.h"
#include "mapblock_nodemod.h"
#include "constants.h"
#include "voxel.h"
#include "utility.h" // Needed for UniqueQueue, a member of Map
@ -570,33 +569,6 @@ public:
void renderPostFx();
/*
Methods for setting temporary modifications to nodes for
drawing.
Returns true if something changed.
All blocks whose mesh could have been changed are inserted
to affected_blocks.
*/
bool setTempMod(v3s16 p, NodeMod mod,
core::map<v3s16, MapBlock*> *affected_blocks=NULL);
bool clearTempMod(v3s16 p,
core::map<v3s16, MapBlock*> *affected_blocks=NULL);
// Efficient implementation needs a cache of TempMods
//void clearTempMods();
void expireMeshes(bool only_daynight_diffed);
/*
Update the faces of the given block and blocks on the
leading edge, without threading. Rarely used.
*/
void updateMeshes(v3s16 blockpos, u32 daynight_ratio);
// Update meshes that touch the node
//void updateNodeMeshes(v3s16 nodepos, u32 daynight_ratio);
// For debug printing
virtual void PrintInfo(std::ostream &out);

@ -59,10 +59,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
reallocate();
#ifndef SERVER
m_mesh_expired = false;
mesh_mutex.Init();
//mesh_mutex.Init();
mesh = NULL;
m_temp_mods_mutex.Init();
#endif
}
@ -70,11 +68,11 @@ MapBlock::~MapBlock()
{
#ifndef SERVER
{
JMutexAutoLock lock(mesh_mutex);
//JMutexAutoLock lock(mesh_mutex);
if(mesh)
{
mesh->drop();
delete mesh;
mesh = NULL;
}
}
@ -147,78 +145,6 @@ MapNode MapBlock::getNodeParentNoEx(v3s16 p)
}
}
#ifndef SERVER
#if 1
void MapBlock::updateMesh(u32 daynight_ratio)
{
#if 0
/*
DEBUG: If mesh has been generated, don't generate it again
*/
{
JMutexAutoLock meshlock(mesh_mutex);
if(mesh != NULL)
return;
}
#endif
MeshMakeData data;
data.fill(daynight_ratio, this);
scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef);
/*
Replace the mesh
*/
replaceMesh(mesh_new);
}
#endif
void MapBlock::replaceMesh(scene::SMesh *mesh_new)
{
mesh_mutex.Lock();
//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)
{
// 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);
}*/
/*infostream<<"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);
infostream<<"buf->getReferenceCount()="
<<buf->getReferenceCount()<<std::endl;
}*/
// Drop the mesh
mesh_old->drop();
//delete mesh_old;
}
mesh_mutex.Unlock();
}
#endif // !SERVER
/*
Propagates sunlight down through the block.
Doesn't modify nodes that are not affected by sunlight.
@ -1232,13 +1158,6 @@ std::string analyze_block(MapBlock *block)
else
desc<<"is_ug [ ], ";
#ifndef SERVER
if(block->getMeshExpired())
desc<<"mesh_exp [X], ";
else
desc<<"mesh_exp [ ], ";
#endif
if(block->getLightingExpired())
desc<<"lighting_exp [X], ";
else

@ -31,13 +31,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h"
#include "voxel.h"
#include "staticobject.h"
#include "mapblock_nodemod.h"
#include "modifiedstate.h"
class Map;
class NodeMetadataList;
class IGameDef;
class IWritableNodeDefManager;
class MapBlockMesh;
#define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff
@ -193,18 +192,6 @@ public:
raiseModified(MOD_STATE_WRITE_NEEDED, "setIsUnderground");
}
#ifndef SERVER
void setMeshExpired(bool expired)
{
m_mesh_expired = expired;
}
bool getMeshExpired()
{
return m_mesh_expired;
}
#endif
void setLightingExpired(bool expired)
{
if(expired != m_lighting_expired){
@ -359,33 +346,6 @@ public:
setNode(x0+x, y0+y, z0+z, node);
}
/*
Graphics-related methods
*/
#ifndef SERVER // Only on client
u8 getFaceLight2(u32 daynight_ratio, v3s16 p, v3s16 face_dir,
INodeDefManager *nodemgr)
{
return getFaceLight(daynight_ratio,
getNodeParentNoEx(p),
getNodeParentNoEx(p + face_dir),
face_dir, nodemgr);
}
#if 1
/*
Thread-safely updates the whole mesh of the mapblock.
NOTE: Prefer generating the mesh separately and then using
replaceMesh().
*/
void updateMesh(u32 daynight_ratio);
#endif
// Replace the mesh with a new one
void replaceMesh(scene::SMesh *mesh_new);
#endif
// See comments in mapblock.cpp
bool propagateSunlight(core::map<v3s16, bool> & light_sources,
bool remove_light=false, bool *black_air_left=NULL);
@ -395,59 +355,10 @@ public:
// Copies data from VoxelManipulator getPosRelative()
void copyFrom(VoxelManipulator &dst);
#ifndef SERVER // Only on client
/*
Methods for setting temporary modifications to nodes for
drawing
returns true if the mod was different last time
*/
bool setTempMod(v3s16 p, const NodeMod &mod)
{
/*dstream<<"setTempMod called on block"
<<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<", mod.type="<<mod.type
<<", mod.param="<<mod.param
<<std::endl;*/
JMutexAutoLock lock(m_temp_mods_mutex);
return m_temp_mods.set(p, mod);
}
// Returns true if there was one
bool getTempMod(v3s16 p, NodeMod *mod)
{
JMutexAutoLock lock(m_temp_mods_mutex);
return m_temp_mods.get(p, mod);
}
bool clearTempMod(v3s16 p)
{
JMutexAutoLock lock(m_temp_mods_mutex);
return m_temp_mods.clear(p);
}
bool clearTempMods()
{
JMutexAutoLock lock(m_temp_mods_mutex);
return m_temp_mods.clear();
}
void copyTempMods(NodeModMap &dst)
{
JMutexAutoLock lock(m_temp_mods_mutex);
m_temp_mods.copy(dst);
}
#endif
/*
Update day-night lighting difference flag.
Sets m_day_night_differs to appropriate value.
These methods don't care about neighboring blocks.
It means that to know if a block really doesn't need a mesh
update between day and night, the neighboring blocks have
to be taken into account. Use Map::dayNightDiffed().
*/
void updateDayNightDiff();
@ -551,8 +462,8 @@ public:
*/
#ifndef SERVER // Only on client
scene::SMesh *mesh;
JMutex mesh_mutex;
MapBlockMesh *mesh;
//JMutex mesh_mutex;
#endif
NodeMetadataList *m_node_metadata;
@ -609,20 +520,6 @@ private:
bool m_generated;
#ifndef SERVER // Only on client
/*
Set to true if the mesh has been ordered to be updated
sometime in the background.
In practice this is set when the day/night lighting switches.
*/
bool m_mesh_expired;
// Temporary modifications to nodes
// These are only used when drawing
NodeModMap m_temp_mods;
JMutex m_temp_mods_mutex;
#endif
/*
When block is removed from active blocks, this is set to gametime.
Value BLOCK_TIMESTAMP_UNDEFINED=0xffffffff means there is no timestamp.

File diff suppressed because it is too large Load Diff

@ -21,9 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MAPBLOCK_MESH_HEADER
#include "common_irrlicht.h"
#include "mapblock_nodemod.h"
#include "tile.h"
#include "voxel.h"
#include <map>
class IGameDef;
@ -31,127 +31,143 @@ class IGameDef;
Mesh making stuff
*/
/*
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);
}
for(u32 i=0; i<numVertices; i++)
{
p->vertices.push_back(vertices[i]);
}
}
void fillMesh(scene::SMesh *mesh)
{
/*dstream<<"Filling mesh with "<<m_prebuffers.size()
<<" meshbuffers"<<std::endl;*/
for(u32 i=0; i<m_prebuffers.size(); i++)
{
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());
}
}
private:
core::array<PreMeshBuffer> m_prebuffers;
};
// Helper functions
video::SColor MapBlock_LightColor(u8 alpha, u8 light);
TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef);
class MapBlock;
struct MeshMakeData
{
u32 m_daynight_ratio;
NodeModMap m_temp_mods;
VoxelManipulator m_vmanip;
v3s16 m_blockpos;
v3s16 m_crack_pos_relative;
bool m_smooth_lighting;
IGameDef *m_gamedef;
MeshMakeData(IGameDef *gamedef);
/*
Copy central data directly from block, and other data from
parent of block.
*/
void fill(u32 daynight_ratio, MapBlock *block);
void fill(MapBlock *block);
/*
Set up with only a single node at (1,1,1)
*/
void fillSingleNode(u32 daynight_ratio, MapNode *node);
void fillSingleNode(MapNode *node);
/*
Set the (node) position of a crack
*/
void setCrack(int crack_level, v3s16 crack_pos);
/*
Enable or disable smooth lighting
*/
void setSmoothLighting(bool smooth_lighting);
};
// This is the highest-level function in here
scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef);
/*
Holds a mesh for a mapblock.
Besides the SMesh*, this contains information used for animating
the vertex positions, colors and texture coordinates of the mesh.
For example:
- cracks [implemented]
- day/night transitions [implemented]
- animated flowing liquids [not implemented]
- animating vertex positions for e.g. axles [not implemented]
*/
class MapBlockMesh
{
public:
// Builds the mesh given
MapBlockMesh(MeshMakeData *data);
~MapBlockMesh();
// Main animation function, parameters:
// faraway: whether the block is far away from the camera (~50 nodes)
// time: the global animation time, 0 .. 60 (repeats every minute)
// daynight_ratio: 0 .. 1000
// crack: -1 .. CRACK_ANIMATION_LENGTH-1 (-1 for off)
// Returns true if anything has been changed.
bool animate(bool faraway, float time, int crack, u32 daynight_ratio);
scene::SMesh* getMesh()
{
return m_mesh;
}
bool isAnimationForced() const
{
return m_animation_force_timer == 0;
}
void decreaseAnimationForceTimer()
{
if(m_animation_force_timer > 0)
m_animation_force_timer--;
}
private:
scene::SMesh *m_mesh;
IGameDef *m_gamedef;
// Must animate() be called before rendering?
bool m_has_animation;
int m_animation_force_timer;
// Animation info: cracks
// Last crack value passed to animate()
int m_last_crack;
// Maps mesh buffer (i.e. material) indices to base texture names
std::map<u32, std::string> m_crack_materials;
// Animation info: day/night transitions
// Last daynight_ratio value passed to animate()
u32 m_last_daynight_ratio;
// For each meshbuffer, maps vertex indices to (day,night) pairs
std::map<u32, std::map<u32, std::pair<u8, u8> > > m_daynight_diffs;
};
/*
This is used because CMeshBuffer::append() is very slow
*/
struct PreMeshBuffer
{
TileSpec tile;
core::array<u16> indices;
core::array<video::S3DVertex> vertices;
};
struct MeshCollector
{
core::array<PreMeshBuffer> prebuffers;
void append(const TileSpec &material,
const video::S3DVertex *vertices, u32 numVertices,
const u16 *indices, u32 numIndices);
};
// This encodes
// alpha in the A channel of the returned SColor
// day light (0-255) in the R channel of the returned SColor
// night light (0-255) in the G channel of the returned SColor
inline video::SColor MapBlock_LightColor(u8 alpha, u16 light)
{
return video::SColor(alpha, (light & 0xff), (light >> 8), 0);
}
// Compute light at node
u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data);
u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data);
u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data);
// Retrieves the TileSpec of a face of a node
// Adds MATERIAL_FLAG_CRACK if the node is cracked
TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data);
TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data);
#endif

@ -1,114 +0,0 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MAPBLOCK_NODEMOD_HEADER
#define MAPBLOCK_NODEMOD_HEADER
enum NodeModType
{
NODEMOD_NONE,
NODEMOD_CHANGECONTENT, //param is content id
NODEMOD_CRACK // param is crack progression
};
struct NodeMod
{
NodeMod(enum NodeModType a_type=NODEMOD_NONE, u16 a_param=0)
{
type = a_type;
param = a_param;
}
bool operator==(const NodeMod &other)
{
return (type == other.type && param == other.param);
}
enum NodeModType type;
u16 param;
};
class NodeModMap
{
public:
/*
returns true if the mod was different last time
*/
bool set(v3s16 p, const NodeMod &mod)
{
// See if old is different, cancel if it is not different.
core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
if(n)
{
NodeMod old = n->getValue();
if(old == mod)
return false;
n->setValue(mod);
}
else
{
m_mods.insert(p, mod);
}
return true;
}
// Returns true if there was one
bool get(v3s16 p, NodeMod *mod)
{
core::map<v3s16, NodeMod>::Node *n;
n = m_mods.find(p);
if(n == NULL)
return false;
if(mod)
*mod = n->getValue();
return true;
}
bool clear(v3s16 p)
{
if(m_mods.find(p))
{
m_mods.remove(p);
return true;
}
return false;
}
bool clear()
{
if(m_mods.size() == 0)
return false;
m_mods.clear();
return true;
}
void copy(NodeModMap &dest)
{
dest.m_mods.clear();
for(core::map<v3s16, NodeMod>::Iterator
i = m_mods.getIterator();
i.atEnd() == false; i++)
{
dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
}
private:
core::map<v3s16, NodeMod> m_mods;
};
#endif

@ -353,115 +353,3 @@ void MapNode::deSerialize_pre22(u8 *source, u8 version)
// Translate to our known version
*this = mapnode_translate_to_internal(*this, version);
}
#ifndef SERVER
/*
Nodes make a face if contents differ and solidness differs.
Return value:
0: No face
1: Face uses m1's content
2: Face uses m2's content
equivalent: Whether the blocks share the same face (eg. water and glass)
TODO: Add 3: Both faces drawn with backface culling, remove equivalent
*/
u8 face_contents(content_t m1, content_t m2, bool *equivalent,
INodeDefManager *nodemgr)
{
*equivalent = false;
if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
return 0;
bool contents_differ = (m1 != m2);
const ContentFeatures &f1 = nodemgr->get(m1);
const ContentFeatures &f2 = nodemgr->get(m2);
// Contents don't differ for different forms of same liquid
if(f1.sameLiquid(f2))
contents_differ = false;
u8 c1 = f1.solidness;
u8 c2 = f2.solidness;
bool solidness_differs = (c1 != c2);
bool makes_face = contents_differ && solidness_differs;
if(makes_face == false)
return 0;
if(c1 == 0)
c1 = f1.visual_solidness;
if(c2 == 0)
c2 = f2.visual_solidness;
if(c1 == c2){
*equivalent = true;
// If same solidness, liquid takes precense
if(f1.isLiquid())
return 1;
if(f2.isLiquid())
return 2;
}
if(c1 > c2)
return 1;
else
return 2;
}
/*
Gets lighting value at face of node
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: getNode(p) (uses only the lighting value)
n2: getNode(p + face_dir) (uses only the lighting value)
face_dir: axis oriented unit vector from p to p2
returns encoded light value.
*/
u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir, INodeDefManager *nodemgr)
{
try{
u8 light;
u8 l1 = n.getLightBlend(daynight_ratio, nodemgr);
u8 l2 = n2.getLightBlend(daynight_ratio, nodemgr);
if(l1 > l2)
light = l1;
else
light = l2;
// Make some nice difference to different sides
// This makes light come from a corner
/*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);*/
// All neighboring faces have different shade (like in minecraft)
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;
}
}
#endif

@ -223,45 +223,5 @@ private:
void deSerialize_pre22(u8 *source, u8 version);
};
/*
MapNode helpers for mesh making stuff
*/
#ifndef SERVER
/*
Nodes make a face if contents differ and solidness differs.
Return value:
0: No face
1: Face uses m1's content
2: Face uses m2's content
equivalent: Whether the blocks share the same face (eg. water and glass)
*/
u8 face_contents(content_t m1, content_t m2, bool *equivalent,
INodeDefManager *nodemgr);
/*
Gets lighting value at face of node
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: getNode(p) (uses only the lighting value)
n2: getNode(p + face_dir) (uses only the lighting value)
face_dir: axis oriented unit vector from p to p2
returns encoded light value.
*/
u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir, INodeDefManager *nodemgr);
#endif
#endif

@ -89,12 +89,6 @@ ContentFeatures::ContentFeatures()
ContentFeatures::~ContentFeatures()
{
#ifndef SERVER
for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
delete special_materials[j];
delete special_aps[j];
}
#endif
}
void ContentFeatures::reset()
@ -103,10 +97,6 @@ void ContentFeatures::reset()
Cached stuff
*/
#ifndef SERVER
for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
special_materials[j] = NULL;
special_aps[j] = NULL;
}
solidness = 2;
visual_solidness = 0;
backface_culling = true;
@ -526,38 +516,21 @@ public:
f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else
f->tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
f->tiles[j].material_flags = 0;
if(f->backface_culling)
f->tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
else
f->tiles[j].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
}
// Special textures
// Special tiles
for(u16 j=0; j<CF_SPECIAL_COUNT; j++){
// Remove all stuff
if(f->special_aps[j]){
delete f->special_aps[j];
f->special_aps[j] = NULL;
}
if(f->special_materials[j]){
delete f->special_materials[j];
f->special_materials[j] = NULL;
}
// Skip if should not exist
if(f->mspec_special[j].tname == "")
continue;
// Create all stuff
f->special_aps[j] = new AtlasPointer(
tsrc->getTexture(f->mspec_special[j].tname));
f->special_materials[j] = new video::SMaterial;
f->special_materials[j]->setFlag(video::EMF_LIGHTING, false);
f->special_materials[j]->setFlag(video::EMF_BACK_FACE_CULLING,
f->mspec_special[j].backface_culling);
f->special_materials[j]->setFlag(video::EMF_BILINEAR_FILTER, false);
f->special_materials[j]->setFlag(video::EMF_FOG_ENABLE, true);
f->special_materials[j]->setTexture(0, f->special_aps[j]->atlas);
if(f->alpha != 255)
f->special_materials[j]->MaterialType =
video::EMT_TRANSPARENT_VERTEX_ALPHA;
f->special_tiles[j].texture = tsrc->getTexture(f->mspec_special[j].tname);
f->special_tiles[j].alpha = f->alpha;
if(f->alpha == 255)
f->special_tiles[j].material_type = MATERIAL_ALPHA_SIMPLE;
else
f->special_tiles[j].material_type = MATERIAL_ALPHA_VERTEX;
f->special_tiles[j].material_flags = 0;
if(f->mspec_special[j].backface_culling)
f->special_tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
}
}
#endif

@ -135,10 +135,9 @@ struct ContentFeatures
// 0 1 2 3 4 5
// up down right left back front
TileSpec tiles[6];
// Special material/texture
// Special tiles
// - Currently used for flowing liquids
video::SMaterial *special_materials[CF_SPECIAL_COUNT];
AtlasPointer *special_aps[CF_SPECIAL_COUNT];
TileSpec special_tiles[CF_SPECIAL_COUNT];
u8 solidness; // Used when choosing which face is drawn
u8 visual_solidness; // When solidness=0, this tells how it looks like
bool backface_culling;

@ -279,12 +279,14 @@ public:
Example case #2:
- Assume a texture with the id 1 exists, and has the name
"stone.png^mineral1" and is specified as a part of some atlas.
- Now MapBlock::getNodeTile() stumbles upon a node which uses
texture id 1, and finds out that NODEMOD_CRACK must be applied
with progression=0
- It finds out the name of the texture with getTextureName(1),
- Now getNodeTile() stumbles upon a node which uses
texture id 1, and determines that MATERIAL_FLAG_CRACK
must be applied to the tile
- MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and
has received the current crack level 0 from the client. It
finds out the name of the texture with getTextureName(1),
appends "^crack0" to it and gets a new texture id with
getTextureId("stone.png^mineral1^crack0")
getTextureId("stone.png^mineral1^crack0").
*/
@ -337,6 +339,12 @@ public:
return ap.atlas;
}
// Gets a separate texture atlas pointer
AtlasPointer getTextureRawAP(const AtlasPointer &ap)
{
return getTexture(getTextureName(ap.id) + "^[forcesingle");
}
// Returns a pointer to the irrlicht device
virtual IrrlichtDevice* getDevice()
{
@ -475,6 +483,9 @@ u32 TextureSource::getTextureId(const std::string &name)
return 0;
}
// Overlay image on top of another image (used for cracks)
void overlay(video::IImage *image, video::IImage *overlay);
// Brighten image
void brighten(video::IImage *image);
@ -1183,8 +1194,19 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
return false;
}
// Crack image number
u16 progression = stoi(part_of_name.substr(6));
// Crack image number and overlay option
s32 progression = 0;
bool use_overlay = false;
if(part_of_name.substr(6,1) == "o")
{
progression = stoi(part_of_name.substr(7));
use_overlay = true;
}
else
{
progression = stoi(part_of_name.substr(6));
use_overlay = false;
}
// Size of the base image
core::dimension2d<u32> dim_base = baseimg->getDimension();
@ -1197,65 +1219,56 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
*/
video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device);
if(img_crack)
if(img_crack && progression >= 0)
{
// Dimension of original image
core::dimension2d<u32> dim_crack
= img_crack->getDimension();
// Count of crack stages
u32 crack_count = dim_crack.Height / dim_crack.Width;
s32 crack_count = dim_crack.Height / dim_crack.Width;
// Limit progression
if(progression > crack_count-1)
progression = crack_count-1;
// Dimension of a single scaled crack stage
core::dimension2d<u32> dim_crack_scaled_single(
dim_base.Width,
dim_base.Height
// Dimension of a single crack stage
core::dimension2d<u32> dim_crack_cropped(
dim_crack.Width,
dim_crack.Width
);
// Dimension of scaled size
core::dimension2d<u32> dim_crack_scaled(
dim_crack_scaled_single.Width,
dim_crack_scaled_single.Height * crack_count
);
// Create scaled crack image
// Create cropped and scaled crack images
video::IImage *img_crack_cropped = driver->createImage(
video::ECF_A8R8G8B8, dim_crack_cropped);
video::IImage *img_crack_scaled = driver->createImage(
video::ECF_A8R8G8B8, dim_crack_scaled);
if(img_crack_scaled)
video::ECF_A8R8G8B8, dim_base);
if(img_crack_cropped && img_crack_scaled)
{
// Crop crack image
v2s32 pos_crack(0, progression*dim_crack.Width);
img_crack->copyTo(img_crack_cropped,
v2s32(0,0),
core::rect<s32>(pos_crack, dim_crack_cropped));
// Scale crack image by copying
img_crack->copyToScaling(img_crack_scaled);
// Position to copy the crack from
core::position2d<s32> pos_crack_scaled(
0,
dim_crack_scaled_single.Height * progression
);
// This tiling does nothing currently but is useful
for(u32 y0=0; y0<dim_base.Height
/ dim_crack_scaled_single.Height; y0++)
for(u32 x0=0; x0<dim_base.Width
/ dim_crack_scaled_single.Width; x0++)
img_crack_cropped->copyToScaling(img_crack_scaled);
// Copy or overlay crack image
if(use_overlay)
{
// Position to copy the crack to in the base image
core::position2d<s32> pos_base(
x0*dim_crack_scaled_single.Width,
y0*dim_crack_scaled_single.Height
);
// Rectangle to copy the crack from on the scaled image
core::rect<s32> rect_crack_scaled(
pos_crack_scaled,
dim_crack_scaled_single
);
// Copy it
img_crack_scaled->copyToWithAlpha(baseimg, pos_base,
rect_crack_scaled,
video::SColor(255,255,255,255),
NULL);
overlay(baseimg, img_crack_scaled);
}
else
{
img_crack_scaled->copyToWithAlpha(
baseimg,
v2s32(0,0),
core::rect<s32>(v2s32(0,0), dim_base),
video::SColor(255,255,255,255));
}
}
if(img_crack_scaled)
img_crack_scaled->drop();
}
if(img_crack_cropped)
img_crack_cropped->drop();
img_crack->drop();
}
@ -1511,6 +1524,37 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg,
return true;
}
void overlay(video::IImage *image, video::IImage *overlay)
{
/*
Copy overlay to image, taking alpha into account.
Where image is transparent, don't copy from overlay.
Images sizes must be identical.
*/
if(image == NULL || overlay == NULL)
return;
core::dimension2d<u32> dim = image->getDimension();
core::dimension2d<u32> dim_overlay = overlay->getDimension();
assert(dim == dim_overlay);
for(u32 y=0; y<dim.Height; y++)
for(u32 x=0; x<dim.Width; x++)
{
video::SColor c1 = image->getPixel(x,y);
video::SColor c2 = overlay->getPixel(x,y);
u32 a1 = c1.getAlpha();
u32 a2 = c2.getAlpha();
if(a1 == 255 && a2 != 0)
{
c1.setRed((c1.getRed()*(255-a2) + c2.getRed()*a2)/255);
c1.setGreen((c1.getGreen()*(255-a2) + c2.getGreen()*a2)/255);
c1.setBlue((c1.getBlue()*(255-a2) + c2.getBlue()*a2)/255);
}
image->setPixel(x,y,c1);
}
}
void brighten(video::IImage *image)
{
if(image == NULL)

@ -73,7 +73,7 @@ struct AtlasPointer
{
}
bool operator==(const AtlasPointer &other)
bool operator==(const AtlasPointer &other) const
{
return (
id == other.id
@ -87,6 +87,11 @@ struct AtlasPointer
);*/
}
bool operator!=(const AtlasPointer &other) const
{
return !(*this == other);
}
float x0(){ return pos.X; }
float x1(){ return pos.X + size.X; }
float y0(){ return pos.Y; }
@ -110,6 +115,8 @@ public:
{return AtlasPointer(0);}
virtual video::ITexture* getTextureRaw(const std::string &name)
{return NULL;}
virtual AtlasPointer getTextureRawAP(const AtlasPointer &ap)
{return AtlasPointer(0);}
virtual IrrlichtDevice* getDevice()
{return NULL;}
virtual void updateAP(AtlasPointer &ap){};
@ -148,7 +155,13 @@ enum MaterialType{
};
// Material flags
// Should backface culling be enabled?
#define MATERIAL_FLAG_BACKFACE_CULLING 0x01
// Should a crack be drawn?
#define MATERIAL_FLAG_CRACK 0x02
// Should the crack be drawn on transparent pixels (unset) or not (set)?
// Ignored if MATERIAL_FLAG_CRACK is not set.
#define MATERIAL_FLAG_CRACK_OVERLAY 0x04
/*
This fully defines the looks of a tile.
@ -169,7 +182,7 @@ struct TileSpec
{
}
bool operator==(TileSpec &other)
bool operator==(const TileSpec &other) const
{
return (
texture == other.texture &&
@ -179,6 +192,11 @@ struct TileSpec
);
}
bool operator!=(const TileSpec &other) const
{
return !(*this == other);
}
// Sets everything else except the texture in the material
void applyMaterialOptions(video::SMaterial &material) const
{

@ -1753,18 +1753,17 @@ std::string deSerializeJsonString(std::istream &is);
inline u32 time_to_daynight_ratio(u32 time_of_day)
{
const s32 daylength = 16;
const s32 nightlength = 6;
const s32 daytimelength = 8;
s32 d = daylength;
s32 t = (((time_of_day)%24000)/(24000/d));
if(t < nightlength/2 || t >= d - nightlength/2)
//return 300;
s32 t = time_of_day%24000;
if(t < 4500 || t >= 19500)
return 150;
else if(t < 5000 || t >= 19000)
return 350;
else if(t >= d/2 - daytimelength/2 && t < d/2 + daytimelength/2)
return 1000;
else
else if(t < 5500 || t >= 18500)
return 500;
else if(t < 6000 || t >= 18000)
return 750;
else
return 1000;
}
// Random helper. Usually d=BS