From 807a0d313ba667356ee8af8ef5ae82b6c4881d15 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Tue, 13 Mar 2012 18:56:12 +0100 Subject: [PATCH] MapBlockMesh, mesh animation system, urgent mesh updates, athmospheric light, removed footprints --- src/client.cpp | 214 +++++---- src/client.h | 28 +- src/content_mapblock.cpp | 323 +++++-------- src/content_mapblock.h | 9 +- src/environment.cpp | 62 --- src/environment.h | 18 - src/game.cpp | 20 +- src/itemdef.cpp | 27 +- src/map.cpp | 321 +++---------- src/map.h | 28 -- src/mapblock.cpp | 87 +--- src/mapblock.h | 109 +---- src/mapblock_mesh.cpp | 968 ++++++++++++++++++++++++--------------- src/mapblock_mesh.h | 220 ++++----- src/mapblock_nodemod.h | 114 ----- src/mapnode.cpp | 112 ----- src/mapnode.h | 40 -- src/nodedef.cpp | 49 +- src/nodedef.h | 5 +- src/tile.cpp | 144 ++++-- src/tile.h | 22 +- src/utility.h | 19 +- 22 files changed, 1205 insertions(+), 1734 deletions(-) delete mode 100644 src/mapblock_nodemod.h diff --git a/src/client.cpp b/src/client.cpp index 14f93a1a..203a905b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -82,8 +82,9 @@ MeshUpdateQueue::~MeshUpdateQueue() { JMutexAutoLock lock(m_mutex); - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) + for(std::vector::iterator + i = m_queue.begin(); + i != m_queue.end(); i++) { QueuedMeshUpdate *q = *i; delete q; @@ -93,7 +94,7 @@ MeshUpdateQueue::~MeshUpdateQueue() /* peer_id=0 adds with nobody to send to */ -void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server) +void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server, bool urgent) { DSTACK(__FUNCTION_NAME); @@ -101,12 +102,16 @@ void MeshUpdateQueue::addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_se JMutexAutoLock lock(m_mutex); + if(urgent) + m_urgents.insert(p); + /* Find if block is already in queue. If it is, update the data and quit. */ - core::list::Iterator i; - for(i=m_queue.begin(); i!=m_queue.end(); i++) + for(std::vector::iterator + i = m_queue.begin(); + i != m_queue.end(); i++) { QueuedMeshUpdate *q = *i; if(q->p == p) @@ -136,12 +141,19 @@ QueuedMeshUpdate * MeshUpdateQueue::pop() { JMutexAutoLock lock(m_mutex); - core::list::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::iterator + i = m_queue.begin(); + i != m_queue.end(); i++) + { + QueuedMeshUpdate *q = *i; + if(must_be_urgent && m_urgents.count(q->p) == 0) + continue; + m_queue.erase(i); + m_urgents.erase(q->p); + return q; + } + return NULL; } /* @@ -178,8 +190,12 @@ void * MeshUpdateThread::Thread() ScopeProfiler sp(g_profiler, "Client: Mesh making"); - scene::SMesh *mesh_new = NULL; - mesh_new = makeMapBlockMesh(q->data, m_gamedef); + MapBlockMesh *mesh_new = new MapBlockMesh(q->data); + if(mesh_new->getMesh()->getMeshBufferCount() == 0) + { + delete mesh_new; + mesh_new = NULL; + } MeshUpdateResult r; r.p = q->p; @@ -227,6 +243,9 @@ Client::Client( m_inventory_updated(false), m_inventory_from_server(NULL), m_inventory_from_server_age(0.0), + m_animation_time(0), + m_crack_level(-1), + m_crack_pos(0,0,0), m_time_of_day(0), m_map_seed(0), m_password(password), @@ -309,6 +328,10 @@ void Client::step(float dtime) else m_ignore_damage_timer = 0.0; + m_animation_time += dtime; + if(m_animation_time > 60.0) + m_animation_time -= 60.0; + //infostream<<"Client steps "<replaceMesh(r.mesh); + //JMutexAutoLock lock(block->mesh_mutex); + + // Delete the old mesh + if(block->mesh != NULL) + { + // TODO: Remove hardware buffers of meshbuffers of block->mesh + delete block->mesh; + block->mesh = NULL; + } + + // Replace with the new mesh + block->mesh = r.mesh; } if(r.ack_block_to_server) { @@ -868,9 +902,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) //TimeTaker t1("TOCLIENT_REMOVENODE"); - // This will clear the cracking animation after digging - ((ClientMap&)m_env.getMap()).clearTempMod(p); - removeNode(p); } else if(command == TOCLIENT_ADDNODE) @@ -960,13 +991,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) m_con.Send(PEER_ID_SERVER, 1, reply, true); #endif - /* - Update Mesh of this block and blocks at x-, y- and z-. - Environment should not be locked as it interlocks with the - main thread, from which is will want to retrieve textures. - */ - - //m_env.getClientMap().updateMeshes(block->getPos(), getDayNightRatio()); /* Add it to mesh update queue and set it to be acknowledged after update. */ @@ -1837,12 +1861,14 @@ void Client::removeNode(v3s16 p) { } + // add urgent task to update the modified node + addUpdateMeshTaskForNode(p, false, true); + for(core::map::Iterator i = modified_blocks.getIterator(); i.atEnd() == false; i++) { v3s16 p = i.getNode()->getKey(); - //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio()); addUpdateMeshTaskWithEdge(p); } } @@ -1863,14 +1889,13 @@ void Client::addNode(v3s16 p, MapNode n) catch(InvalidPositionException &e) {} - //TimeTaker timer2("Client::addNode(): updateMeshes"); + //TimeTaker timer2("Client::addNode(): addUpdateMeshTaskWithEdge"); for(core::map::Iterator i = modified_blocks.getIterator(); i.atEnd() == false; i++) { v3s16 p = i.getNode()->getKey(); - //m_env.getClientMap().updateMeshes(p, m_env.getDayNightRatio()); addUpdateMeshTaskWithEdge(p); } } @@ -2051,51 +2076,47 @@ core::list Client::getConnectedPlayerNames() 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 affected_blocks; - ((ClientMap&)m_env.getMap()).setTempMod(p, mod, - &affected_blocks); + m_crack_level = level; + m_crack_pos = pos; - for(core::map::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 affected_blocks; - ((ClientMap&)m_env.getMap()).clearTempMod(p, - &affected_blocks); + return m_env.getDayNightRatio(); +} - for(core::map::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) @@ -2131,10 +2152,12 @@ void Client::typeChatMessage(const std::wstring &message) } } -void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server) +void Client::addUpdateMeshTask(v3s16 p, bool ack_to_server, bool urgent) { /*infostream<<"Client::addUpdateMeshTask(): " <<"("<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 " <replaceMesh(mesh_new); - delete data; - } -#endif - - /* - Mark mesh as non-expired at this point so that it can already - be marked as expired again if the data changes - */ - b->setMeshExpired(false); } -void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server) +void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server, bool urgent) { /*{ v3s16 p = blockpos; @@ -2195,25 +2202,66 @@ void Client::addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server) try{ v3s16 p = blockpos + v3s16(0,0,0); //MapBlock *b = m_env.getMap().getBlockNoCreate(p); - addUpdateMeshTask(p, ack_to_server); + addUpdateMeshTask(p, ack_to_server, urgent); } catch(InvalidPositionException &e){} // Leading edge try{ v3s16 p = blockpos + v3s16(-1,0,0); - addUpdateMeshTask(p); + addUpdateMeshTask(p, false, urgent); } catch(InvalidPositionException &e){} try{ v3s16 p = blockpos + v3s16(0,-1,0); - addUpdateMeshTask(p); + addUpdateMeshTask(p, false, urgent); } catch(InvalidPositionException &e){} try{ v3s16 p = blockpos + v3s16(0,0,-1); - addUpdateMeshTask(p); + addUpdateMeshTask(p, false, urgent); + } + catch(InvalidPositionException &e){} +} + +void Client::addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server, bool urgent) +{ + { + v3s16 p = nodepos; + infostream<<"Client::addUpdateMeshTaskForNode(): " + <<"("< +#include +#include #include "clientobject.h" #include "utility.h" // For IntervalLimiter #include "gamedef.h" @@ -34,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" struct MeshMakeData; +class MapBlockMesh; class IGameDef; class IWritableTextureSource; class IWritableItemDefManager; @@ -71,7 +74,8 @@ public: /* peer_id=0 adds with nobody to send to */ - void addBlock(v3s16 p, MeshMakeData *data, bool ack_block_to_server); + void addBlock(v3s16 p, MeshMakeData *data, + bool ack_block_to_server, bool urgent); // Returned pointer must be deleted // Returns NULL if queue is empty @@ -84,14 +88,15 @@ public: } private: - core::list m_queue; + std::vector m_queue; + std::set m_urgents; JMutex m_mutex; }; struct MeshUpdateResult { v3s16 p; - scene::SMesh *mesh; + MapBlockMesh *mesh; bool ack_block_to_server; MeshUpdateResult(): @@ -260,13 +265,15 @@ public: core::list getConnectedPlayerNames(); + float getAnimationTime(); + + int getCrackLevel(); + void setCrack(int level, v3s16 pos); + u32 getDayNightRatio(); u16 getHP(); - void setTempMod(v3s16 p, NodeMod mod); - void clearTempMod(v3s16 p); - float getAvgRtt() { try{ @@ -281,9 +288,10 @@ public: u64 getMapSeed(){ return m_map_seed; } - void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false); + void addUpdateMeshTask(v3s16 blockpos, bool ack_to_server=false, bool urgent=false); // Including blocks at appropriate edges - void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false); + void addUpdateMeshTaskWithEdge(v3s16 blockpos, bool ack_to_server=false, bool urgent=false); + void addUpdateMeshTaskForNode(v3s16 nodepos, bool ack_to_server=false, bool urgent=false); // Get event from queue. CE_NONE is returned if queue is empty. ClientEvent getClientEvent(); @@ -352,6 +360,10 @@ private: float m_inventory_from_server_age; core::map 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 diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index 402c4e8b..9a427a44 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -29,9 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc., // Create a cuboid. // collector - the MeshCollector for the resulting polygons // box - the position and size of the box -// materials - the materials to use (for all 6 faces) -// pa - texture atlas pointers for the materials -// matcount - number of entries in "materials" and "pa", 1<=matcount<=6 +// tiles - the tiles (materials) to use (for all 6 faces) +// tilecount - number of entries in tiles, 1<=tilecount<=6 // c - vertex colour - used for all // txc - texture coordinates - this is a list of texture coordinates // for the opposite corners of each face - therefore, there @@ -41,10 +40,10 @@ with this program; if not, write to the Free Software Foundation, Inc., // (compatible with ContentFeatures). If you specified 0,0,1,1 // for each face, that would be the same as passing NULL. void makeCuboid(MeshCollector *collector, const aabb3f &box, - const video::SMaterial *materials, const AtlasPointer *pa, int matcount, + const TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc) { - assert(matcount >= 1); + assert(tilecount >= 1 && tilecount <= 6); v3f min = box.MinEdge; v3f max = box.MaxEdge; @@ -98,9 +97,9 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, for(s32 j=0; j<24; j++) { - int matindex = MYMIN(j/4, matcount-1); - vertices[j].TCoords *= pa[matindex].size; - vertices[j].TCoords += pa[matindex].pos; + int tileindex = MYMIN(j/4, tilecount-1); + vertices[j].TCoords *= tiles[tileindex].texture.size; + vertices[j].TCoords += tiles[tileindex].texture.pos; } u16 indices[] = {0,1,2,2,3,0}; @@ -108,17 +107,16 @@ void makeCuboid(MeshCollector *collector, const aabb3f &box, // Add to mesh collector for(s32 j=0; j<24; j+=4) { - int matindex = MYMIN(j/4, matcount-1); - collector->append(materials[matindex], + int tileindex = MYMIN(j/4, tilecount-1); + collector->append(tiles[tileindex], vertices+j, 4, indices, 6); } } void mapblock_mesh_generate_special(MeshMakeData *data, - MeshCollector &collector, IGameDef *gamedef) + MeshCollector &collector) { - INodeDefManager *nodedef = gamedef->ndef(); - ITextureSource *tsrc = gamedef->getTextureSource(); + INodeDefManager *nodedef = data->m_gamedef->ndef(); // 0ms //TimeTaker timer("mapblock_mesh_generate_special()"); @@ -134,14 +132,6 @@ void mapblock_mesh_generate_special(MeshMakeData *data, v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE; - /*// General ground material for special output - // Texture is modified just before usage - video::SMaterial material_general; - material_general.setFlag(video::EMF_LIGHTING, false); - material_general.setFlag(video::EMF_BILINEAR_FILTER, false); - material_general.setFlag(video::EMF_FOG_ENABLE, true); - material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;*/ - for(s16 z=0; zget(n).special_materials[0]); - //assert(nodedef->get(n).special_materials[1]); - assert(nodedef->get(n).special_aps[0]); - - video::SMaterial &liquid_material = - *nodedef->get(n).special_materials[0]; - /*video::SMaterial &liquid_material_bfculled = - *nodedef->get(n).special_materials[1];*/ - AtlasPointer &pa_liquid1 = - *nodedef->get(n).special_aps[0]; + TileSpec tile_liquid = f.special_tiles[0]; + AtlasPointer &pa_liquid = tile_liquid.texture; bool top_is_air = false; MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); @@ -186,64 +168,55 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(top_is_air == false) continue; - u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef)); - video::SColor c = MapBlock_LightColor( - nodedef->get(n).alpha, l); + u16 l = getInteriorLight(n, 0, data); + video::SColor c = MapBlock_LightColor(f.alpha, l); video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x0(), pa_liquid1.y1()), + pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x1(), pa_liquid1.y1()), + pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, - pa_liquid1.x1(), pa_liquid1.y0()), + pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, - pa_liquid1.x0(), pa_liquid1.y0()), + pa_liquid.x0(), pa_liquid.y0()), }; + v3f offset(p.X, p.Y + (-0.5+node_liquid_level)*BS, p.Z); for(s32 i=0; i<4; i++) { - vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS; - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[i].Pos += offset; } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector - collector.append(liquid_material, vertices, 4, indices, 6); + collector.append(tile_liquid, vertices, 4, indices, 6); break;} case NDT_FLOWINGLIQUID: { /* Add flowing liquid to mesh */ - assert(nodedef->get(n).special_materials[0]); - assert(nodedef->get(n).special_materials[1]); - assert(nodedef->get(n).special_aps[0]); - - video::SMaterial &liquid_material = - *nodedef->get(n).special_materials[0]; - video::SMaterial &liquid_material_bfculled = - *nodedef->get(n).special_materials[1]; - AtlasPointer &pa_liquid1 = - *nodedef->get(n).special_aps[0]; + TileSpec tile_liquid = f.special_tiles[0]; + TileSpec tile_liquid_bfculled = f.special_tiles[1]; + AtlasPointer &pa_liquid = tile_liquid.texture; bool top_is_same_liquid = false; MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z)); - content_t c_flowing = nodedef->getId(nodedef->get(n).liquid_alternative_flowing); - content_t c_source = nodedef->getId(nodedef->get(n).liquid_alternative_source); + content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing); + content_t c_source = nodedef->getId(f.liquid_alternative_source); if(ntop.getContent() == c_flowing || ntop.getContent() == c_source) top_is_same_liquid = true; - u8 l = 0; + u16 l = 0; // Use the light of the node on top if possible if(nodedef->get(ntop).param_type == CPT_LIGHT) - l = decode_light(ntop.getLightBlend(data->m_daynight_ratio, nodedef)); + l = getInteriorLight(ntop, 0, data); // Otherwise use the light of this node (the liquid) else - l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef)); - video::SColor c = MapBlock_LightColor( - nodedef->get(n).alpha, l); + l = getInteriorLight(n, 0, data); + video::SColor c = MapBlock_LightColor(f.alpha, l); // Neighbor liquid levels (key = relative position) // Includes current node @@ -393,20 +366,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data, // Use backface culled material if neighbor doesn't have a // solidness of 0 - video::SMaterial *current_material = &liquid_material; + const TileSpec *current_tile = &tile_liquid; if(n_feat.solidness != 0 || n_feat.visual_solidness != 0) - current_material = &liquid_material_bfculled; + current_tile = &tile_liquid_bfculled; video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x0(), pa_liquid1.y1()), + pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x1(), pa_liquid1.y1()), + pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x1(), pa_liquid1.y0()), + pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x0(), pa_liquid1.y0()), + pa_liquid.x0(), pa_liquid.y0()), }; /* @@ -464,12 +437,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[j].Pos.Z *= 0.98; }*/ - vertices[j].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[j].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector - collector.append(*current_material, vertices, 4, indices, 6); + collector.append(*current_tile, vertices, 4, indices, 6); } /* @@ -481,13 +454,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x0(), pa_liquid1.y1()), + pa_liquid.x0(), pa_liquid.y1()), video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, - pa_liquid1.x1(), pa_liquid1.y1()), + pa_liquid.x1(), pa_liquid.y1()), video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, - pa_liquid1.x1(), pa_liquid1.y0()), + pa_liquid.x1(), pa_liquid.y0()), video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, - pa_liquid1.x0(), pa_liquid1.y0()), + pa_liquid.x0(), pa_liquid.y0()), }; // This fixes a strange bug @@ -499,27 +472,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data, //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)]; s32 j = corner_resolve[i]; vertices[i].Pos.Y += corner_levels[j]; - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector - collector.append(liquid_material, vertices, 4, indices, 6); + collector.append(tile_liquid, vertices, 4, indices, 6); } break;} case NDT_GLASSLIKE: { - video::SMaterial material_glass; - material_glass.setFlag(video::EMF_LIGHTING, false); - material_glass.setFlag(video::EMF_BILINEAR_FILTER, false); - material_glass.setFlag(video::EMF_FOG_ENABLE, true); - material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - TileSpec tile_glass = getNodeTile(n, p, v3s16(0,0,0), - &data->m_temp_mods, tsrc, nodedef); - AtlasPointer pa_glass = tile_glass.texture; - material_glass.setTexture(0, pa_glass.atlas); - - 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++) @@ -535,13 +501,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, - pa_glass.x0(), pa_glass.y1()), + ap.x0(), ap.y1()), video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, - pa_glass.x1(), pa_glass.y1()), + ap.x1(), ap.y1()), video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, - pa_glass.x1(), pa_glass.y0()), + ap.x1(), ap.y0()), video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, - pa_glass.x0(), pa_glass.y0()), + ap.x0(), ap.y0()), }; // Rotations in the g_6dirs format @@ -565,36 +531,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data, vertices[i].Pos.rotateXZBy(90); for(u16 i=0; i<4; i++){ - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector - collector.append(material_glass, vertices, 4, indices, 6); + collector.append(tile, vertices, 4, indices, 6); } break;} case NDT_ALLFACES: { - video::SMaterial material_leaves1; - material_leaves1.setFlag(video::EMF_LIGHTING, false); - material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false); - material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); - material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - TileSpec tile_leaves1 = getNodeTile(n, p, v3s16(0,0,0), - &data->m_temp_mods, tsrc, nodedef); - AtlasPointer pa_leaves1 = tile_leaves1.texture; - material_leaves1.setTexture(0, pa_leaves1.atlas); - - 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 @@ -604,28 +562,23 @@ void mapblock_mesh_generate_special(MeshMakeData *data, { v3s16 dir = n.getWallMountedDir(nodedef); - AtlasPointer ap(0); + u8 tileindex = 0; if(dir == v3s16(0,-1,0)){ - ap = f.tiles[0].texture; // floor + tileindex = 0; // floor } else if(dir == v3s16(0,1,0)){ - ap = f.tiles[1].texture; // ceiling + tileindex = 1; // ceiling // For backwards compatibility } else if(dir == v3s16(0,0,0)){ - ap = f.tiles[0].texture; // floor + tileindex = 0; // floor } else { - ap = f.tiles[2].texture; // side + tileindex = 2; // side } - // Set material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - material.setFlag(video::EMF_FOG_ENABLE, true); - //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - material.MaterialType - = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - material.setTexture(0, ap.atlas); + TileSpec tile = getNodeTileN(n, p, tileindex, data); + tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING; + tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; + + AtlasPointer ap = tile.texture; video::SColor c(255,255,255,255); @@ -657,27 +610,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXZBy(-45); - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector - collector.append(material, vertices, 4, indices, 6); + collector.append(tile, vertices, 4, indices, 6); break;} case NDT_SIGNLIKE: { - // Set material - video::SMaterial material; - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BACK_FACE_CULLING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - material.setFlag(video::EMF_FOG_ENABLE, true); - material.MaterialType - = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - AtlasPointer ap = f.tiles[0].texture; - material.setTexture(0, ap.atlas); - - 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; @@ -711,24 +658,20 @@ void mapblock_mesh_generate_special(MeshMakeData *data, if(dir == v3s16(0,1,0)) vertices[i].Pos.rotateXYBy(90); - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector - collector.append(material, vertices, 4, indices, 6); + collector.append(tile, vertices, 4, indices, 6); break;} case NDT_PLANTLIKE: { - video::SMaterial material_papyrus; - material_papyrus.setFlag(video::EMF_LIGHTING, false); - material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false); - material_papyrus.setFlag(video::EMF_FOG_ENABLE, true); - material_papyrus.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - AtlasPointer pa_papyrus = f.tiles[0].texture; - material_papyrus.setTexture(0, pa_papyrus.atlas); + TileSpec tile = getNodeTileN(n, p, 0, data); + tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY; + AtlasPointer ap = tile.texture; - u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); + u16 l = getInteriorLight(n, 1, data); video::SColor c = MapBlock_LightColor(255, l); for(u32 j=0; j<4; j++) @@ -736,15 +679,15 @@ void mapblock_mesh_generate_special(MeshMakeData *data, video::S3DVertex vertices[4] = { video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c, - pa_papyrus.x0(), pa_papyrus.y1()), + ap.x0(), ap.y1()), video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c, - pa_papyrus.x1(), pa_papyrus.y1()), + ap.x1(), ap.y1()), video::S3DVertex( BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, - pa_papyrus.x1(), pa_papyrus.y0()), + ap.x1(), ap.y0()), video::S3DVertex(-BS/2*f.visual_scale, -BS/2 + f.visual_scale*BS,0, 0,0,0, c, - pa_papyrus.x0(), pa_papyrus.y0()), + ap.x0(), ap.y0()), }; if(j == 0) @@ -771,45 +714,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data, for(u16 i=0; i<4; i++) { vertices[i].Pos *= f.visual_scale; - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; // Add to mesh collector - collector.append(material_papyrus, vertices, 4, indices, 6); + collector.append(tile, vertices, 4, indices, 6); } break;} case NDT_FENCELIKE: { - video::SMaterial material_wood; - material_wood.setFlag(video::EMF_LIGHTING, false); - material_wood.setFlag(video::EMF_BILINEAR_FILTER, false); - material_wood.setFlag(video::EMF_FOG_ENABLE, true); - material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - TileSpec tile_wood = getNodeTile(n, p, v3s16(0,0,0), - &data->m_temp_mods, tsrc, nodedef); - AtlasPointer pa_wood = tile_wood.texture; - material_wood.setTexture(0, pa_wood.atlas); - - 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); @@ -822,8 +748,7 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.35,0,0.65,1}; - makeCuboid(&collector, post, &material_wood, - &pa_wood, 1, c, postuv); + makeCuboid(&collector, post, &tile, 1, c, postuv); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; @@ -843,12 +768,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6}; - makeCuboid(&collector, bar, &material_wood_nomod, - &pa_wood_nomod, 1, c, xrailuv); + makeCuboid(&collector, bar, &tile_nocrack, 1, + c, xrailuv); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; - makeCuboid(&collector, bar, &material_wood_nomod, - &pa_wood_nomod, 1, c, xrailuv); + // TODO: no crack + makeCuboid(&collector, bar, &tile_nocrack, 1, + c, xrailuv); } // Now a section of fence, +Z, if there's a post there @@ -870,12 +796,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0,0.4,1,0.6, 0,0.4,1,0.6}; - makeCuboid(&collector, bar, &material_wood_nomod, - &pa_wood_nomod, 1, c, zrailuv); + makeCuboid(&collector, bar, &tile_nocrack, 1, + c, zrailuv); bar.MinEdge.Y -= BS/2; bar.MaxEdge.Y -= BS/2; - makeCuboid(&collector, bar, &material_wood_nomod, - &pa_wood_nomod, 1, c, zrailuv); + makeCuboid(&collector, bar, &tile_nocrack, 1, + c, zrailuv); } break;} case NDT_RAILLIKE: @@ -948,31 +874,28 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } // Assign textures - AtlasPointer ap = f.tiles[0].texture; // straight + u8 tileindex = 0; // straight if(adjacencies < 2) - ap = f.tiles[0].texture; // straight + tileindex = 0; // straight else if(adjacencies == 2) { if(is_straight) - ap = f.tiles[0].texture; // straight + tileindex = 0; // straight else - ap = f.tiles[1].texture; // curved + tileindex = 1; // curved } else if(adjacencies == 3) - ap = f.tiles[2].texture; // t-junction + tileindex = 2; // t-junction else if(adjacencies == 4) - ap = f.tiles[3].texture; // crossing + tileindex = 3; // crossing + + 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; @@ -1048,11 +971,11 @@ void mapblock_mesh_generate_special(MeshMakeData *data, for(s32 i=0; i<4; i++) { - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); + vertices[i].Pos += intToFloat(p, BS); } u16 indices[] = {0,1,2,2,3,0}; - collector.append(material_rail, vertices, 4, indices, 6); + collector.append(tile, vertices, 4, indices, 6); break;} } } diff --git a/src/content_mapblock.h b/src/content_mapblock.h index eaf74b14..af25191b 100644 --- a/src/content_mapblock.h +++ b/src/content_mapblock.h @@ -20,13 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef CONTENT_MAPBLOCK_HEADER #define CONTENT_MAPBLOCK_HEADER -#ifndef SERVER - #include "mapblock_mesh.h" - #include "utility.h" -class IGameDef; +struct MeshMakeData; +struct MeshCollector; void mapblock_mesh_generate_special(MeshMakeData *data, - MeshCollector &collector, IGameDef *gamedef); -#endif + MeshCollector &collector); #endif diff --git a/src/environment.cpp b/src/environment.cpp index cfe82fb8..c3ff7b75 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -890,9 +890,6 @@ void ServerEnvironment::step(float dtime) //TimeTaker timer("ServerEnv step"); - // Get some settings - bool footprints = g_settings->getBool("footprints"); - /* Increment game time */ @@ -921,26 +918,6 @@ void ServerEnvironment::step(float dtime) // Move player->move(dtime, *m_map, 100*BS); - - /* - Add footsteps to grass - */ - if(footprints) - { - // Get node that is at BS/4 under player - v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); - try{ - MapNode n = m_map->getNode(bottompos); - if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS")) - { - n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS")); - m_map->setNode(bottompos, n); - } - } - catch(InvalidPositionException &e) - { - } - } } } @@ -1873,7 +1850,6 @@ void ClientEnvironment::step(float dtime) // Get some settings bool free_move = g_settings->getBool("free_move"); - bool footprints = g_settings->getBool("footprints"); // Get local player LocalPlayer *lplayer = getLocalPlayer(); @@ -2058,34 +2034,6 @@ void ClientEnvironment::step(float dtime) light = blend_light(getDayNightRatio(), LIGHT_SUN, 0); } player->updateLight(light); - - /* - Add footsteps to grass - */ - if(footprints) - { - // Get node that is at BS/4 under player - v3s16 bottompos = floatToInt(playerpos + v3f(0,-BS/4,0), BS); - try{ - MapNode n = m_map->getNode(bottompos); - if(n.getContent() == LEGN(m_gamedef->ndef(), "CONTENT_GRASS")) - { - n.setContent(LEGN(m_gamedef->ndef(), "CONTENT_GRASS_FOOTSTEPS")); - m_map->setNode(bottompos, n); - // Update mesh on client - if(m_map->mapType() == MAPTYPE_CLIENT) - { - v3s16 p_blocks = getNodeBlockPos(bottompos); - MapBlock *b = m_map->getBlockNoCreate(p_blocks); - //b->updateMesh(getDayNightRatio()); - b->setMeshExpired(true); - } - } - } - catch(InvalidPositionException &e) - { - } - } } /* @@ -2133,16 +2081,6 @@ void ClientEnvironment::step(float dtime) } } } - -void ClientEnvironment::updateMeshes(v3s16 blockpos) -{ - m_map->updateMeshes(blockpos, getDayNightRatio()); -} - -void ClientEnvironment::expireMeshes(bool only_daynight_diffed) -{ - m_map->expireMeshes(only_daynight_diffed); -} void ClientEnvironment::addSimpleObject(ClientSimpleObject *simple) { diff --git a/src/environment.h b/src/environment.h index fcd16e29..65495fc8 100644 --- a/src/environment.h +++ b/src/environment.h @@ -407,24 +407,6 @@ public: virtual void addPlayer(Player *player); LocalPlayer * getLocalPlayer(); - // Slightly deprecated - void updateMeshes(v3s16 blockpos); - void expireMeshes(bool only_daynight_diffed); - - void setTimeOfDay(u32 time) - { - u32 old_dr = getDayNightRatio(); - - Environment::setTimeOfDay(time); - - if(getDayNightRatio() != old_dr) - { - /*infostream<<"ClientEnvironment: DayNightRatio changed" - <<" -> expiring meshes"<getBool("invert_mouse"); bool respawn_menu_active = false; @@ -1185,7 +1183,6 @@ void the_game( if(object_hit_delay_timer >= 0) object_hit_delay_timer -= dtime; time_from_last_punch += dtime; - crack_update_timer += dtime; g_profiler->add("Elapsed time", dtime); g_profiler->avg("FPS", 1./dtime); @@ -1908,7 +1905,7 @@ void the_game( if(!digging) { client.interact(1, pointed_old); - client.clearTempMod(pointed_old.node_undersurface); + client.setCrack(-1, v3s16(0,0,0)); dig_time = 0.0; } } @@ -2003,20 +2000,15 @@ void the_game( } else if(dig_index < CRACK_ANIMATION_LENGTH) { - // Limit crack update speed - if(crack_update_timer >= 0.1){ - crack_update_timer = 0.0; - //infostream<<"dig_index="<= 0.2 && brightness < 0.7) bgcolor = video::SColor( diff --git a/src/itemdef.cpp b/src/itemdef.cpp index ace9c253..a646134a 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -381,17 +381,20 @@ public: content_t id = nodedef->getId(def->name); const ContentFeatures &f = nodedef->get(id); + u8 param1 = 0; + if(f.param_type == CPT_LIGHT) + param1 = 0xee; + /* Make a mesh from the node */ - MeshMakeData mesh_make_data; - MapNode mesh_make_node( - id, - (f.param_type == CPT_LIGHT) ? 0xee : 0, - 0); - mesh_make_data.fillSingleNode(1000, &mesh_make_node); - scene::IMesh *node_mesh = - makeMapBlockMesh(&mesh_make_data, gamedef); + MeshMakeData mesh_make_data(gamedef); + MapNode mesh_make_node(id, param1, 0); + mesh_make_data.fillSingleNode(&mesh_make_node); + MapBlockMesh mapblock_mesh(&mesh_make_data); + + scene::IMesh *node_mesh = mapblock_mesh.getMesh(); + assert(node_mesh); setMeshColor(node_mesh, video::SColor(255, 255, 255, 255)); /* @@ -404,7 +407,7 @@ public: /* Draw node mesh into a render target texture */ - if(def->inventory_texture == NULL && node_mesh != NULL) + if(def->inventory_texture == NULL) { core::dimension2d dim(64,64); std::string rtt_texture_name = "INVENTORY_" @@ -443,7 +446,7 @@ public: /* Use the node mesh as the wield mesh */ - if(def->wield_mesh == NULL && node_mesh != NULL) + if(def->wield_mesh == NULL) { // Scale to proper wield mesh proportions scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0) @@ -452,9 +455,7 @@ public: def->wield_mesh->grab(); } - - if(node_mesh != NULL) - node_mesh->drop(); + // falling outside of here deletes node_mesh } } #endif diff --git a/src/map.cpp b/src/map.cpp index e0175a4a..03a842e7 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -21,23 +21,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapsector.h" #include "mapblock.h" #include "main.h" -#ifndef SERVER -#include "client.h" -#endif #include "filesys.h" #include "utility.h" #include "voxel.h" #include "porting.h" #include "mapgen.h" #include "nodemetadata.h" -#ifndef SERVER -#include -#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 +#endif #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -3676,7 +3675,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) */ int time1 = time(0); - //u32 daynight_ratio = m_client->getDayNightRatio(); + /* + Get animation parameters + */ + float animation_time = m_client->getAnimationTime(); + int crack = m_client->getCrackLevel(); + u32 daynight_ratio = m_client->getDayNightRatio(); m_camera_mutex.Lock(); v3f camera_position = m_camera_position; @@ -3709,8 +3713,9 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) u32 vertex_count = 0; u32 meshbuffer_count = 0; - // For limiting number of mesh updates per frame - u32 mesh_update_count = 0; + // For limiting number of mesh animations per frame + u32 mesh_animate_count = 0; + u32 mesh_animate_count_far = 0; // Number of blocks in rendering range u32 blocks_in_range = 0; @@ -3791,57 +3796,18 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) blocks_in_range++; -#if 1 /* - Update expired mesh (used for day/night change) - - It doesn't work exactly like it should now with the - tasked mesh update but whatever. + Ignore if mesh doesn't exist */ - - bool mesh_expired = false; - { - JMutexAutoLock lock(block->mesh_mutex); - - 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 */ @@ -3883,27 +3849,40 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) // This block is in range. Reset usage timer. block->resetUsageTimer(); - /* - Ignore if mesh doesn't exist - */ - { - JMutexAutoLock lock(block->mesh_mutex); - - scene::SMesh *mesh = block->mesh; - - if(mesh == NULL){ - blocks_in_range_without_mesh++; - continue; - } - } - // Limit block count in case of a sudden increase blocks_would_have_drawn++; if(blocks_drawn >= m_control.wanted_max_blocks && 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; @@ -3951,11 +3930,14 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) 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; iavg("CM: blocks in range without mesh (frac)", (float)blocks_in_range_without_mesh/blocks_in_range); g_profiler->avg("CM: blocks drawn", blocks_drawn); + g_profiler->avg("CM: animated meshes", mesh_animate_count); + g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far); } g_profiler->avg(prefix+"vertices drawn", vertex_count); @@ -4047,205 +4031,6 @@ void ClientMap::renderPostFx() } } -bool ClientMap::setTempMod(v3s16 p, NodeMod mod, - core::map *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 *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::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; jupdateMesh(daynight_ratio); - } -} -#endif - void ClientMap::PrintInfo(std::ostream &out) { out<<"ClientMap: "; diff --git a/src/map.h b/src/map.h index 90255c1d..ada59a4d 100644 --- a/src/map.h +++ b/src/map.h @@ -28,7 +28,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common_irrlicht.h" #include "mapnode.h" -#include "mapblock_nodemod.h" #include "constants.h" #include "voxel.h" #include "utility.h" // Needed for UniqueQueue, a member of Map @@ -570,33 +569,6 @@ public: void renderPostFx(); - /* - Methods for setting temporary modifications to nodes for - drawing. - - Returns true if something changed. - - All blocks whose mesh could have been changed are inserted - to affected_blocks. - */ - bool setTempMod(v3s16 p, NodeMod mod, - core::map *affected_blocks=NULL); - bool clearTempMod(v3s16 p, - core::map *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); diff --git a/src/mapblock.cpp b/src/mapblock.cpp index b436378d..70cb0e37 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -59,10 +59,8 @@ MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy): reallocate(); #ifndef SERVER - m_mesh_expired = false; - mesh_mutex.Init(); + //mesh_mutex.Init(); mesh = NULL; - m_temp_mods_mutex.Init(); #endif } @@ -70,11 +68,11 @@ MapBlock::~MapBlock() { #ifndef SERVER { - JMutexAutoLock lock(mesh_mutex); + //JMutexAutoLock lock(mesh_mutex); if(mesh) { - mesh->drop(); + delete mesh; mesh = NULL; } } @@ -147,78 +145,6 @@ MapNode MapBlock::getNodeParentNoEx(v3s16 p) } } -#ifndef SERVER - -#if 1 -void MapBlock::updateMesh(u32 daynight_ratio) -{ -#if 0 - /* - DEBUG: If mesh has been generated, don't generate it again - */ - { - JMutexAutoLock meshlock(mesh_mutex); - if(mesh != NULL) - return; - } -#endif - - MeshMakeData data; - data.fill(daynight_ratio, this); - - scene::SMesh *mesh_new = makeMapBlockMesh(&data, m_gamedef); - - /* - Replace the mesh - */ - - replaceMesh(mesh_new); - -} -#endif - -void MapBlock::replaceMesh(scene::SMesh *mesh_new) -{ - mesh_mutex.Lock(); - - //scene::SMesh *mesh_old = mesh[daynight_i]; - //mesh[daynight_i] = mesh_new; - - scene::SMesh *mesh_old = mesh; - mesh = mesh_new; - setMeshExpired(false); - - if(mesh_old != NULL) - { - // Remove hardware buffers of meshbuffers of mesh - // NOTE: No way, this runs in a different thread and everything - /*u32 c = mesh_old->getMeshBufferCount(); - for(u32 i=0; igetMeshBuffer(i); - }*/ - - /*infostream<<"mesh_old->getReferenceCount()=" - <getReferenceCount()<getMeshBufferCount(); - for(u32 i=0; igetMeshBuffer(i); - infostream<<"buf->getReferenceCount()=" - <getReferenceCount()<drop(); - - //delete mesh_old; - } - - mesh_mutex.Unlock(); -} - -#endif // !SERVER - /* Propagates sunlight down through the block. Doesn't modify nodes that are not affected by sunlight. @@ -1232,13 +1158,6 @@ std::string analyze_block(MapBlock *block) else desc<<"is_ug [ ], "; -#ifndef SERVER - if(block->getMeshExpired()) - desc<<"mesh_exp [X], "; - else - desc<<"mesh_exp [ ], "; -#endif - if(block->getLightingExpired()) desc<<"lighting_exp [X], "; else diff --git a/src/mapblock.h b/src/mapblock.h index c9ff3667..272da8ce 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -31,13 +31,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "voxel.h" #include "staticobject.h" -#include "mapblock_nodemod.h" #include "modifiedstate.h" class Map; class NodeMetadataList; class IGameDef; -class IWritableNodeDefManager; +class MapBlockMesh; #define BLOCK_TIMESTAMP_UNDEFINED 0xffffffff @@ -193,18 +192,6 @@ public: raiseModified(MOD_STATE_WRITE_NEEDED, "setIsUnderground"); } -#ifndef SERVER - void setMeshExpired(bool expired) - { - m_mesh_expired = expired; - } - - bool getMeshExpired() - { - return m_mesh_expired; - } -#endif - void setLightingExpired(bool expired) { if(expired != m_lighting_expired){ @@ -359,33 +346,6 @@ public: setNode(x0+x, y0+y, z0+z, node); } - /* - Graphics-related methods - */ - -#ifndef SERVER // Only on client - - u8 getFaceLight2(u32 daynight_ratio, v3s16 p, v3s16 face_dir, - INodeDefManager *nodemgr) - { - return getFaceLight(daynight_ratio, - getNodeParentNoEx(p), - getNodeParentNoEx(p + face_dir), - face_dir, nodemgr); - } - -#if 1 - /* - Thread-safely updates the whole mesh of the mapblock. - NOTE: Prefer generating the mesh separately and then using - replaceMesh(). - */ - void updateMesh(u32 daynight_ratio); -#endif - // Replace the mesh with a new one - void replaceMesh(scene::SMesh *mesh_new); -#endif - // See comments in mapblock.cpp bool propagateSunlight(core::map & light_sources, bool remove_light=false, bool *black_air_left=NULL); @@ -395,59 +355,10 @@ public: // Copies data from VoxelManipulator getPosRelative() void copyFrom(VoxelManipulator &dst); -#ifndef SERVER // Only on client - /* - Methods for setting temporary modifications to nodes for - drawing - - returns true if the mod was different last time - */ - bool setTempMod(v3s16 p, const NodeMod &mod) - { - /*dstream<<"setTempMod called on block" - <<" ("<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 */ @@ -81,11 +85,9 @@ void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block) } } -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, @@ -114,6 +116,226 @@ void MeshMakeData::fillSingleNode(u32 daynight_ratio, MapNode *node) 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] */ @@ -177,41 +399,19 @@ static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs) } } -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 &dest) +static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3, + v3f p, v3s16 dir, v3f scale, core::array &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]; @@ -230,7 +430,7 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, 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.; @@ -241,26 +441,12 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, 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(x0+w*abs_scale, y0+h)); - face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c, - core::vector2d(x0, y0+h)); - face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c, - core::vector2d(x0, y0)); - face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c, - core::vector2d(x0+w*abs_scale, y0));*/ - face.vertices[0] = video::S3DVertex(vertex_pos[0], normal, MapBlock_LightColor(alpha, li0), core::vector2d(x0+w*abs_scale, y0+h)); @@ -275,14 +461,89 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, core::vector2d(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); @@ -299,7 +560,7 @@ static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr 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] = @@ -310,196 +571,36 @@ static TileSpec getTile(const MapNode &node, v3s16 dir, INodeDefManager *nodemgr 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::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<getTextureId(os.str()); - - /*dstream<<"MapBlock::getNodeTile(): Switching from " - <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) { @@ -526,10 +627,10 @@ static void getTileInfo( 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 { @@ -537,8 +638,9 @@ static void getTileInfo( 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); } } @@ -551,20 +653,13 @@ static void getTileInfo( 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 &dest, - NodeModMap *temp_mods, - VoxelManipulator &vmanip, - v3s16 blockpos_nodes, - bool smooth_lighting, - IGameDef *gamedef) + core::array &dest) { v3s16 p = startpos; @@ -573,13 +668,13 @@ static void updateFastFaceRow( 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; javg("Meshgen: faces drawn by tiling", 0); for(int i=1; i &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 fastfaces_new; + /* + Go through every x,y and get right(x+) faces in rows of z+ + */ + for(s16 x=0; xm_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; zm_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 fastfaces_new; + /* We are including the faces of the trailing edges of the block. This means that when something changes, the caller must @@ -739,92 +881,23 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef) 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; ym_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; xm_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; zm_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; irecalculateBoundingBox(); + // 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 + 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 "<getMeshBufferCount() + <<"and uses "<getMeshBufferCount() <<" materials (meshbuffers)"<setHardwareMappingHint(scene::EHM_STATIC); + //m_mesh->setHardwareMappingHint(scene::EHM_STATIC); /* NOTE: If that is enabled, some kind of a queue to the main @@ -909,9 +1024,118 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef) the hardware buffer and then delete the mesh */ } - - return mesh_new; //std::cout<<"added "<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::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<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 > >::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 >::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; ivertices.size(); + for(u32 i=0; i 65535) + { + dstream<<"FIXME: Meshbuffer ran out of indices"<indices.push_back(j); + } + for(u32 i=0; ivertices.push_back(vertices[i]); + } +} diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 4d3e7d29..5028873c 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -21,9 +21,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MAPBLOCK_MESH_HEADER #include "common_irrlicht.h" -#include "mapblock_nodemod.h" #include "tile.h" #include "voxel.h" +#include class IGameDef; @@ -31,127 +31,143 @@ 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 indices; - core::array 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; ivertices.size(); - for(u32 i=0; i 65535) - { - dstream<<"FIXME: Meshbuffer ran out of indices"<indices.push_back(j); - } - for(u32 i=0; ivertices.push_back(vertices[i]); - } + return m_mesh; } - void fillMesh(scene::SMesh *mesh) + bool isAnimationForced() const { - /*dstream<<"Filling mesh with "< - 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 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 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 > > 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 indices; + core::array vertices; +}; - /* - Set up with only a single node at (1,1,1) - */ - void fillSingleNode(u32 daynight_ratio, MapNode *node); +struct MeshCollector +{ + core::array 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 diff --git a/src/mapblock_nodemod.h b/src/mapblock_nodemod.h deleted file mode 100644 index 4952df10..00000000 --- a/src/mapblock_nodemod.h +++ /dev/null @@ -1,114 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola - -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::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::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::Iterator - i = m_mods.getIterator(); - i.atEnd() == false; i++) - { - dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue()); - } - } - -private: - core::map m_mods; -}; - -#endif - diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 6cb9671b..54be5d1d 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -353,115 +353,3 @@ void MapNode::deSerialize_pre22(u8 *source, u8 version) // Translate to our known version *this = mapnode_translate_to_internal(*this, version); } - - -#ifndef SERVER - -/* - Nodes make a face if contents differ and solidness differs. - Return value: - 0: No face - 1: Face uses m1's content - 2: Face uses m2's content - equivalent: Whether the blocks share the same face (eg. water and glass) - - TODO: Add 3: Both faces drawn with backface culling, remove equivalent -*/ -u8 face_contents(content_t m1, content_t m2, bool *equivalent, - INodeDefManager *nodemgr) -{ - *equivalent = false; - - if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE) - return 0; - - bool contents_differ = (m1 != m2); - - const ContentFeatures &f1 = nodemgr->get(m1); - const ContentFeatures &f2 = nodemgr->get(m2); - - // Contents don't differ for different forms of same liquid - if(f1.sameLiquid(f2)) - contents_differ = false; - - u8 c1 = f1.solidness; - u8 c2 = f2.solidness; - - bool solidness_differs = (c1 != c2); - bool makes_face = contents_differ && solidness_differs; - - if(makes_face == false) - return 0; - - if(c1 == 0) - c1 = f1.visual_solidness; - if(c2 == 0) - c2 = f2.visual_solidness; - - if(c1 == c2){ - *equivalent = true; - // If same solidness, liquid takes precense - if(f1.isLiquid()) - return 1; - if(f2.isLiquid()) - return 2; - } - - if(c1 > c2) - return 1; - else - return 2; -} - -/* - Gets lighting value at face of node - - Parameters must consist of air and !air. - Order doesn't matter. - - If either of the nodes doesn't exist, light is 0. - - parameters: - daynight_ratio: 0...1000 - n: getNode(p) (uses only the lighting value) - n2: getNode(p + face_dir) (uses only the lighting value) - face_dir: axis oriented unit vector from p to p2 - - returns encoded light value. -*/ -u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, - v3s16 face_dir, INodeDefManager *nodemgr) -{ - try{ - u8 light; - u8 l1 = n.getLightBlend(daynight_ratio, nodemgr); - u8 l2 = n2.getLightBlend(daynight_ratio, nodemgr); - if(l1 > l2) - light = l1; - else - light = l2; - - // Make some nice difference to different sides - - // This makes light come from a corner - /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1) - light = diminish_light(diminish_light(light)); - else if(face_dir.X == -1 || face_dir.Z == -1) - light = diminish_light(light);*/ - - // All neighboring faces have different shade (like in minecraft) - if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1) - light = diminish_light(diminish_light(light)); - else if(face_dir.Z == 1 || face_dir.Z == -1) - light = diminish_light(light); - - return light; - } - catch(InvalidPositionException &e) - { - return 0; - } -} - -#endif - diff --git a/src/mapnode.h b/src/mapnode.h index 5e066604..c3ba9c6a 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -223,45 +223,5 @@ private: void deSerialize_pre22(u8 *source, u8 version); }; - -/* - MapNode helpers for mesh making stuff -*/ - -#ifndef SERVER - -/* - Nodes make a face if contents differ and solidness differs. - Return value: - 0: No face - 1: Face uses m1's content - 2: Face uses m2's content - equivalent: Whether the blocks share the same face (eg. water and glass) -*/ -u8 face_contents(content_t m1, content_t m2, bool *equivalent, - INodeDefManager *nodemgr); - -/* - Gets lighting value at face of node - - Parameters must consist of air and !air. - Order doesn't matter. - - If either of the nodes doesn't exist, light is 0. - - parameters: - daynight_ratio: 0...1000 - n: getNode(p) (uses only the lighting value) - n2: getNode(p + face_dir) (uses only the lighting value) - face_dir: axis oriented unit vector from p to p2 - - returns encoded light value. -*/ -u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2, - v3s16 face_dir, INodeDefManager *nodemgr); - -#endif - - #endif diff --git a/src/nodedef.cpp b/src/nodedef.cpp index f24497dd..e9abc323 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -89,12 +89,6 @@ ContentFeatures::ContentFeatures() ContentFeatures::~ContentFeatures() { -#ifndef SERVER - for(u16 j=0; jtiles[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; jspecial_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 diff --git a/src/nodedef.h b/src/nodedef.h index c875e0b7..2842b81e 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -135,10 +135,9 @@ struct ContentFeatures // 0 1 2 3 4 5 // up down right left back front TileSpec tiles[6]; - // Special material/texture + // Special tiles // - Currently used for flowing liquids - video::SMaterial *special_materials[CF_SPECIAL_COUNT]; - AtlasPointer *special_aps[CF_SPECIAL_COUNT]; + TileSpec special_tiles[CF_SPECIAL_COUNT]; u8 solidness; // Used when choosing which face is drawn u8 visual_solidness; // When solidness=0, this tells how it looks like bool backface_culling; diff --git a/src/tile.cpp b/src/tile.cpp index 5ef46994..fc371c94 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -279,12 +279,14 @@ public: Example case #2: - Assume a texture with the id 1 exists, and has the name "stone.png^mineral1" and is specified as a part of some atlas. - - Now MapBlock::getNodeTile() stumbles upon a node which uses - texture id 1, and finds out that NODEMOD_CRACK must be applied - with progression=0 - - It finds out the name of the texture with getTextureName(1), + - Now getNodeTile() stumbles upon a node which uses + texture id 1, and determines that MATERIAL_FLAG_CRACK + must be applied to the tile + - MapBlockMesh::animate() finds the MATERIAL_FLAG_CRACK and + has received the current crack level 0 from the client. It + finds out the name of the texture with getTextureName(1), appends "^crack0" to it and gets a new texture id with - getTextureId("stone.png^mineral1^crack0") + getTextureId("stone.png^mineral1^crack0"). */ @@ -337,6 +339,12 @@ public: return ap.atlas; } + // Gets a separate texture atlas pointer + AtlasPointer getTextureRawAP(const AtlasPointer &ap) + { + return getTexture(getTextureName(ap.id) + "^[forcesingle"); + } + // Returns a pointer to the irrlicht device virtual IrrlichtDevice* getDevice() { @@ -475,6 +483,9 @@ u32 TextureSource::getTextureId(const std::string &name) return 0; } +// Overlay image on top of another image (used for cracks) +void overlay(video::IImage *image, video::IImage *overlay); + // Brighten image void brighten(video::IImage *image); @@ -1183,8 +1194,19 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, return false; } - // Crack image number - u16 progression = stoi(part_of_name.substr(6)); + // Crack image number and overlay option + s32 progression = 0; + bool use_overlay = false; + if(part_of_name.substr(6,1) == "o") + { + progression = stoi(part_of_name.substr(7)); + use_overlay = true; + } + else + { + progression = stoi(part_of_name.substr(6)); + use_overlay = false; + } // Size of the base image core::dimension2d dim_base = baseimg->getDimension(); @@ -1197,65 +1219,56 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, */ video::IImage *img_crack = sourcecache->getOrLoad("crack.png", device); - if(img_crack) + if(img_crack && progression >= 0) { // Dimension of original image core::dimension2d 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 dim_crack_scaled_single( - dim_base.Width, - dim_base.Height + // Dimension of a single crack stage + core::dimension2d dim_crack_cropped( + dim_crack.Width, + dim_crack.Width ); - // Dimension of scaled size - core::dimension2d 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(pos_crack, dim_crack_cropped)); // Scale crack image by copying - img_crack->copyToScaling(img_crack_scaled); - - // Position to copy the crack from - core::position2d pos_crack_scaled( - 0, - dim_crack_scaled_single.Height * progression - ); - - // This tiling does nothing currently but is useful - for(u32 y0=0; y0copyToScaling(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 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 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(v2s32(0,0), dim_base), + video::SColor(255,255,255,255)); } + } + if(img_crack_scaled) img_crack_scaled->drop(); - } + + if(img_crack_cropped) + img_crack_cropped->drop(); img_crack->drop(); } @@ -1511,6 +1524,37 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, return true; } +void overlay(video::IImage *image, video::IImage *overlay) +{ + /* + Copy overlay to image, taking alpha into account. + Where image is transparent, don't copy from overlay. + Images sizes must be identical. + */ + if(image == NULL || overlay == NULL) + return; + + core::dimension2d dim = image->getDimension(); + core::dimension2d dim_overlay = overlay->getDimension(); + assert(dim == dim_overlay); + + for(u32 y=0; ygetPixel(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) diff --git a/src/tile.h b/src/tile.h index c0d8914b..698ce3dc 100644 --- a/src/tile.h +++ b/src/tile.h @@ -73,7 +73,7 @@ struct AtlasPointer { } - bool operator==(const AtlasPointer &other) + bool operator==(const AtlasPointer &other) const { return ( id == other.id @@ -87,6 +87,11 @@ struct AtlasPointer );*/ } + bool operator!=(const AtlasPointer &other) const + { + return !(*this == other); + } + float x0(){ return pos.X; } float x1(){ return pos.X + size.X; } float y0(){ return pos.Y; } @@ -110,6 +115,8 @@ public: {return AtlasPointer(0);} virtual video::ITexture* getTextureRaw(const std::string &name) {return NULL;} + virtual AtlasPointer getTextureRawAP(const AtlasPointer &ap) + {return AtlasPointer(0);} virtual IrrlichtDevice* getDevice() {return NULL;} virtual void updateAP(AtlasPointer &ap){}; @@ -148,7 +155,13 @@ enum MaterialType{ }; // Material flags +// Should backface culling be enabled? #define MATERIAL_FLAG_BACKFACE_CULLING 0x01 +// Should a crack be drawn? +#define MATERIAL_FLAG_CRACK 0x02 +// Should the crack be drawn on transparent pixels (unset) or not (set)? +// Ignored if MATERIAL_FLAG_CRACK is not set. +#define MATERIAL_FLAG_CRACK_OVERLAY 0x04 /* This fully defines the looks of a tile. @@ -169,7 +182,7 @@ struct TileSpec { } - bool operator==(TileSpec &other) + bool operator==(const TileSpec &other) const { return ( texture == other.texture && @@ -178,6 +191,11 @@ struct TileSpec 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 diff --git a/src/utility.h b/src/utility.h index aa64c28b..15c01373 100644 --- a/src/utility.h +++ b/src/utility.h @@ -1753,18 +1753,17 @@ std::string deSerializeJsonString(std::istream &is); inline u32 time_to_daynight_ratio(u32 time_of_day) { - const s32 daylength = 16; - const s32 nightlength = 6; - const s32 daytimelength = 8; - s32 d = daylength; - s32 t = (((time_of_day)%24000)/(24000/d)); - if(t < nightlength/2 || t >= d - nightlength/2) - //return 300; + s32 t = time_of_day%24000; + if(t < 4500 || t >= 19500) + return 150; + else if(t < 5000 || t >= 19000) return 350; - else if(t >= d/2 - daytimelength/2 && t < d/2 + daytimelength/2) - return 1000; - else + else if(t < 5500 || t >= 18500) + return 500; + else if(t < 6000 || t >= 18000) return 750; + else + return 1000; } // Random helper. Usually d=BS -- 2.30.2