Move ClientMap to clientmap.{h,cpp}
parent
54917e3062
commit
58bed83d03
|
@ -159,6 +159,7 @@ endif()
|
||||||
# Client sources
|
# Client sources
|
||||||
set(minetest_SRCS
|
set(minetest_SRCS
|
||||||
${common_SRCS}
|
${common_SRCS}
|
||||||
|
clientmap.cpp
|
||||||
content_cso.cpp
|
content_cso.cpp
|
||||||
content_mapblock.cpp
|
content_mapblock.cpp
|
||||||
content_cao.cpp
|
content_cao.cpp
|
||||||
|
|
|
@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "main.h" // for g_settings
|
#include "main.h" // for g_settings
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
|
#include "clientmap.h" // MapDrawControl
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "tile.h"
|
#include "tile.h"
|
||||||
|
|
|
@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include <IFileSystem.h>
|
#include <IFileSystem.h>
|
||||||
#include "sha1.h"
|
#include "sha1.h"
|
||||||
#include "base64.h"
|
#include "base64.h"
|
||||||
|
#include "clientmap.h"
|
||||||
|
|
||||||
static std::string getTextureCacheDir()
|
static std::string getTextureCacheDir()
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,6 +43,7 @@ class IWritableItemDefManager;
|
||||||
class IWritableNodeDefManager;
|
class IWritableNodeDefManager;
|
||||||
//class IWritableCraftDefManager;
|
//class IWritableCraftDefManager;
|
||||||
class ClientEnvironment;
|
class ClientEnvironment;
|
||||||
|
struct MapDrawControl;
|
||||||
|
|
||||||
class ClientNotReadyException : public BaseException
|
class ClientNotReadyException : public BaseException
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,548 @@
|
||||||
|
/*
|
||||||
|
Minetest-c55
|
||||||
|
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "clientmap.h"
|
||||||
|
#include "client.h"
|
||||||
|
#include "mapblock_mesh.h"
|
||||||
|
#include <IMaterialRenderer.h>
|
||||||
|
#include "log.h"
|
||||||
|
#include "mapsector.h"
|
||||||
|
#include "main.h" // dout_client, g_settings
|
||||||
|
#include "nodedef.h"
|
||||||
|
#include "mapblock.h"
|
||||||
|
#include "profiler.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
ClientMap::ClientMap(
|
||||||
|
Client *client,
|
||||||
|
IGameDef *gamedef,
|
||||||
|
MapDrawControl &control,
|
||||||
|
scene::ISceneNode* parent,
|
||||||
|
scene::ISceneManager* mgr,
|
||||||
|
s32 id
|
||||||
|
):
|
||||||
|
Map(dout_client, gamedef),
|
||||||
|
scene::ISceneNode(parent, mgr, id),
|
||||||
|
m_client(client),
|
||||||
|
m_control(control),
|
||||||
|
m_camera_position(0,0,0),
|
||||||
|
m_camera_direction(0,0,1),
|
||||||
|
m_camera_fov(PI)
|
||||||
|
{
|
||||||
|
m_camera_mutex.Init();
|
||||||
|
assert(m_camera_mutex.IsInitialized());
|
||||||
|
|
||||||
|
m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
|
||||||
|
BS*1000000,BS*1000000,BS*1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMap::~ClientMap()
|
||||||
|
{
|
||||||
|
/*JMutexAutoLock lock(mesh_mutex);
|
||||||
|
|
||||||
|
if(mesh != NULL)
|
||||||
|
{
|
||||||
|
mesh->drop();
|
||||||
|
mesh = NULL;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
MapSector * ClientMap::emergeSector(v2s16 p2d)
|
||||||
|
{
|
||||||
|
DSTACK(__FUNCTION_NAME);
|
||||||
|
// Check that it doesn't exist already
|
||||||
|
try{
|
||||||
|
return getSectorNoGenerate(p2d);
|
||||||
|
}
|
||||||
|
catch(InvalidPositionException &e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a sector
|
||||||
|
ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
|
||||||
|
|
||||||
|
{
|
||||||
|
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
|
||||||
|
m_sectors.insert(p2d, sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sector;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
|
||||||
|
{
|
||||||
|
DSTACK(__FUNCTION_NAME);
|
||||||
|
ClientMapSector *sector = NULL;
|
||||||
|
|
||||||
|
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
|
||||||
|
|
||||||
|
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
|
||||||
|
|
||||||
|
if(n != NULL)
|
||||||
|
{
|
||||||
|
sector = (ClientMapSector*)n->getValue();
|
||||||
|
assert(sector->getId() == MAPSECTOR_CLIENT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sector = new ClientMapSector(this, p2d);
|
||||||
|
{
|
||||||
|
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
|
||||||
|
m_sectors.insert(p2d, sector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sector->deSerialize(is);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ClientMap::OnRegisterSceneNode()
|
||||||
|
{
|
||||||
|
if(IsVisible)
|
||||||
|
{
|
||||||
|
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
|
||||||
|
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ISceneNode::OnRegisterSceneNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
|
||||||
|
float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
|
||||||
|
{
|
||||||
|
float d0 = (float)BS * p0.getDistanceFrom(p1);
|
||||||
|
v3s16 u0 = p1 - p0;
|
||||||
|
v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
|
||||||
|
uf.normalize();
|
||||||
|
v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
|
||||||
|
u32 count = 0;
|
||||||
|
for(float s=start_off; s<d0+end_off; s+=step){
|
||||||
|
v3f pf = p0f + uf * s;
|
||||||
|
v3s16 p = floatToInt(pf, BS);
|
||||||
|
MapNode n = map->getNodeNoEx(p);
|
||||||
|
bool is_transparent = false;
|
||||||
|
const ContentFeatures &f = nodemgr->get(n);
|
||||||
|
if(f.solidness == 0)
|
||||||
|
is_transparent = (f.visual_solidness != 2);
|
||||||
|
else
|
||||||
|
is_transparent = (f.solidness != 2);
|
||||||
|
if(!is_transparent){
|
||||||
|
count++;
|
||||||
|
if(count >= needed_count)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
step *= stepfac;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
||||||
|
{
|
||||||
|
INodeDefManager *nodemgr = m_gamedef->ndef();
|
||||||
|
|
||||||
|
//m_dout<<DTIME<<"Rendering map..."<<std::endl;
|
||||||
|
DSTACK(__FUNCTION_NAME);
|
||||||
|
|
||||||
|
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
|
||||||
|
|
||||||
|
std::string prefix;
|
||||||
|
if(pass == scene::ESNRP_SOLID)
|
||||||
|
prefix = "CM: solid: ";
|
||||||
|
else
|
||||||
|
prefix = "CM: transparent: ";
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is called two times per frame, reset on the non-transparent one
|
||||||
|
*/
|
||||||
|
if(pass == scene::ESNRP_SOLID)
|
||||||
|
{
|
||||||
|
m_last_drawn_sectors.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get time for measuring timeout.
|
||||||
|
|
||||||
|
Measuring time is very useful for long delays when the
|
||||||
|
machine is swapping a lot.
|
||||||
|
*/
|
||||||
|
int time1 = time(0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
v3f camera_direction = m_camera_direction;
|
||||||
|
f32 camera_fov = m_camera_fov;
|
||||||
|
m_camera_mutex.Unlock();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get all blocks and draw all visible ones
|
||||||
|
*/
|
||||||
|
|
||||||
|
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
|
||||||
|
|
||||||
|
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
|
||||||
|
|
||||||
|
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
|
||||||
|
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
|
||||||
|
|
||||||
|
// Take a fair amount as we will be dropping more out later
|
||||||
|
// Umm... these additions are a bit strange but they are needed.
|
||||||
|
v3s16 p_blocks_min(
|
||||||
|
p_nodes_min.X / MAP_BLOCKSIZE - 3,
|
||||||
|
p_nodes_min.Y / MAP_BLOCKSIZE - 3,
|
||||||
|
p_nodes_min.Z / MAP_BLOCKSIZE - 3);
|
||||||
|
v3s16 p_blocks_max(
|
||||||
|
p_nodes_max.X / MAP_BLOCKSIZE + 1,
|
||||||
|
p_nodes_max.Y / MAP_BLOCKSIZE + 1,
|
||||||
|
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
|
||||||
|
|
||||||
|
u32 vertex_count = 0;
|
||||||
|
u32 meshbuffer_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;
|
||||||
|
// Number of blocks occlusion culled
|
||||||
|
u32 blocks_occlusion_culled = 0;
|
||||||
|
// Number of blocks in rendering range but don't have a mesh
|
||||||
|
u32 blocks_in_range_without_mesh = 0;
|
||||||
|
// Blocks that had mesh that would have been drawn according to
|
||||||
|
// rendering range (if max blocks limit didn't kick in)
|
||||||
|
u32 blocks_would_have_drawn = 0;
|
||||||
|
// Blocks that were drawn and had a mesh
|
||||||
|
u32 blocks_drawn = 0;
|
||||||
|
// Blocks which had a corresponding meshbuffer for this pass
|
||||||
|
u32 blocks_had_pass_meshbuf = 0;
|
||||||
|
// Blocks from which stuff was actually drawn
|
||||||
|
u32 blocks_without_stuff = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Collect a set of blocks for drawing
|
||||||
|
*/
|
||||||
|
|
||||||
|
core::map<v3s16, MapBlock*> drawset;
|
||||||
|
|
||||||
|
{
|
||||||
|
ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
|
||||||
|
|
||||||
|
for(core::map<v2s16, MapSector*>::Iterator
|
||||||
|
si = m_sectors.getIterator();
|
||||||
|
si.atEnd() == false; si++)
|
||||||
|
{
|
||||||
|
MapSector *sector = si.getNode()->getValue();
|
||||||
|
v2s16 sp = sector->getPos();
|
||||||
|
|
||||||
|
if(m_control.range_all == false)
|
||||||
|
{
|
||||||
|
if(sp.X < p_blocks_min.X
|
||||||
|
|| sp.X > p_blocks_max.X
|
||||||
|
|| sp.Y < p_blocks_min.Z
|
||||||
|
|| sp.Y > p_blocks_max.Z)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
core::list< MapBlock * > sectorblocks;
|
||||||
|
sector->getBlocks(sectorblocks);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Loop through blocks in sector
|
||||||
|
*/
|
||||||
|
|
||||||
|
u32 sector_blocks_drawn = 0;
|
||||||
|
|
||||||
|
core::list< MapBlock * >::Iterator i;
|
||||||
|
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
|
||||||
|
{
|
||||||
|
MapBlock *block = *i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Compare block position to camera position, skip
|
||||||
|
if not seen on display
|
||||||
|
*/
|
||||||
|
|
||||||
|
float range = 100000 * BS;
|
||||||
|
if(m_control.range_all == false)
|
||||||
|
range = m_control.wanted_range * BS;
|
||||||
|
|
||||||
|
float d = 0.0;
|
||||||
|
if(isBlockInSight(block->getPos(), camera_position,
|
||||||
|
camera_direction, camera_fov,
|
||||||
|
range, &d) == false)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is ugly (spherical distance limit?)
|
||||||
|
/*if(m_control.range_all == false &&
|
||||||
|
d - 0.5*BS*MAP_BLOCKSIZE > range)
|
||||||
|
continue;*/
|
||||||
|
|
||||||
|
blocks_in_range++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ignore if mesh doesn't exist
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
//JMutexAutoLock lock(block->mesh_mutex);
|
||||||
|
|
||||||
|
if(block->mesh == NULL){
|
||||||
|
blocks_in_range_without_mesh++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Occlusion culling
|
||||||
|
*/
|
||||||
|
|
||||||
|
v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
|
||||||
|
cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
|
||||||
|
float step = BS*1;
|
||||||
|
float stepfac = 1.1;
|
||||||
|
float startoff = BS*1;
|
||||||
|
float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
|
||||||
|
v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
|
||||||
|
s16 bs2 = MAP_BLOCKSIZE/2 + 1;
|
||||||
|
u32 needed_count = 1;
|
||||||
|
if(
|
||||||
|
isOccluded(this, spn, cpn + v3s16(0,0,0),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
||||||
|
isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
|
||||||
|
step, stepfac, startoff, endoff, needed_count, nodemgr)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
blocks_occlusion_culled++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This block is in range. Reset usage timer.
|
||||||
|
block->resetUsageTimer();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
sector_blocks_drawn++;
|
||||||
|
blocks_drawn++;
|
||||||
|
|
||||||
|
} // foreach sectorblocks
|
||||||
|
|
||||||
|
if(sector_blocks_drawn != 0)
|
||||||
|
m_last_drawn_sectors[sp] = true;
|
||||||
|
}
|
||||||
|
} // ScopeProfiler
|
||||||
|
|
||||||
|
/*
|
||||||
|
Draw the selected MapBlocks
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
|
||||||
|
|
||||||
|
int timecheck_counter = 0;
|
||||||
|
for(core::map<v3s16, MapBlock*>::Iterator
|
||||||
|
i = drawset.getIterator();
|
||||||
|
i.atEnd() == false; i++)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
timecheck_counter++;
|
||||||
|
if(timecheck_counter > 50)
|
||||||
|
{
|
||||||
|
timecheck_counter = 0;
|
||||||
|
int time2 = time(0);
|
||||||
|
if(time2 > time1 + 4)
|
||||||
|
{
|
||||||
|
infostream<<"ClientMap::renderMap(): "
|
||||||
|
"Rendering takes ages, returning."
|
||||||
|
<<std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MapBlock *block = i.getNode()->getValue();
|
||||||
|
|
||||||
|
/*
|
||||||
|
Draw the faces of the block
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
//JMutexAutoLock lock(block->mesh_mutex);
|
||||||
|
|
||||||
|
MapBlockMesh *mapBlockMesh = block->mesh;
|
||||||
|
assert(mapBlockMesh);
|
||||||
|
|
||||||
|
scene::SMesh *mesh = mapBlockMesh->getMesh();
|
||||||
|
assert(mesh);
|
||||||
|
|
||||||
|
u32 c = mesh->getMeshBufferCount();
|
||||||
|
bool stuff_actually_drawn = false;
|
||||||
|
for(u32 i=0; i<c; i++)
|
||||||
|
{
|
||||||
|
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
|
||||||
|
const video::SMaterial& material = buf->getMaterial();
|
||||||
|
video::IMaterialRenderer* rnd =
|
||||||
|
driver->getMaterialRenderer(material.MaterialType);
|
||||||
|
bool transparent = (rnd && rnd->isTransparent());
|
||||||
|
// Render transparent on transparent pass and likewise.
|
||||||
|
if(transparent == is_transparent_pass)
|
||||||
|
{
|
||||||
|
if(buf->getVertexCount() == 0)
|
||||||
|
errorstream<<"Block ["<<analyze_block(block)
|
||||||
|
<<"] contains an empty meshbuf"<<std::endl;
|
||||||
|
/*
|
||||||
|
This *shouldn't* hurt too much because Irrlicht
|
||||||
|
doesn't change opengl textures if the old
|
||||||
|
material has the same texture.
|
||||||
|
*/
|
||||||
|
driver->setMaterial(buf->getMaterial());
|
||||||
|
driver->drawMeshBuffer(buf);
|
||||||
|
vertex_count += buf->getVertexCount();
|
||||||
|
meshbuffer_count++;
|
||||||
|
stuff_actually_drawn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(stuff_actually_drawn)
|
||||||
|
blocks_had_pass_meshbuf++;
|
||||||
|
else
|
||||||
|
blocks_without_stuff++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // ScopeProfiler
|
||||||
|
|
||||||
|
// Log only on solid pass because values are the same
|
||||||
|
if(pass == scene::ESNRP_SOLID){
|
||||||
|
g_profiler->avg("CM: blocks in range", blocks_in_range);
|
||||||
|
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
|
||||||
|
if(blocks_in_range != 0)
|
||||||
|
g_profiler->avg("CM: blocks in range without mesh (frac)",
|
||||||
|
(float)blocks_in_range_without_mesh/blocks_in_range);
|
||||||
|
g_profiler->avg("CM: blocks drawn", blocks_drawn);
|
||||||
|
g_profiler->avg("CM: animated meshes", mesh_animate_count);
|
||||||
|
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_profiler->avg(prefix+"vertices drawn", vertex_count);
|
||||||
|
if(blocks_had_pass_meshbuf != 0)
|
||||||
|
g_profiler->avg(prefix+"meshbuffers per block",
|
||||||
|
(float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
|
||||||
|
if(blocks_drawn != 0)
|
||||||
|
g_profiler->avg(prefix+"empty blocks (frac)",
|
||||||
|
(float)blocks_without_stuff / blocks_drawn);
|
||||||
|
|
||||||
|
m_control.blocks_drawn = blocks_drawn;
|
||||||
|
m_control.blocks_would_have_drawn = blocks_would_have_drawn;
|
||||||
|
|
||||||
|
/*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
|
||||||
|
<<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientMap::renderPostFx()
|
||||||
|
{
|
||||||
|
INodeDefManager *nodemgr = m_gamedef->ndef();
|
||||||
|
|
||||||
|
// Sadly ISceneManager has no "post effects" render pass, in that case we
|
||||||
|
// could just register for that and handle it in renderMap().
|
||||||
|
|
||||||
|
m_camera_mutex.Lock();
|
||||||
|
v3f camera_position = m_camera_position;
|
||||||
|
m_camera_mutex.Unlock();
|
||||||
|
|
||||||
|
MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
|
||||||
|
|
||||||
|
// - If the player is in a solid node, make everything black.
|
||||||
|
// - If the player is in liquid, draw a semi-transparent overlay.
|
||||||
|
const ContentFeatures& features = nodemgr->get(n);
|
||||||
|
video::SColor post_effect_color = features.post_effect_color;
|
||||||
|
if(features.solidness == 2 && g_settings->getBool("free_move") == false)
|
||||||
|
{
|
||||||
|
post_effect_color = video::SColor(255, 0, 0, 0);
|
||||||
|
}
|
||||||
|
if (post_effect_color.getAlpha() != 0)
|
||||||
|
{
|
||||||
|
// Draw a full-screen rectangle
|
||||||
|
video::IVideoDriver* driver = SceneManager->getVideoDriver();
|
||||||
|
v2u32 ss = driver->getScreenSize();
|
||||||
|
core::rect<s32> rect(0,0, ss.X, ss.Y);
|
||||||
|
driver->draw2DRectangle(post_effect_color, rect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientMap::PrintInfo(std::ostream &out)
|
||||||
|
{
|
||||||
|
out<<"ClientMap: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
/*
|
||||||
|
Minetest-c55
|
||||||
|
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License along
|
||||||
|
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CLIENTMAP_HEADER
|
||||||
|
#define CLIENTMAP_HEADER
|
||||||
|
|
||||||
|
#include "common_irrlicht.h"
|
||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
struct MapDrawControl
|
||||||
|
{
|
||||||
|
MapDrawControl():
|
||||||
|
range_all(false),
|
||||||
|
wanted_range(50),
|
||||||
|
wanted_max_blocks(0),
|
||||||
|
wanted_min_range(0),
|
||||||
|
blocks_drawn(0),
|
||||||
|
blocks_would_have_drawn(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// Overrides limits by drawing everything
|
||||||
|
bool range_all;
|
||||||
|
// Wanted drawing range
|
||||||
|
float wanted_range;
|
||||||
|
// Maximum number of blocks to draw
|
||||||
|
u32 wanted_max_blocks;
|
||||||
|
// Blocks in this range are drawn regardless of number of blocks drawn
|
||||||
|
float wanted_min_range;
|
||||||
|
// Number of blocks rendered is written here by the renderer
|
||||||
|
u32 blocks_drawn;
|
||||||
|
// Number of blocks that would have been drawn in wanted_range
|
||||||
|
u32 blocks_would_have_drawn;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
class ITextureSource;
|
||||||
|
|
||||||
|
/*
|
||||||
|
ClientMap
|
||||||
|
|
||||||
|
This is the only map class that is able to render itself on screen.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ClientMap : public Map, public scene::ISceneNode
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ClientMap(
|
||||||
|
Client *client,
|
||||||
|
IGameDef *gamedef,
|
||||||
|
MapDrawControl &control,
|
||||||
|
scene::ISceneNode* parent,
|
||||||
|
scene::ISceneManager* mgr,
|
||||||
|
s32 id
|
||||||
|
);
|
||||||
|
|
||||||
|
~ClientMap();
|
||||||
|
|
||||||
|
s32 mapType() const
|
||||||
|
{
|
||||||
|
return MAPTYPE_CLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void drop()
|
||||||
|
{
|
||||||
|
ISceneNode::drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateCamera(v3f pos, v3f dir, f32 fov)
|
||||||
|
{
|
||||||
|
JMutexAutoLock lock(m_camera_mutex);
|
||||||
|
m_camera_position = pos;
|
||||||
|
m_camera_direction = dir;
|
||||||
|
m_camera_fov = fov;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Forcefully get a sector from somewhere
|
||||||
|
*/
|
||||||
|
MapSector * emergeSector(v2s16 p);
|
||||||
|
|
||||||
|
//void deSerializeSector(v2s16 p2d, std::istream &is);
|
||||||
|
|
||||||
|
/*
|
||||||
|
ISceneNode methods
|
||||||
|
*/
|
||||||
|
|
||||||
|
virtual void OnRegisterSceneNode();
|
||||||
|
|
||||||
|
virtual void render()
|
||||||
|
{
|
||||||
|
video::IVideoDriver* driver = SceneManager->getVideoDriver();
|
||||||
|
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
||||||
|
renderMap(driver, SceneManager->getSceneNodeRenderPass());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const core::aabbox3d<f32>& getBoundingBox() const
|
||||||
|
{
|
||||||
|
return m_box;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renderMap(video::IVideoDriver* driver, s32 pass);
|
||||||
|
|
||||||
|
void renderPostFx();
|
||||||
|
|
||||||
|
// For debug printing
|
||||||
|
virtual void PrintInfo(std::ostream &out);
|
||||||
|
|
||||||
|
// Check if sector was drawn on last render()
|
||||||
|
bool sectorWasDrawn(v2s16 p)
|
||||||
|
{
|
||||||
|
return (m_last_drawn_sectors.find(p) != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Client *m_client;
|
||||||
|
|
||||||
|
core::aabbox3d<f32> m_box;
|
||||||
|
|
||||||
|
MapDrawControl &m_control;
|
||||||
|
|
||||||
|
v3f m_camera_position;
|
||||||
|
v3f m_camera_direction;
|
||||||
|
f32 m_camera_fov;
|
||||||
|
JMutex m_camera_mutex;
|
||||||
|
|
||||||
|
core::map<v2s16, bool> m_last_drawn_sectors;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -38,6 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "main.h" // For g_settings, g_profiler
|
#include "main.h" // For g_settings, g_profiler
|
||||||
#include "gamedef.h"
|
#include "gamedef.h"
|
||||||
#include "serverremoteplayer.h"
|
#include "serverremoteplayer.h"
|
||||||
|
#ifndef SERVER
|
||||||
|
#include "clientmap.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||||
|
|
||||||
|
@ -1820,6 +1823,16 @@ ClientEnvironment::~ClientEnvironment()
|
||||||
m_map->drop();
|
m_map->drop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map & ClientEnvironment::getMap()
|
||||||
|
{
|
||||||
|
return *m_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientMap & ClientEnvironment::getClientMap()
|
||||||
|
{
|
||||||
|
return *m_map;
|
||||||
|
}
|
||||||
|
|
||||||
void ClientEnvironment::addPlayer(Player *player)
|
void ClientEnvironment::addPlayer(Player *player)
|
||||||
{
|
{
|
||||||
DSTACK(__FUNCTION_NAME);
|
DSTACK(__FUNCTION_NAME);
|
||||||
|
|
|
@ -45,6 +45,7 @@ class ServerActiveObject;
|
||||||
typedef struct lua_State lua_State;
|
typedef struct lua_State lua_State;
|
||||||
class ITextureSource;
|
class ITextureSource;
|
||||||
class IGameDef;
|
class IGameDef;
|
||||||
|
class ClientMap;
|
||||||
|
|
||||||
class Environment
|
class Environment
|
||||||
{
|
{
|
||||||
|
@ -393,11 +394,8 @@ public:
|
||||||
IrrlichtDevice *device);
|
IrrlichtDevice *device);
|
||||||
~ClientEnvironment();
|
~ClientEnvironment();
|
||||||
|
|
||||||
Map & getMap()
|
Map & getMap();
|
||||||
{ return *m_map; }
|
ClientMap & getClientMap();
|
||||||
|
|
||||||
ClientMap & getClientMap()
|
|
||||||
{ return *m_map; }
|
|
||||||
|
|
||||||
IGameDef *getGameDef()
|
IGameDef *getGameDef()
|
||||||
{ return m_gamedef; }
|
{ return m_gamedef; }
|
||||||
|
|
|
@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "tile.h" // ITextureSource
|
#include "tile.h" // ITextureSource
|
||||||
|
#include "clientmap.h"
|
||||||
|
|
||||||
#include "mapgen.h" // Shouldn't really be done this way
|
#include "mapgen.h" // Shouldn't really be done this way
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "logoutputbuffer.h"
|
#include "logoutputbuffer.h"
|
||||||
#include "subgame.h"
|
#include "subgame.h"
|
||||||
#include "quicktune_shortcutter.h"
|
#include "quicktune_shortcutter.h"
|
||||||
|
#include "clientmap.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Setting this to 1 enables a special camera mode that forces
|
Setting this to 1 enables a special camera mode that forces
|
||||||
|
|
529
src/map.cpp
529
src/map.cpp
|
@ -32,11 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
#include "profiler.h"
|
#include "profiler.h"
|
||||||
#include "nodedef.h"
|
#include "nodedef.h"
|
||||||
#include "gamedef.h"
|
#include "gamedef.h"
|
||||||
#ifndef SERVER
|
|
||||||
#include "client.h"
|
|
||||||
#include "mapblock_mesh.h"
|
|
||||||
#include <IMaterialRenderer.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
|
||||||
|
|
||||||
|
@ -3514,530 +3509,6 @@ void ServerMap::PrintInfo(std::ostream &out)
|
||||||
out<<"ServerMap: ";
|
out<<"ServerMap: ";
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SERVER
|
|
||||||
|
|
||||||
/*
|
|
||||||
ClientMap
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClientMap::ClientMap(
|
|
||||||
Client *client,
|
|
||||||
IGameDef *gamedef,
|
|
||||||
MapDrawControl &control,
|
|
||||||
scene::ISceneNode* parent,
|
|
||||||
scene::ISceneManager* mgr,
|
|
||||||
s32 id
|
|
||||||
):
|
|
||||||
Map(dout_client, gamedef),
|
|
||||||
scene::ISceneNode(parent, mgr, id),
|
|
||||||
m_client(client),
|
|
||||||
m_control(control),
|
|
||||||
m_camera_position(0,0,0),
|
|
||||||
m_camera_direction(0,0,1),
|
|
||||||
m_camera_fov(PI)
|
|
||||||
{
|
|
||||||
m_camera_mutex.Init();
|
|
||||||
assert(m_camera_mutex.IsInitialized());
|
|
||||||
|
|
||||||
m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
|
|
||||||
BS*1000000,BS*1000000,BS*1000000);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientMap::~ClientMap()
|
|
||||||
{
|
|
||||||
/*JMutexAutoLock lock(mesh_mutex);
|
|
||||||
|
|
||||||
if(mesh != NULL)
|
|
||||||
{
|
|
||||||
mesh->drop();
|
|
||||||
mesh = NULL;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
MapSector * ClientMap::emergeSector(v2s16 p2d)
|
|
||||||
{
|
|
||||||
DSTACK(__FUNCTION_NAME);
|
|
||||||
// Check that it doesn't exist already
|
|
||||||
try{
|
|
||||||
return getSectorNoGenerate(p2d);
|
|
||||||
}
|
|
||||||
catch(InvalidPositionException &e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a sector
|
|
||||||
ClientMapSector *sector = new ClientMapSector(this, p2d, m_gamedef);
|
|
||||||
|
|
||||||
{
|
|
||||||
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
|
|
||||||
m_sectors.insert(p2d, sector);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sector;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
|
|
||||||
{
|
|
||||||
DSTACK(__FUNCTION_NAME);
|
|
||||||
ClientMapSector *sector = NULL;
|
|
||||||
|
|
||||||
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
|
|
||||||
|
|
||||||
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
|
|
||||||
|
|
||||||
if(n != NULL)
|
|
||||||
{
|
|
||||||
sector = (ClientMapSector*)n->getValue();
|
|
||||||
assert(sector->getId() == MAPSECTOR_CLIENT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sector = new ClientMapSector(this, p2d);
|
|
||||||
{
|
|
||||||
//JMutexAutoLock lock(m_sector_mutex); // Bulk comment-out
|
|
||||||
m_sectors.insert(p2d, sector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sector->deSerialize(is);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void ClientMap::OnRegisterSceneNode()
|
|
||||||
{
|
|
||||||
if(IsVisible)
|
|
||||||
{
|
|
||||||
SceneManager->registerNodeForRendering(this, scene::ESNRP_SOLID);
|
|
||||||
SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
ISceneNode::OnRegisterSceneNode();
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isOccluded(Map *map, v3s16 p0, v3s16 p1, float step, float stepfac,
|
|
||||||
float start_off, float end_off, u32 needed_count, INodeDefManager *nodemgr)
|
|
||||||
{
|
|
||||||
float d0 = (float)BS * p0.getDistanceFrom(p1);
|
|
||||||
v3s16 u0 = p1 - p0;
|
|
||||||
v3f uf = v3f(u0.X, u0.Y, u0.Z) * BS;
|
|
||||||
uf.normalize();
|
|
||||||
v3f p0f = v3f(p0.X, p0.Y, p0.Z) * BS;
|
|
||||||
u32 count = 0;
|
|
||||||
for(float s=start_off; s<d0+end_off; s+=step){
|
|
||||||
v3f pf = p0f + uf * s;
|
|
||||||
v3s16 p = floatToInt(pf, BS);
|
|
||||||
MapNode n = map->getNodeNoEx(p);
|
|
||||||
bool is_transparent = false;
|
|
||||||
const ContentFeatures &f = nodemgr->get(n);
|
|
||||||
if(f.solidness == 0)
|
|
||||||
is_transparent = (f.visual_solidness != 2);
|
|
||||||
else
|
|
||||||
is_transparent = (f.solidness != 2);
|
|
||||||
if(!is_transparent){
|
|
||||||
count++;
|
|
||||||
if(count >= needed_count)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
step *= stepfac;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass)
|
|
||||||
{
|
|
||||||
INodeDefManager *nodemgr = m_gamedef->ndef();
|
|
||||||
|
|
||||||
//m_dout<<DTIME<<"Rendering map..."<<std::endl;
|
|
||||||
DSTACK(__FUNCTION_NAME);
|
|
||||||
|
|
||||||
bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
|
|
||||||
|
|
||||||
std::string prefix;
|
|
||||||
if(pass == scene::ESNRP_SOLID)
|
|
||||||
prefix = "CM: solid: ";
|
|
||||||
else
|
|
||||||
prefix = "CM: transparent: ";
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is called two times per frame, reset on the non-transparent one
|
|
||||||
*/
|
|
||||||
if(pass == scene::ESNRP_SOLID)
|
|
||||||
{
|
|
||||||
m_last_drawn_sectors.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Get time for measuring timeout.
|
|
||||||
|
|
||||||
Measuring time is very useful for long delays when the
|
|
||||||
machine is swapping a lot.
|
|
||||||
*/
|
|
||||||
int time1 = time(0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
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;
|
|
||||||
v3f camera_direction = m_camera_direction;
|
|
||||||
f32 camera_fov = m_camera_fov;
|
|
||||||
m_camera_mutex.Unlock();
|
|
||||||
|
|
||||||
/*
|
|
||||||
Get all blocks and draw all visible ones
|
|
||||||
*/
|
|
||||||
|
|
||||||
v3s16 cam_pos_nodes = floatToInt(camera_position, BS);
|
|
||||||
|
|
||||||
v3s16 box_nodes_d = m_control.wanted_range * v3s16(1,1,1);
|
|
||||||
|
|
||||||
v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
|
|
||||||
v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
|
|
||||||
|
|
||||||
// Take a fair amount as we will be dropping more out later
|
|
||||||
// Umm... these additions are a bit strange but they are needed.
|
|
||||||
v3s16 p_blocks_min(
|
|
||||||
p_nodes_min.X / MAP_BLOCKSIZE - 3,
|
|
||||||
p_nodes_min.Y / MAP_BLOCKSIZE - 3,
|
|
||||||
p_nodes_min.Z / MAP_BLOCKSIZE - 3);
|
|
||||||
v3s16 p_blocks_max(
|
|
||||||
p_nodes_max.X / MAP_BLOCKSIZE + 1,
|
|
||||||
p_nodes_max.Y / MAP_BLOCKSIZE + 1,
|
|
||||||
p_nodes_max.Z / MAP_BLOCKSIZE + 1);
|
|
||||||
|
|
||||||
u32 vertex_count = 0;
|
|
||||||
u32 meshbuffer_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;
|
|
||||||
// Number of blocks occlusion culled
|
|
||||||
u32 blocks_occlusion_culled = 0;
|
|
||||||
// Number of blocks in rendering range but don't have a mesh
|
|
||||||
u32 blocks_in_range_without_mesh = 0;
|
|
||||||
// Blocks that had mesh that would have been drawn according to
|
|
||||||
// rendering range (if max blocks limit didn't kick in)
|
|
||||||
u32 blocks_would_have_drawn = 0;
|
|
||||||
// Blocks that were drawn and had a mesh
|
|
||||||
u32 blocks_drawn = 0;
|
|
||||||
// Blocks which had a corresponding meshbuffer for this pass
|
|
||||||
u32 blocks_had_pass_meshbuf = 0;
|
|
||||||
// Blocks from which stuff was actually drawn
|
|
||||||
u32 blocks_without_stuff = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Collect a set of blocks for drawing
|
|
||||||
*/
|
|
||||||
|
|
||||||
core::map<v3s16, MapBlock*> drawset;
|
|
||||||
|
|
||||||
{
|
|
||||||
ScopeProfiler sp(g_profiler, prefix+"collecting blocks for drawing", SPT_AVG);
|
|
||||||
|
|
||||||
for(core::map<v2s16, MapSector*>::Iterator
|
|
||||||
si = m_sectors.getIterator();
|
|
||||||
si.atEnd() == false; si++)
|
|
||||||
{
|
|
||||||
MapSector *sector = si.getNode()->getValue();
|
|
||||||
v2s16 sp = sector->getPos();
|
|
||||||
|
|
||||||
if(m_control.range_all == false)
|
|
||||||
{
|
|
||||||
if(sp.X < p_blocks_min.X
|
|
||||||
|| sp.X > p_blocks_max.X
|
|
||||||
|| sp.Y < p_blocks_min.Z
|
|
||||||
|| sp.Y > p_blocks_max.Z)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
core::list< MapBlock * > sectorblocks;
|
|
||||||
sector->getBlocks(sectorblocks);
|
|
||||||
|
|
||||||
/*
|
|
||||||
Loop through blocks in sector
|
|
||||||
*/
|
|
||||||
|
|
||||||
u32 sector_blocks_drawn = 0;
|
|
||||||
|
|
||||||
core::list< MapBlock * >::Iterator i;
|
|
||||||
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
|
|
||||||
{
|
|
||||||
MapBlock *block = *i;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Compare block position to camera position, skip
|
|
||||||
if not seen on display
|
|
||||||
*/
|
|
||||||
|
|
||||||
float range = 100000 * BS;
|
|
||||||
if(m_control.range_all == false)
|
|
||||||
range = m_control.wanted_range * BS;
|
|
||||||
|
|
||||||
float d = 0.0;
|
|
||||||
if(isBlockInSight(block->getPos(), camera_position,
|
|
||||||
camera_direction, camera_fov,
|
|
||||||
range, &d) == false)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is ugly (spherical distance limit?)
|
|
||||||
/*if(m_control.range_all == false &&
|
|
||||||
d - 0.5*BS*MAP_BLOCKSIZE > range)
|
|
||||||
continue;*/
|
|
||||||
|
|
||||||
blocks_in_range++;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Ignore if mesh doesn't exist
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
//JMutexAutoLock lock(block->mesh_mutex);
|
|
||||||
|
|
||||||
if(block->mesh == NULL){
|
|
||||||
blocks_in_range_without_mesh++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Occlusion culling
|
|
||||||
*/
|
|
||||||
|
|
||||||
v3s16 cpn = block->getPos() * MAP_BLOCKSIZE;
|
|
||||||
cpn += v3s16(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2);
|
|
||||||
float step = BS*1;
|
|
||||||
float stepfac = 1.1;
|
|
||||||
float startoff = BS*1;
|
|
||||||
float endoff = -BS*MAP_BLOCKSIZE*1.42*1.42;
|
|
||||||
v3s16 spn = cam_pos_nodes + v3s16(0,0,0);
|
|
||||||
s16 bs2 = MAP_BLOCKSIZE/2 + 1;
|
|
||||||
u32 needed_count = 1;
|
|
||||||
if(
|
|
||||||
isOccluded(this, spn, cpn + v3s16(0,0,0),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(bs2,bs2,bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(bs2,bs2,-bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(bs2,-bs2,bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(bs2,-bs2,-bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(-bs2,bs2,bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(-bs2,bs2,-bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr) &&
|
|
||||||
isOccluded(this, spn, cpn + v3s16(-bs2,-bs2,-bs2),
|
|
||||||
step, stepfac, startoff, endoff, needed_count, nodemgr)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
blocks_occlusion_culled++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This block is in range. Reset usage timer.
|
|
||||||
block->resetUsageTimer();
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
sector_blocks_drawn++;
|
|
||||||
blocks_drawn++;
|
|
||||||
|
|
||||||
} // foreach sectorblocks
|
|
||||||
|
|
||||||
if(sector_blocks_drawn != 0)
|
|
||||||
m_last_drawn_sectors[sp] = true;
|
|
||||||
}
|
|
||||||
} // ScopeProfiler
|
|
||||||
|
|
||||||
/*
|
|
||||||
Draw the selected MapBlocks
|
|
||||||
*/
|
|
||||||
|
|
||||||
{
|
|
||||||
ScopeProfiler sp(g_profiler, prefix+"drawing blocks", SPT_AVG);
|
|
||||||
|
|
||||||
int timecheck_counter = 0;
|
|
||||||
for(core::map<v3s16, MapBlock*>::Iterator
|
|
||||||
i = drawset.getIterator();
|
|
||||||
i.atEnd() == false; i++)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
timecheck_counter++;
|
|
||||||
if(timecheck_counter > 50)
|
|
||||||
{
|
|
||||||
timecheck_counter = 0;
|
|
||||||
int time2 = time(0);
|
|
||||||
if(time2 > time1 + 4)
|
|
||||||
{
|
|
||||||
infostream<<"ClientMap::renderMap(): "
|
|
||||||
"Rendering takes ages, returning."
|
|
||||||
<<std::endl;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MapBlock *block = i.getNode()->getValue();
|
|
||||||
|
|
||||||
/*
|
|
||||||
Draw the faces of the block
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
//JMutexAutoLock lock(block->mesh_mutex);
|
|
||||||
|
|
||||||
MapBlockMesh *mapBlockMesh = block->mesh;
|
|
||||||
assert(mapBlockMesh);
|
|
||||||
|
|
||||||
scene::SMesh *mesh = mapBlockMesh->getMesh();
|
|
||||||
assert(mesh);
|
|
||||||
|
|
||||||
u32 c = mesh->getMeshBufferCount();
|
|
||||||
bool stuff_actually_drawn = false;
|
|
||||||
for(u32 i=0; i<c; i++)
|
|
||||||
{
|
|
||||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
|
|
||||||
const video::SMaterial& material = buf->getMaterial();
|
|
||||||
video::IMaterialRenderer* rnd =
|
|
||||||
driver->getMaterialRenderer(material.MaterialType);
|
|
||||||
bool transparent = (rnd && rnd->isTransparent());
|
|
||||||
// Render transparent on transparent pass and likewise.
|
|
||||||
if(transparent == is_transparent_pass)
|
|
||||||
{
|
|
||||||
if(buf->getVertexCount() == 0)
|
|
||||||
errorstream<<"Block ["<<analyze_block(block)
|
|
||||||
<<"] contains an empty meshbuf"<<std::endl;
|
|
||||||
/*
|
|
||||||
This *shouldn't* hurt too much because Irrlicht
|
|
||||||
doesn't change opengl textures if the old
|
|
||||||
material has the same texture.
|
|
||||||
*/
|
|
||||||
driver->setMaterial(buf->getMaterial());
|
|
||||||
driver->drawMeshBuffer(buf);
|
|
||||||
vertex_count += buf->getVertexCount();
|
|
||||||
meshbuffer_count++;
|
|
||||||
stuff_actually_drawn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(stuff_actually_drawn)
|
|
||||||
blocks_had_pass_meshbuf++;
|
|
||||||
else
|
|
||||||
blocks_without_stuff++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // ScopeProfiler
|
|
||||||
|
|
||||||
// Log only on solid pass because values are the same
|
|
||||||
if(pass == scene::ESNRP_SOLID){
|
|
||||||
g_profiler->avg("CM: blocks in range", blocks_in_range);
|
|
||||||
g_profiler->avg("CM: blocks occlusion culled", blocks_occlusion_culled);
|
|
||||||
if(blocks_in_range != 0)
|
|
||||||
g_profiler->avg("CM: blocks in range without mesh (frac)",
|
|
||||||
(float)blocks_in_range_without_mesh/blocks_in_range);
|
|
||||||
g_profiler->avg("CM: blocks drawn", blocks_drawn);
|
|
||||||
g_profiler->avg("CM: animated meshes", mesh_animate_count);
|
|
||||||
g_profiler->avg("CM: animated meshes (far)", mesh_animate_count_far);
|
|
||||||
}
|
|
||||||
|
|
||||||
g_profiler->avg(prefix+"vertices drawn", vertex_count);
|
|
||||||
if(blocks_had_pass_meshbuf != 0)
|
|
||||||
g_profiler->avg(prefix+"meshbuffers per block",
|
|
||||||
(float)meshbuffer_count / (float)blocks_had_pass_meshbuf);
|
|
||||||
if(blocks_drawn != 0)
|
|
||||||
g_profiler->avg(prefix+"empty blocks (frac)",
|
|
||||||
(float)blocks_without_stuff / blocks_drawn);
|
|
||||||
|
|
||||||
m_control.blocks_drawn = blocks_drawn;
|
|
||||||
m_control.blocks_would_have_drawn = blocks_would_have_drawn;
|
|
||||||
|
|
||||||
/*infostream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
|
|
||||||
<<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientMap::renderPostFx()
|
|
||||||
{
|
|
||||||
INodeDefManager *nodemgr = m_gamedef->ndef();
|
|
||||||
|
|
||||||
// Sadly ISceneManager has no "post effects" render pass, in that case we
|
|
||||||
// could just register for that and handle it in renderMap().
|
|
||||||
|
|
||||||
m_camera_mutex.Lock();
|
|
||||||
v3f camera_position = m_camera_position;
|
|
||||||
m_camera_mutex.Unlock();
|
|
||||||
|
|
||||||
MapNode n = getNodeNoEx(floatToInt(camera_position, BS));
|
|
||||||
|
|
||||||
// - If the player is in a solid node, make everything black.
|
|
||||||
// - If the player is in liquid, draw a semi-transparent overlay.
|
|
||||||
const ContentFeatures& features = nodemgr->get(n);
|
|
||||||
video::SColor post_effect_color = features.post_effect_color;
|
|
||||||
if(features.solidness == 2 && g_settings->getBool("free_move") == false)
|
|
||||||
{
|
|
||||||
post_effect_color = video::SColor(255, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if (post_effect_color.getAlpha() != 0)
|
|
||||||
{
|
|
||||||
// Draw a full-screen rectangle
|
|
||||||
video::IVideoDriver* driver = SceneManager->getVideoDriver();
|
|
||||||
v2u32 ss = driver->getScreenSize();
|
|
||||||
core::rect<s32> rect(0,0, ss.X, ss.Y);
|
|
||||||
driver->draw2DRectangle(post_effect_color, rect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientMap::PrintInfo(std::ostream &out)
|
|
||||||
{
|
|
||||||
out<<"ClientMap: ";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // !SERVER
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
MapVoxelManipulator
|
MapVoxelManipulator
|
||||||
*/
|
*/
|
||||||
|
|
133
src/map.h
133
src/map.h
|
@ -37,9 +37,9 @@ extern "C" {
|
||||||
#include "sqlite3.h"
|
#include "sqlite3.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ClientMap;
|
||||||
class MapSector;
|
class MapSector;
|
||||||
class ServerMapSector;
|
class ServerMapSector;
|
||||||
class ClientMapSector;
|
|
||||||
class MapBlock;
|
class MapBlock;
|
||||||
class NodeMetadata;
|
class NodeMetadata;
|
||||||
class IGameDef;
|
class IGameDef;
|
||||||
|
@ -468,137 +468,6 @@ private:
|
||||||
sqlite3_stmt *m_database_list;
|
sqlite3_stmt *m_database_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
ClientMap stuff
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SERVER
|
|
||||||
|
|
||||||
struct MapDrawControl
|
|
||||||
{
|
|
||||||
MapDrawControl():
|
|
||||||
range_all(false),
|
|
||||||
wanted_range(50),
|
|
||||||
wanted_max_blocks(0),
|
|
||||||
wanted_min_range(0),
|
|
||||||
blocks_drawn(0),
|
|
||||||
blocks_would_have_drawn(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
// Overrides limits by drawing everything
|
|
||||||
bool range_all;
|
|
||||||
// Wanted drawing range
|
|
||||||
float wanted_range;
|
|
||||||
// Maximum number of blocks to draw
|
|
||||||
u32 wanted_max_blocks;
|
|
||||||
// Blocks in this range are drawn regardless of number of blocks drawn
|
|
||||||
float wanted_min_range;
|
|
||||||
// Number of blocks rendered is written here by the renderer
|
|
||||||
u32 blocks_drawn;
|
|
||||||
// Number of blocks that would have been drawn in wanted_range
|
|
||||||
u32 blocks_would_have_drawn;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Client;
|
|
||||||
class ITextureSource;
|
|
||||||
|
|
||||||
/*
|
|
||||||
ClientMap
|
|
||||||
|
|
||||||
This is the only map class that is able to render itself on screen.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class ClientMap : public Map, public scene::ISceneNode
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ClientMap(
|
|
||||||
Client *client,
|
|
||||||
IGameDef *gamedef,
|
|
||||||
MapDrawControl &control,
|
|
||||||
scene::ISceneNode* parent,
|
|
||||||
scene::ISceneManager* mgr,
|
|
||||||
s32 id
|
|
||||||
);
|
|
||||||
|
|
||||||
~ClientMap();
|
|
||||||
|
|
||||||
s32 mapType() const
|
|
||||||
{
|
|
||||||
return MAPTYPE_CLIENT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void drop()
|
|
||||||
{
|
|
||||||
ISceneNode::drop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateCamera(v3f pos, v3f dir, f32 fov)
|
|
||||||
{
|
|
||||||
JMutexAutoLock lock(m_camera_mutex);
|
|
||||||
m_camera_position = pos;
|
|
||||||
m_camera_direction = dir;
|
|
||||||
m_camera_fov = fov;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Forcefully get a sector from somewhere
|
|
||||||
*/
|
|
||||||
MapSector * emergeSector(v2s16 p);
|
|
||||||
|
|
||||||
//void deSerializeSector(v2s16 p2d, std::istream &is);
|
|
||||||
|
|
||||||
/*
|
|
||||||
ISceneNode methods
|
|
||||||
*/
|
|
||||||
|
|
||||||
virtual void OnRegisterSceneNode();
|
|
||||||
|
|
||||||
virtual void render()
|
|
||||||
{
|
|
||||||
video::IVideoDriver* driver = SceneManager->getVideoDriver();
|
|
||||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
|
||||||
renderMap(driver, SceneManager->getSceneNodeRenderPass());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const core::aabbox3d<f32>& getBoundingBox() const
|
|
||||||
{
|
|
||||||
return m_box;
|
|
||||||
}
|
|
||||||
|
|
||||||
void renderMap(video::IVideoDriver* driver, s32 pass);
|
|
||||||
|
|
||||||
void renderPostFx();
|
|
||||||
|
|
||||||
// For debug printing
|
|
||||||
virtual void PrintInfo(std::ostream &out);
|
|
||||||
|
|
||||||
// Check if sector was drawn on last render()
|
|
||||||
bool sectorWasDrawn(v2s16 p)
|
|
||||||
{
|
|
||||||
return (m_last_drawn_sectors.find(p) != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Client *m_client;
|
|
||||||
|
|
||||||
core::aabbox3d<f32> m_box;
|
|
||||||
|
|
||||||
// This is the master heightmap mesh
|
|
||||||
//scene::SMesh *mesh;
|
|
||||||
//JMutex mesh_mutex;
|
|
||||||
|
|
||||||
MapDrawControl &m_control;
|
|
||||||
|
|
||||||
v3f m_camera_position;
|
|
||||||
v3f m_camera_direction;
|
|
||||||
f32 m_camera_fov;
|
|
||||||
JMutex m_camera_mutex;
|
|
||||||
|
|
||||||
core::map<v2s16, bool> m_last_drawn_sectors;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class MapVoxelManipulator : public VoxelManipulator
|
class MapVoxelManipulator : public VoxelManipulator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
Loading…
Reference in New Issue