{
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;
/*
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);
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)
{
JMutexAutoLock lock(m_mutex);
- core::list<QueuedMeshUpdate*>::Iterator i = m_queue.begin();
- if(i == m_queue.end())
- return NULL;
- QueuedMeshUpdate *q = *i;
- m_queue.erase(i);
- return q;
+ 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;
}
/*
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;
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),
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;
{
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)
{
//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)
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.
*/
{
}
+ // 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);
}
}
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);
}
}
return playerNames;
}
-u32 Client::getDayNightRatio()
+float Client::getAnimationTime()
{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
- return m_env.getDayNightRatio();
+ return m_animation_time;
}
-u16 Client::getHP()
+int Client::getCrackLevel()
{
- Player *player = m_env.getLocalPlayer();
- assert(player != NULL);
- return player->hp;
+ return m_crack_level;
}
-void Client::setTempMod(v3s16 p, NodeMod mod)
+void Client::setCrack(int level, v3s16 pos)
{
- //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out
- assert(m_env.getMap().mapType() == MAPTYPE_CLIENT);
+ int old_crack_level = m_crack_level;
+ v3s16 old_crack_pos = m_crack_pos;
- core::map<v3s16, MapBlock*> affected_blocks;
- ((ClientMap&)m_env.getMap()).setTempMod(p, mod,
- &affected_blocks);
+ m_crack_level = level;
+ m_crack_pos = pos;
- for(core::map<v3s16, MapBlock*>::Iterator
- i = affected_blocks.getIterator();
- i.atEnd() == false; i++)
+ 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))
{
- i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
+ // add new crack
+ addUpdateMeshTaskForNode(pos, false, true);
}
}
-void Client::clearTempMod(v3s16 p)
+u32 Client::getDayNightRatio()
{
//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);
+ return m_env.getDayNightRatio();
+}
- for(core::map<v3s16, MapBlock*>::Iterator
- i = affected_blocks.getIterator();
- i.atEnd() == false; i++)
- {
- i.getNode()->getValue()->updateMesh(m_env.getDayNightRatio());
- }
+u16 Client::getHP()
+{
+ Player *player = m_env.getLocalPlayer();
+ assert(player != NULL);
+ return player->hp;
}
bool Client::getChatMessage(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);
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;
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()
#include "common_irrlicht.h"
#include "jmutex.h"
#include <ostream>
+#include <set>
+#include <vector>
#include "clientobject.h"
#include "utility.h" // For IntervalLimiter
#include "gamedef.h"
#include "filesys.h"
struct MeshMakeData;
+class MapBlockMesh;
class IGameDef;
class IWritableTextureSource;
class IWritableItemDefManager;
/*
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
}
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():
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{
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();
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
// 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
// (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;
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};
// 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()");
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++)
/*
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));
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
// 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()),
};
/*
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);
}
/*
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
//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);
-
- u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
+ TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
+ AtlasPointer ap = tile.texture;
+
+ u16 l = getInteriorLight(n, 1, data);
video::SColor c = MapBlock_LightColor(255, l);
for(u32 j=0; j<6; j++)
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
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);
-
- u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
+ TileSpec tile_leaves = getNodeTile(n, p,
+ v3s16(0,0,0), data);
+ AtlasPointer pa_leaves = tile_leaves.texture;
+
+ 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
{
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);
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);
-
- u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
+ 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;
+
+ u16 l = getInteriorLight(n, 0, data);
video::SColor c = MapBlock_LightColor(255, l);
float d = (float)BS/16;
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++)
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)
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);
-
- 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)));
+ TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
+ TileSpec tile_nocrack = tile;
+ tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
+
+ 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);
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;
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
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:
}
// 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
+
+ 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::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);
-
- 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/64;
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;}
}
}
#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
//TimeTaker timer("ServerEnv step");
- // Get some settings
- bool footprints = g_settings->getBool("footprints");
-
/*
Increment game time
*/
// 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)
- {
- }
- }
}
}
// Get some settings
bool free_move = g_settings->getBool("free_move");
- bool footprints = g_settings->getBool("footprints");
// Get local player
LocalPlayer *lplayer = getLocalPlayer();
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)
- {
- }
- }
}
/*
}
}
}
-
-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)
{
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
*/
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;
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);
if(!digging)
{
client.interact(1, pointed_old);
- client.clearTempMod(pointed_old.node_undersurface);
+ client.setCrack(-1, v3s16(0,0,0));
dig_time = 0.0;
}
}
}
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));
- }
+ //TimeTaker timer("client.setTempMod");
+ //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;
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(
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));
/*
/*
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_"
/*
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)
def->wield_mesh->grab();
}
-
- if(node_mesh != NULL)
- node_mesh->drop();
+ // falling outside of here deletes node_mesh
}
}
#endif
#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<<")"
*/
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;
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;
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);
-
- mesh_expired = block->getMeshExpired();
+ //JMutexAutoLock lock(block->mesh_mutex);
- // 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
*/
// 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
&& m_control.range_all == false
&& 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;
Draw the faces of the block
*/
{
- JMutexAutoLock lock(block->mesh_mutex);
+ //JMutexAutoLock lock(block->mesh_mutex);
+
+ MapBlockMesh *mapBlockMesh = block->mesh;
+ assert(mapBlockMesh);
- scene::SMesh *mesh = block->mesh;
+ scene::SMesh *mesh = mapBlockMesh->getMesh();
assert(mesh);
-
+
u32 c = mesh->getMeshBufferCount();
bool stuff_actually_drawn = false;
for(u32 i=0; i<c; i++)
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);
}
}
-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: ";
#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
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);
reallocate();
#ifndef SERVER
- m_mesh_expired = false;
- mesh_mutex.Init();
+ //mesh_mutex.Init();
mesh = NULL;
- m_temp_mods_mutex.Init();
#endif
}
{
#ifndef SERVER
{
- JMutexAutoLock lock(mesh_mutex);
+ //JMutexAutoLock lock(mesh_mutex);
if(mesh)
{
- mesh->drop();
+ delete mesh;
mesh = NULL;
}
}
}
}
-#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.
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
#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
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){
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);
// 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();
*/
#ifndef SERVER // Only on client
- scene::SMesh *mesh;
- JMutex mesh_mutex;
+ MapBlockMesh *mesh;
+ //JMutex mesh_mutex;
#endif
NodeMetadataList *m_node_metadata;
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.
#include "light.h"
#include "mapblock.h"
#include "map.h"
-#include "main.h" // For g_settings and g_texturesource
-#include "settings.h"
+#include "main.h" // for g_profiler
#include "profiler.h"
#include "nodedef.h"
#include "gamedef.h"
+#include "mesh.h"
#include "content_mapblock.h"
-void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
+/*
+ MeshMakeData
+*/
+
+MeshMakeData::MeshMakeData(IGameDef *gamedef):
+ m_vmanip(),
+ m_blockpos(-1337,-1337,-1337),
+ m_crack_pos_relative(-1337, -1337, -1337),
+ m_smooth_lighting(false),
+ m_gamedef(gamedef)
+{}
+
+void MeshMakeData::fill(MapBlock *block)
{
- m_daynight_ratio = daynight_ratio;
m_blockpos = block->getPos();
v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
- /*
- There is no harm not copying the TempMods of the neighbors
- because they are already copied to this block
- */
- m_temp_mods.clear();
- block->copyTempMods(m_temp_mods);
-
/*
Copy data
*/
}
}
-void MeshMakeData::fillSingleNode(u32 daynight_ratio, MapNode *node)
+void MeshMakeData::fillSingleNode(MapNode *node)
{
- m_daynight_ratio = daynight_ratio;
m_blockpos = v3s16(0,0,0);
- m_temp_mods.clear();
v3s16 blockpos_nodes = v3s16(0,0,0);
VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
delete[] data;
}
+void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
+{
+ if(crack_level >= 0)
+ m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
+}
+
+void MeshMakeData::setSmoothLighting(bool smooth_lighting)
+{
+ m_smooth_lighting = smooth_lighting;
+}
+
+/*
+ Light and vertex color functions
+*/
+
+/*
+ Calculate non-smooth lighting at interior of node.
+ Single light bank.
+*/
+static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
+ MeshMakeData *data)
+{
+ INodeDefManager *ndef = data->m_gamedef->ndef();
+ u8 light = n.getLight(bank, ndef);
+
+ while(increment > 0)
+ {
+ light = undiminish_light(light);
+ --increment;
+ }
+ while(increment < 0)
+ {
+ light = diminish_light(light);
+ ++increment;
+ }
+
+ return decode_light(light);
+}
+
+/*
+ Calculate non-smooth lighting at interior of node.
+ Both light banks.
+*/
+u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
+{
+ u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
+ u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
+ return day | (night << 8);
+}
+
+/*
+ Calculate non-smooth lighting at face of node.
+ Single light bank.
+*/
+static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
+ v3s16 face_dir, MeshMakeData *data)
+{
+ INodeDefManager *ndef = data->m_gamedef->ndef();
+
+ u8 light;
+ u8 l1 = n.getLight(bank, ndef);
+ u8 l2 = n2.getLight(bank, ndef);
+ 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 decode_light(light);
+}
+
+/*
+ Calculate non-smooth lighting at face of node.
+ Both light banks.
+*/
+u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
+{
+ u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
+ u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
+ return day | (night << 8);
+}
+
+/*
+ Calculate smooth lighting at the XYZ- corner of p.
+ Single light bank.
+*/
+static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
+{
+ static v3s16 dirs8[8] = {
+ v3s16(0,0,0),
+ v3s16(0,0,1),
+ v3s16(0,1,0),
+ v3s16(0,1,1),
+ v3s16(1,0,0),
+ v3s16(1,1,0),
+ v3s16(1,0,1),
+ v3s16(1,1,1),
+ };
+
+ INodeDefManager *ndef = data->m_gamedef->ndef();
+
+ u16 ambient_occlusion = 0;
+ u16 light = 0;
+ u16 light_count = 0;
+ for(u32 i=0; i<8; i++)
+ {
+ MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
+ const ContentFeatures &f = ndef->get(n);
+ // Check f.solidness because fast-style leaves look
+ // better this way
+ if(f.param_type == CPT_LIGHT && f.solidness != 2)
+ {
+ light += decode_light(n.getLight(bank, ndef));
+ light_count++;
+ }
+ else if(n.getContent() != CONTENT_IGNORE)
+ {
+ ambient_occlusion++;
+ }
+ }
+
+ if(light_count == 0)
+ return 255;
+
+ light /= light_count;
+
+ if(ambient_occlusion > 4)
+ {
+ ambient_occlusion -= 4;
+ light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
+ }
+
+ return light;
+}
+
+/*
+ Calculate smooth lighting at the XYZ- corner of p.
+ Both light banks.
+*/
+static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
+{
+ u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
+ u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
+ return day | (night << 8);
+}
+
+/*
+ Calculate smooth lighting at the given corner of p.
+ Both light banks.
+*/
+u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
+{
+ if(corner.X == 1) p.X += 1;
+ else assert(corner.X == -1);
+ if(corner.Y == 1) p.Y += 1;
+ else assert(corner.Y == -1);
+ if(corner.Z == 1) p.Z += 1;
+ else assert(corner.Z == -1);
+
+ return getSmoothLight(p, data);
+}
+
+/*
+ Converts from day + night color values (0..255)
+ and a given daynight_ratio to the final SColor shown on screen.
+*/
+static void finalColorBlend(video::SColor& result,
+ u8 day, u8 night, u32 daynight_ratio)
+{
+ s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
+ s32 b = rg;
+
+ // Moonlight is blue
+ b += (day - night) / 13;
+ rg -= (day - night) / 23;
+
+ // Emphase blue a bit in darker places
+ // Each entry of this array represents a range of 8 blue levels
+ static u8 emphase_blue_when_dark[32] = {
+ 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ };
+ if(b < 0)
+ b = 0;
+ if(b > 255)
+ b = 255;
+ b += emphase_blue_when_dark[b / 8];
+
+ // Artificial light is yellow-ish
+ static u8 emphase_yellow_when_artificial[16] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
+ };
+ rg += emphase_yellow_when_artificial[night/16];
+ if(rg < 0)
+ rg = 0;
+ if(rg > 255)
+ rg = 255;
+
+ result.setRed(rg);
+ result.setGreen(rg);
+ result.setBlue(b);
+}
+
+/*
+ Mesh generation helpers
+*/
+
/*
vertex_dirs: v3s16[4]
*/
}
}
-video::SColor MapBlock_LightColor(u8 alpha, u8 light)
-{
-#if 0
- return video::SColor(alpha,light,light,light);
-#endif
- //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
- /*return video::SColor(alpha,light,light,MYMAX(0,
- pow((float)light/255.0, 0.8)*255.0));*/
-#if 1
- // Emphase blue a bit in darker places
- float lim = 80;
- float power = 0.8;
- if(light > lim)
- return video::SColor(alpha,light,light,light);
- else
- return video::SColor(alpha,light,light,MYMAX(0,
- pow((float)light/lim, power)*lim));
-#endif
-}
-
struct FastFace
{
TileSpec tile;
video::S3DVertex vertices[4]; // Precalculated vertices
};
-static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
- v3s16 dir, v3f scale, v3f posRelative_f,
- core::array<FastFace> &dest)
+static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
+ v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
{
FastFace face;
// Position is at the center of the cube.
v3f pos = p * BS;
- posRelative_f *= BS;
v3f vertex_pos[4];
v3s16 vertex_dirs[4];
vertex_pos[i].X *= scale.X;
vertex_pos[i].Y *= scale.Y;
vertex_pos[i].Z *= scale.Z;
- vertex_pos[i] += pos + posRelative_f;
+ vertex_pos[i] += pos;
}
f32 abs_scale = 1.;
v3f normal(dir.X, dir.Y, dir.Z);
u8 alpha = tile.alpha;
- /*u8 alpha = 255;
- if(tile.id == TILE_WATER)
- alpha = WATER_ALPHA;*/
float x0 = tile.texture.pos.X;
float y0 = tile.texture.pos.Y;
float w = tile.texture.size.X;
float h = tile.texture.size.Y;
- /*video::SColor c = MapBlock_LightColor(alpha, li);
-
- face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
- core::vector2d<f32>(x0+w*abs_scale, y0+h));
- face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
- core::vector2d<f32>(x0, y0+h));
- face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
- core::vector2d<f32>(x0, y0));
- face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
- core::vector2d<f32>(x0+w*abs_scale, y0));*/
-
face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
MapBlock_LightColor(alpha, li0),
core::vector2d<f32>(x0+w*abs_scale, y0+h));
core::vector2d<f32>(x0+w*abs_scale, y0));
face.tile = tile;
- //DEBUG
- //f->tile = TILE_STONE;
dest.push_back(face);
}
+
+/*
+ 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
+*/
+static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
+ INodeDefManager *ndef)
+{
+ *equivalent = false;
+
+ if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
+ return 0;
+
+ bool contents_differ = (m1 != m2);
+
+ const ContentFeatures &f1 = ndef->get(m1);
+ const ContentFeatures &f2 = ndef->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;
-static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr)
+ 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 nth node tile (0 <= n <= 5).
+*/
+TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
{
+ INodeDefManager *ndef = data->m_gamedef->ndef();
+ TileSpec spec = ndef->get(mn).tiles[tileindex];
+ // Apply temporary crack
+ if(p == data->m_crack_pos_relative)
+ {
+ spec.material_flags |= MATERIAL_FLAG_CRACK;
+ spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
+ }
+ return spec;
+}
+
+/*
+ Gets node tile given a face direction.
+*/
+TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
+{
+ INodeDefManager *ndef = data->m_gamedef->ndef();
+
// Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
// (0,0,1), (0,0,-1) or (0,0,0)
assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
// Get rotation for things like chests
- u8 facedir = node.getFaceDir(nodemgr);
+ u8 facedir = mn.getFaceDir(ndef);
assert(facedir <= 3);
static const u8 dir_to_tile[4 * 8] =
0, 3, 0, 5, 0, 4, 1, 2, // facedir = 2
0, 5, 0, 2, 0, 3, 1, 4, // facedir = 3
};
-
- return nodemgr->get(node).tiles[dir_to_tile[facedir*8 + dir_i]];
-}
-
-/*
- Gets node tile from any place relative to block.
- Returns TILE_NODE if doesn't exist or should not be drawn.
-*/
-TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
- NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef)
-{
- TileSpec spec;
- spec = getTile(mn, face_dir, ndef);
-
- /*
- Check temporary modifications on this node
- */
- /*core::map<v3s16, NodeMod>::Node *n;
- n = m_temp_mods.find(p);
- // If modified
- if(n != NULL)
- {
- struct NodeMod mod = n->getValue();*/
- NodeMod mod;
- if(temp_mods && temp_mods->get(p, &mod))
- {
- #if 0 // NODEMOD_CHANGECONTENT isn't used at the moment
- if(mod.type == NODEMOD_CHANGECONTENT)
- {
- MapNode mn2(mod.param);
- spec = getTile(mn2, face_dir, ndef);
- }
- #endif
- if(mod.type == NODEMOD_CRACK)
- {
- /*
- Get texture id, translate it to name, append stuff to
- name, get texture id
- */
-
- // Get original texture name
- u32 orig_id = spec.texture.id;
- std::string orig_name = tsrc->getTextureName(orig_id);
-
- // Create new texture name
- std::ostringstream os;
- os<<orig_name<<"^[crack"<<mod.param;
-
- // Get new texture
- u32 new_id = tsrc->getTextureId(os.str());
-
- /*dstream<<"MapBlock::getNodeTile(): Switching from "
- <<orig_name<<" to "<<os.str()<<" ("
- <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
-
- spec.texture = tsrc->getTexture(new_id);
- }
- }
-
- return spec;
-}
-
-static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap *temp_mods)
-{
- /*
- Check temporary modifications on this node
- */
- #if 0 // NODEMOD_CHANGECONTENT isn't used at the moment
- NodeMod mod;
- if(temp_mods && temp_mods->get(p, &mod))
- {
- if(mod.type == NODEMOD_CHANGECONTENT)
- {
- // Overrides content
- return mod.param;
- }
- if(mod.type == NODEMOD_CRACK)
- {
- /*
- Content doesn't change.
-
- face_contents works just like it should, because
- there should not be faces between differently cracked
- nodes.
-
- If a semi-transparent node is cracked in front an
- another one, it really doesn't matter whether there
- is a cracked face drawn in between or not.
- */
- }
- }
- #endif
-
- return mn.getContent();
-}
-
-v3s16 dirs8[8] = {
- v3s16(0,0,0),
- v3s16(0,0,1),
- v3s16(0,1,0),
- v3s16(0,1,1),
- v3s16(1,0,0),
- v3s16(1,1,0),
- v3s16(1,0,1),
- v3s16(1,1,1),
-};
-
-// Calculate lighting at the XYZ- corner of p
-static u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio,
- INodeDefManager *ndef)
-{
- u16 ambient_occlusion = 0;
- u16 light = 0;
- u16 light_count = 0;
- for(u32 i=0; i<8; i++)
- {
- MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
- if(ndef->get(n).param_type == CPT_LIGHT
- // Fast-style leaves look better this way
- && ndef->get(n).solidness != 2)
- {
- light += decode_light(n.getLightBlend(daynight_ratio, ndef));
- light_count++;
- }
- else
- {
- if(n.getContent() != CONTENT_IGNORE)
- ambient_occlusion++;
- }
- }
-
- if(light_count == 0)
- return 255;
-
- light /= light_count;
-
- if(ambient_occlusion > 4)
- {
- ambient_occlusion -= 4;
- light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
- }
-
- return light;
-}
-
-// Calculate lighting at the given corner of p
-static u8 getSmoothLight(v3s16 p, v3s16 corner,
- VoxelManipulator &vmanip, u32 daynight_ratio, INodeDefManager *ndef)
-{
- if(corner.X == 1) p.X += 1;
- else assert(corner.X == -1);
- if(corner.Y == 1) p.Y += 1;
- else assert(corner.Y == -1);
- if(corner.Z == 1) p.Z += 1;
- else assert(corner.Z == -1);
-
- return getSmoothLight(p, vmanip, daynight_ratio, ndef);
+ u8 tileindex = dir_to_tile[facedir*8 + dir_i];
+ return getNodeTileN(mn, p, tileindex, data);
}
static void getTileInfo(
// Input:
- v3s16 blockpos_nodes,
+ MeshMakeData *data,
v3s16 p,
v3s16 face_dir,
- u32 daynight_ratio,
- VoxelManipulator &vmanip,
- NodeModMap *temp_mods,
- bool smooth_lighting,
- IGameDef *gamedef,
// Output:
bool &makes_face,
v3s16 &p_corrected,
v3s16 &face_dir_corrected,
- u8 *lights,
+ u16 *lights,
TileSpec &tile
)
{
- ITextureSource *tsrc = gamedef->tsrc();
- INodeDefManager *ndef = gamedef->ndef();
+ VoxelManipulator &vmanip = data->m_vmanip;
+ INodeDefManager *ndef = data->m_gamedef->ndef();
+ v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
- TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods, tsrc, ndef);
- TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods, tsrc, ndef);
+ TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
+ TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
// This is hackish
- content_t content0 = getNodeContent(p, n0, temp_mods);
- content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
bool equivalent = false;
- u8 mf = face_contents(content0, content1, &equivalent, ndef);
+ u8 mf = face_contents(n0.getContent(), n1.getContent(),
+ &equivalent, ndef);
if(mf == 0)
{
if(equivalent)
tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
- if(smooth_lighting == false)
+ if(data->m_smooth_lighting == false)
{
lights[0] = lights[1] = lights[2] = lights[3] =
- decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir, ndef));
+ getFaceLight(n0, n1, face_dir, data);
}
else
{
getNodeVertexDirs(face_dir_corrected, vertex_dirs);
for(u16 i=0; i<4; i++)
{
- lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
- vertex_dirs[i], vmanip, daynight_ratio, ndef);
+ lights[i] = getSmoothLight(
+ blockpos_nodes + p_corrected,
+ vertex_dirs[i], data);
}
}
face_dir: unit vector with only one of x, y or z
*/
static void updateFastFaceRow(
- u32 daynight_ratio,
- v3f posRelative_f,
+ MeshMakeData *data,
v3s16 startpos,
- u16 length,
v3s16 translate_dir,
v3f translate_dir_f,
v3s16 face_dir,
v3f face_dir_f,
- core::array<FastFace> &dest,
- NodeModMap *temp_mods,
- VoxelManipulator &vmanip,
- v3s16 blockpos_nodes,
- bool smooth_lighting,
- IGameDef *gamedef)
+ core::array<FastFace> &dest)
{
v3s16 p = startpos;
bool makes_face = false;
v3s16 p_corrected;
v3s16 face_dir_corrected;
- u8 lights[4] = {0,0,0,0};
+ u16 lights[4] = {0,0,0,0};
TileSpec tile;
- getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
- vmanip, temp_mods, smooth_lighting, gamedef,
- makes_face, p_corrected, face_dir_corrected, lights, tile);
+ getTileInfo(data, p, face_dir,
+ makes_face, p_corrected, face_dir_corrected,
+ lights, tile);
- for(u16 j=0; j<length; j++)
+ for(u16 j=0; j<MAP_BLOCKSIZE; j++)
{
// If tiling can be done, this is set to false in the next step
bool next_is_different = true;
bool next_makes_face = false;
v3s16 next_p_corrected;
v3s16 next_face_dir_corrected;
- u8 next_lights[4] = {0,0,0,0};
+ u16 next_lights[4] = {0,0,0,0};
TileSpec next_tile;
// If at last position, there is nothing to compare to and
// the face must be drawn anyway
- if(j != length - 1)
+ if(j != MAP_BLOCKSIZE - 1)
{
p_next = p + translate_dir;
- getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
- vmanip, temp_mods, smooth_lighting, gamedef,
+ getTileInfo(data, p_next, face_dir,
next_makes_face, next_p_corrected,
next_face_dir_corrected, next_lights,
next_tile);
makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
sp, face_dir_corrected, scale,
- posRelative_f, dest);
+ dest);
g_profiler->avg("Meshgen: faces drawn by tiling", 0);
for(int i=1; i<continuous_tiles_count; i++){
}
}
-scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef)
+static void updateAllFastFaceRows(MeshMakeData *data,
+ core::array<FastFace> &dest)
{
- // 4-21ms for MAP_BLOCKSIZE=16
- // 24-155ms for MAP_BLOCKSIZE=32
- //TimeTaker timer1("makeMapBlockMesh()");
+ /*
+ Go through every y,z and get top(y+) faces in rows of x+
+ */
+ for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+ for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+ updateFastFaceRow(data,
+ v3s16(0,y,z),
+ v3s16(1,0,0), //dir
+ v3f (1,0,0),
+ v3s16(0,1,0), //face dir
+ v3f (0,1,0),
+ dest);
+ }
+ }
- core::array<FastFace> fastfaces_new;
+ /*
+ Go through every x,y and get right(x+) faces in rows of z+
+ */
+ for(s16 x=0; x<MAP_BLOCKSIZE; x++){
+ for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+ updateFastFaceRow(data,
+ v3s16(x,y,0),
+ v3s16(0,0,1), //dir
+ v3f (0,0,1),
+ v3s16(1,0,0), //face dir
+ v3f (1,0,0),
+ dest);
+ }
+ }
- v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
-
- // floating point conversion
- v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
-
/*
- Some settings
+ Go through every y,z and get back(z+) faces in rows of x+
*/
- //bool new_style_water = g_settings->getBool("new_style_water");
- //bool new_style_leaves = g_settings->getBool("new_style_leaves");
- bool smooth_lighting = g_settings->getBool("smooth_lighting");
-
+ for(s16 z=0; z<MAP_BLOCKSIZE; z++){
+ for(s16 y=0; y<MAP_BLOCKSIZE; y++){
+ updateFastFaceRow(data,
+ v3s16(0,y,z),
+ v3s16(1,0,0), //dir
+ v3f (1,0,0),
+ v3s16(0,0,1), //face dir
+ v3f (0,0,1),
+ dest);
+ }
+ }
+}
+
+/*
+ MapBlockMesh
+*/
+
+MapBlockMesh::MapBlockMesh(MeshMakeData *data):
+ m_mesh(new scene::SMesh()),
+ m_gamedef(data->m_gamedef),
+ m_animation_force_timer(0), // force initial animation
+ m_last_crack(-1),
+ m_crack_materials(),
+ m_last_daynight_ratio((u32) -1),
+ m_daynight_diffs()
+{
+ // 4-21ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
+ // 24-155ms for MAP_BLOCKSIZE=32 (NOTE: probably outdated)
+ //TimeTaker timer1("MapBlockMesh()");
+
+ core::array<FastFace> fastfaces_new;
+
/*
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(y+) faces in rows of x+
- */
- for(s16 y=0; y<MAP_BLOCKSIZE; y++){
- for(s16 z=0; z<MAP_BLOCKSIZE; z++){
- updateFastFaceRow(data->m_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,
- &data->m_temp_mods,
- data->m_vmanip,
- blockpos_nodes,
- smooth_lighting,
- gamedef);
- }
- }
- /*
- Go through every x,y and get right(x+) faces in rows of z+
- */
- for(s16 x=0; x<MAP_BLOCKSIZE; x++){
- for(s16 y=0; y<MAP_BLOCKSIZE; y++){
- updateFastFaceRow(data->m_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,
- &data->m_temp_mods,
- data->m_vmanip,
- blockpos_nodes,
- smooth_lighting,
- gamedef);
- }
- }
- /*
- Go through every y,z and get back(z+) faces in rows of x+
- */
- for(s16 z=0; z<MAP_BLOCKSIZE; z++){
- for(s16 y=0; y<MAP_BLOCKSIZE; y++){
- updateFastFaceRow(data->m_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,
- &data->m_temp_mods,
- data->m_vmanip,
- blockpos_nodes,
- smooth_lighting,
- gamedef);
- }
- }
+ // 4-23ms for MAP_BLOCKSIZE=16 (NOTE: probably outdated)
+ //TimeTaker timer2("updateAllFastFaceRows()");
+ updateAllFastFaceRows(data, fastfaces_new);
}
-
// End of slow part
/*
- Convert FastFaces to SMesh
+ Convert FastFaces to MeshCollector
*/
MeshCollector collector;
- if(fastfaces_new.size() > 0)
{
// avg 0ms (100ms spikes when loading textures the first time)
- //TimeTaker timer2("updateMesh() mesh building");
-
- video::SMaterial material;
- material.setFlag(video::EMF_LIGHTING, false);
- material.setFlag(video::EMF_BACK_FACE_CULLING, true);
- material.setFlag(video::EMF_BILINEAR_FILTER, false);
- material.setFlag(video::EMF_FOG_ENABLE, true);
- //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
- //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
- material.MaterialType
- = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+ // (NOTE: probably outdated)
+ //TimeTaker timer2("MeshCollector building");
for(u32 i=0; i<fastfaces_new.size(); i++)
{
const u16 indices[] = {0,1,2,2,3,0};
const u16 indices_alternate[] = {0,1,3,2,3,1};
- video::ITexture *texture = f.tile.texture.atlas;
- if(texture == NULL)
+ if(f.tile.texture.atlas == NULL)
continue;
- material.setTexture(0, texture);
-
- f.tile.applyMaterialOptions(material);
-
const u16 *indices_p = indices;
/*
Revert triangles for nicer looking gradient if vertices
1 and 3 have same color or 0 and 2 have different color.
+ getRed() is the day color.
*/
- if(f.vertices[0].Color != f.vertices[2].Color
- || f.vertices[1].Color == f.vertices[3].Color)
+ if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
+ || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
indices_p = indices_alternate;
- collector.append(material, f.vertices, 4, indices_p, 6);
+ collector.append(f.tile, f.vertices, 4, indices_p, 6);
}
}
- whatever
*/
- mapblock_mesh_generate_special(data, collector, gamedef);
+ mapblock_mesh_generate_special(data, collector);
- /*
- Add stuff from collector to mesh
- */
-
- scene::SMesh *mesh_new = NULL;
- mesh_new = new scene::SMesh();
-
- collector.fillMesh(mesh_new);
/*
- Do some stuff to the mesh
+ Convert MeshCollector to SMesh
+ Also store animation info
*/
+ for(u32 i = 0; i < collector.prebuffers.size(); i++)
+ {
+ PreMeshBuffer &p = collector.prebuffers[i];
+ /*dstream<<"p.vertices.size()="<<p.vertices.size()
+ <<", p.indices.size()="<<p.indices.size()
+ <<std::endl;*/
+
+ // Generate animation data
+ // - Cracks
+ if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
+ {
+ ITextureSource *tsrc = data->m_gamedef->tsrc();
+ std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
+ if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
+ crack_basename += "^[cracko";
+ else
+ crack_basename += "^[crack";
+ m_crack_materials.insert(std::make_pair(i, crack_basename));
+ }
+ // - Lighting
+ for(u32 j = 0; j < p.vertices.size(); j++)
+ {
+ video::SColor &vc = p.vertices[j].Color;
+ u8 day = vc.getRed();
+ u8 night = vc.getGreen();
+ finalColorBlend(vc, day, night, 1000);
+ if(day != night)
+ m_daynight_diffs[i][j] = std::make_pair(day, night);
+ }
+
- mesh_new->recalculateBoundingBox();
+ // Create material
+ video::SMaterial material;
+ material.setFlag(video::EMF_LIGHTING, false);
+ material.setFlag(video::EMF_BACK_FACE_CULLING, true);
+ material.setFlag(video::EMF_BILINEAR_FILTER, false);
+ material.setFlag(video::EMF_FOG_ENABLE, true);
+ //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
+ //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
+ material.MaterialType
+ = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+ material.setTexture(0, p.tile.texture.atlas);
+ p.tile.applyMaterialOptions(material);
+
+ // Create meshbuffer
+
+ // This is a "Standard MeshBuffer",
+ // it's a typedeffed CMeshBuffer<video::S3DVertex>
+ scene::SMeshBuffer *buf = new scene::SMeshBuffer();
+ // Set material
+ buf->Material = material;
+ // Add to mesh
+ m_mesh->addMeshBuffer(buf);
+ // Mesh grabbed it
+ buf->drop();
+ buf->append(p.vertices.pointer(), p.vertices.size(),
+ p.indices.pointer(), p.indices.size());
+ }
/*
- Delete new mesh if it is empty
+ Do some stuff to the mesh
*/
- if(mesh_new->getMeshBufferCount() == 0)
- {
- mesh_new->drop();
- mesh_new = NULL;
- }
+ translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
+ m_mesh->recalculateBoundingBox(); // translateMesh already does this
- if(mesh_new)
+ if(m_mesh)
{
#if 0
// Usually 1-700 faces and 1-7 materials
std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
- <<"and uses "<<mesh_new->getMeshBufferCount()
+ <<"and uses "<<m_mesh->getMeshBufferCount()
<<" materials (meshbuffers)"<<std::endl;
#endif
// 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);
+ //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
/*
NOTE: If that is enabled, some kind of a queue to the main
the hardware buffer and then delete the mesh
*/
}
-
- return mesh_new;
//std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
+
+ // Check if animation is required for this mesh
+ m_has_animation =
+ !m_crack_materials.empty() ||
+ !m_daynight_diffs.empty();
+}
+
+MapBlockMesh::~MapBlockMesh()
+{
+ m_mesh->drop();
+ m_mesh = NULL;
}
+bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
+{
+ if(!m_has_animation)
+ {
+ m_animation_force_timer = 100000;
+ return false;
+ }
+
+ m_animation_force_timer = myrand_range(5, 100);
+
+ // Cracks
+ if(crack != m_last_crack)
+ {
+ for(std::map<u32, std::string>::iterator
+ i = m_crack_materials.begin();
+ i != m_crack_materials.end(); i++)
+ {
+ scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+ std::string basename = i->second;
+
+ // Create new texture name from original
+ ITextureSource *tsrc = m_gamedef->getTextureSource();
+ std::ostringstream os;
+ os<<basename<<crack;
+ AtlasPointer ap = tsrc->getTexture(os.str());
+ buf->getMaterial().setTexture(0, ap.atlas);
+ }
+
+ m_last_crack = crack;
+ }
+
+ // Day-night transition
+ if(daynight_ratio != m_last_daynight_ratio)
+ {
+ for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
+ i = m_daynight_diffs.begin();
+ i != m_daynight_diffs.end(); i++)
+ {
+ scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
+ video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
+ for(std::map<u32, std::pair<u8, u8 > >::iterator
+ j = i->second.begin();
+ j != i->second.end(); j++)
+ {
+ u32 vertexIndex = j->first;
+ u8 day = j->second.first;
+ u8 night = j->second.second;
+ finalColorBlend(vertices[vertexIndex].Color,
+ day, night, daynight_ratio);
+ }
+ }
+ m_last_daynight_ratio = daynight_ratio;
+ }
+
+ return true;
+}
+
+/*
+ MeshCollector
+*/
+
+void MeshCollector::append(const TileSpec &tile,
+ const video::S3DVertex *vertices, u32 numVertices,
+ const u16 *indices, u32 numIndices)
+{
+ PreMeshBuffer *p = NULL;
+ for(u32 i=0; i<prebuffers.size(); i++)
+ {
+ PreMeshBuffer &pp = prebuffers[i];
+ if(pp.tile != tile)
+ continue;
+
+ p = &pp;
+ break;
+ }
+
+ if(p == NULL)
+ {
+ PreMeshBuffer pp;
+ pp.tile = tile;
+ prebuffers.push_back(pp);
+ p = &prebuffers[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]);
+ }
+}
#define MAPBLOCK_MESH_HEADER
#include "common_irrlicht.h"
-#include "mapblock_nodemod.h"
#include "tile.h"
#include "voxel.h"
+#include <map>
class IGameDef;
Mesh making stuff
*/
-/*
- This is used because CMeshBuffer::append() is very slow
-*/
-struct PreMeshBuffer
+
+class MapBlock;
+
+struct MeshMakeData
{
- video::SMaterial material;
- core::array<u16> indices;
- core::array<video::S3DVertex> vertices;
+ 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(MapBlock *block);
+
+ /*
+ Set up with only a single node at (1,1,1)
+ */
+ 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);
};
-class MeshCollector
+/*
+ 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:
- void append(
- video::SMaterial material,
- const video::S3DVertex* const vertices,
- u32 numVertices,
- const u16* const indices,
- u32 numIndices
- )
+ // 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()
{
- 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]);
- }
+ return m_mesh;
}
- void fillMesh(scene::SMesh *mesh)
+ bool isAnimationForced() const
{
- /*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());
- }
+ return m_animation_force_timer == 0;
+ }
+
+ void decreaseAnimationForceTimer()
+ {
+ if(m_animation_force_timer > 0)
+ m_animation_force_timer--;
}
private:
- core::array<PreMeshBuffer> m_prebuffers;
+ 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;
};
-// 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
+/*
+ This is used because CMeshBuffer::append() is very slow
+*/
+struct PreMeshBuffer
{
- u32 m_daynight_ratio;
- NodeModMap m_temp_mods;
- VoxelManipulator m_vmanip;
- v3s16 m_blockpos;
-
- /*
- Copy central data directly from block, and other data from
- parent of block.
- */
- void fill(u32 daynight_ratio, MapBlock *block);
+ TileSpec tile;
+ core::array<u16> indices;
+ core::array<video::S3DVertex> vertices;
+};
- /*
- Set up with only a single node at (1,1,1)
- */
- void fillSingleNode(u32 daynight_ratio, MapNode *node);
+struct MeshCollector
+{
+ core::array<PreMeshBuffer> prebuffers;
+
+ void append(const TileSpec &material,
+ const video::S3DVertex *vertices, u32 numVertices,
+ const u16 *indices, u32 numIndices);
};
-// This is the highest-level function in here
-scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef);
+// 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
+++ /dev/null
-/*
-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
-
// 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
-
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
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()
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;
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
// 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;
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").
*/
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()
{
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);
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();
*/
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)
+ {
+ overlay(baseimg, img_crack_scaled);
+ }
+ else
{
- // 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);
+ 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();
}
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)
{
}
- bool operator==(const AtlasPointer &other)
+ bool operator==(const AtlasPointer &other) const
{
return (
id == other.id
);*/
}
+ 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; }
{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){};
};
// 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.
{
}
- bool operator==(TileSpec &other)
+ bool operator==(const TileSpec &other) const
{
return (
texture == other.texture &&
material_flags == other.material_flags
);
}
+
+ bool operator!=(const TileSpec &other) const
+ {
+ return !(*this == other);
+ }
// Sets everything else except the texture in the material
void applyMaterialOptions(video::SMaterial &material) const
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