minetest_nmpr/src/map.cpp

1205 lines
27 KiB
C++

/*
(c) 2010 Perttu Ahola <celeron55@gmail.com>
*/
#include "map.h"
//#include "player.h"
#include "main.h"
#include "jmutexautolock.h"
#include "client.h"
#ifdef _WIN32
#include <windows.h>
#define sleep_ms(x) Sleep(x)
#else
#include <unistd.h>
#define sleep_ms(x) usleep(x*1000)
#endif
/*
void limitBox(core::aabbox3d<s16> & box, core::aabbox3d<s16> & limits)
{
if(box.MinEdge.X < limits.MinEdge.X)
box.MinEdge.X = limits.MinEdge.X;
if(box.MaxEdge.X > limits.MaxEdge.X)
box.MaxEdge.X = limits.MaxEdge.X;
if(box.MinEdge.Y < limits.MinEdge.Y)
box.MinEdge.Y = limits.MinEdge.Y;
if(box.MaxEdge.Y > limits.MaxEdge.Y)
box.MaxEdge.Y = limits.MaxEdge.Y;
if(box.MinEdge.Z < limits.MinEdge.Z)
box.MinEdge.Z = limits.MinEdge.Z;
if(box.MaxEdge.Z > limits.MaxEdge.Z)
box.MaxEdge.Z = limits.MaxEdge.Z;
}
*/
void * MapUpdateThread::Thread()
{
if(map == NULL)
return NULL;
ThreadStarted();
while(getRun())
{
//std::cout<<"UpdateThread running"<<std::endl;
bool did_something;
did_something = map->updateChangedVisibleArea();
if(did_something == false)
sleep_ms(500);
}
std::cout<<"UpdateThread stopped"<<std::endl;
return NULL;
}
Map::Map():
camera_position(0,0,0),
camera_direction(0,0,1),
updater(this),
m_heightmap(32, 66.0, 0.6, 0.0),
//m_heightmap(16, 40.0, 0.6, 0.0),
//m_heightmap(16, 0.0, 0.0, 0.0),
//m_heightmap(4, 0.0, 0.0, 0.0),
m_sector_cache(NULL),
m_hwrapper(this),
drawoffset(0,0,0)
{
m_getsector_mutex.Init();
m_gensector_mutex.Init();
camera_mutex.Init();
assert(m_getsector_mutex.IsInitialized());
assert(m_gensector_mutex.IsInitialized());
assert(camera_mutex.IsInitialized());
// Get this so that the player can stay on it at first
//getSector(v2s16(0,0));
}
Map::~Map()
{
/*
Stop updater thread
*/
updater.setRun(false);
while(updater.IsRunning())
sleep_s(1);
/*
Free all MapSectors.
*/
core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
for(; i.atEnd() == false; i++)
{
MapSector *sector = i.getNode()->getValue();
delete sector;
}
}
/*
TODO: These mutexes need rethinking. They don't work properly
at the moment. Maybe. Or do they?
*/
/*bool Map::sectorExists(v2s16 p)
{
JMutexAutoLock lock(m_getsector_mutex);
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
return (n != NULL);
}*/
MapSector * Map::getSectorNoGenerate(v2s16 p)
{
//JMutexAutoLock lock(m_getsector_mutex);
if(m_sector_cache != NULL && p == m_sector_cache_p){
MapSector * ref(m_sector_cache);
return ref;
}
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
// If sector doesn't exist, throw an exception
if(n == NULL)
{
/*
TODO: Check if sector is stored on disk
and load dynamically
*/
//sector = NULL;
throw InvalidPositionException();
}
MapSector *sector = n->getValue();
// Cache the last result
m_sector_cache_p = p;
m_sector_cache = sector;
//MapSector * ref(sector);
return sector;
}
MapSector * Map::getSector(v2s16 p2d)
{
// Stub virtual method
assert(0);
return getSectorNoGenerate(p2d);
}
/*
If sector doesn't exist, returns NULL
Otherwise returns what the sector returns
TODO: Change this to use exceptions?
*/
MapBlock * Map::getBlockNoCreate(v3s16 p3d)
{
/*std::cout<<"Map::getBlockNoCreate(("
<<p3d.X<<","<<p3d.Y<<","<<p3d.Z
<<")"<<std::endl;*/
v2s16 p2d(p3d.X, p3d.Z);
//v2s16 sectorpos = getNodeSectorPos(p2d);
MapSector * sectorref = getSectorNoGenerate(p2d);
MapSector *sector = sectorref;
//JMutexAutoLock(sector->mutex);
MapBlock * blockref = sector->getBlockNoCreate(p3d.Y);
return blockref;
}
/*
If sector doesn't exist, generates a sector.
Returns what the sector returns.
*/
MapBlock * Map::getBlock(v3s16 p3d)
{
v2s16 p2d(p3d.X, p3d.Z);
//v2s16 sectorpos = getNodeSectorPos(p2d);
MapSector * sref = getSector(p2d);
/*std::cout<<"Map::GetBlock(): sref->getRefcount()="
<<sref->getRefcount()
<<std::endl;*/
MapSector *sector = sref;
//JMutexAutoLock(sector->mutex);
MapBlock * blockref = sector->getBlock(p3d.Y);
/*std::cout<<"Map::GetBlock(): blockref->getRefcount()="
<<blockref->getRefcount()
<<std::endl;*/
/*std::cout<<"Map::GetBlock(): "
<<"Got block. Now returning."<<std::endl;*/
return blockref;
}
f32 Map::getGroundHeight(v2s16 p, bool generate)
{
/*std::cout<<"Map::getGroundHeight(("
<<p.X<<","<<p.Y<<"))"<<std::endl;*/
try{
v2s16 sectorpos = getNodeSectorPos(p);
MapSector * sref = getSectorNoGenerate(sectorpos);
v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
//sref->mutex.Lock();
f32 y = sref->getGroundHeight(relpos);
//sref->mutex.Unlock();
//std::cout<<"[found]"<<std::endl;
return y;
}
catch(InvalidPositionException &e)
{
//std::cout<<"[not found]"<<std::endl;
return GROUNDHEIGHT_NOTFOUND_SETVALUE;
}
}
void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
{
/*std::cout<<"Map::setGroundHeight(("
<<p.X<<","<<p.Y
<<"), "<<y<<")"<<std::endl;*/
v2s16 sectorpos = getNodeSectorPos(p);
MapSector * sref = getSectorNoGenerate(sectorpos);
v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
//sref->mutex.Lock();
sref->setGroundHeight(relpos, y);
//sref->mutex.Unlock();
}
/*
TODO: Check the order of changing lighting and recursing in
these functions (choose the faster one)
TODO: It has to done like this: cache the neighbours that were
changed; then recurse to them.
*/
/*
Goes recursively through the neighbours of the node.
Alters only transparent nodes.
If the lighting of the neighbour is lower than the lighting of
the node was (before changing it to 0 at the step before), the
lighting of the neighbour is set to 0 and then the same stuff
repeats for the neighbour.
Some things are made strangely to make it as fast as possible.
Usage:
core::list<v3s16> light_sources;
core::map<v3s16, MapBlock*> modified_blocks;
f32 oldlight = node_at_pos.light;
node_at_pos.light = 0;
unLightNeighbors(pos, oldlight, light_sources, modified_blocks);
*/
void Map::unLightNeighbors(v3s16 pos, f32 oldlight,
core::list<v3s16> & light_sources,
core::map<v3s16, MapBlock*> & modified_blocks)
{
v3s16 dirs[6] = {
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
};
// Loop through 6 neighbors
for(u16 i=0; i<6; i++){
// Get the position of the neighbor node
v3s16 n2pos = pos + dirs[i];
// Get the block where the node is located
v3s16 blockpos = getNodeBlockPos(n2pos);
MapBlock *block;
try{
block = getBlockNoCreate(blockpos);
}
catch(InvalidPositionException &e)
{
// If block is inexistent, move to next one.
continue;
}
// Find if block is in cache container
core::map<v3s16, MapBlock * >::Node *cacheblocknode;
cacheblocknode = modified_blocks.find(blockpos);
// If the block is not found in the cache
if(cacheblocknode == NULL)
{
/*
Add block to cache container. It could be a 'set' but
there is no such thing in Irrlicht.
We can use the pointer as the value of a map just fine,
it gets nicely cached that way, too.
*/
modified_blocks.insert(blockpos, block);
}
// Calculate relative position in block
v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
// Get node straight from the block (fast!)
MapNode *n2 = block->getNodePtr(relpos);
/*
If the neighbor is dimmer than what was specified
as oldlight (the light of the previous node)...
*/
if(n2->light < oldlight - 0.001){
if(n2->transparent()){
light_t current_light = n2->light;
n2->light = 0.0;
unLightNeighbors(n2pos, current_light,
light_sources, modified_blocks);
}
}
else{
light_sources.push_back(n2pos);
}
}
}
/*
Goes recursively through the neighbours of the node.
Alters only transparent nodes.
If the lighting of the neighbour is lower than the calculated
lighting coming from the current node to it, the lighting of
the neighbour is set and the same thing repeats.
Some things are made strangely to make it as fast as possible.
Usage:
core::map<v3s16, MapBlock*> modified_blocks;
lightNeighbors(pos, node_at_pos.light, modified_blocks);
*/
/*void Map::lightNeighbors(v3s16 pos, f32 oldlight,
core::map<v3s16, MapBlock*> & modified_blocks)*/
void Map::lightNeighbors(v3s16 pos,
core::map<v3s16, MapBlock*> & modified_blocks)
{
v3s16 dirs[6] = {
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
};
core::list<v3s16> neighbour_cache;
/*
Initialize block cache
(using center node as a starting position)
*/
v3s16 blockpos_last = getNodeBlockPos(pos);
MapBlock *block = NULL;
try{
block = getBlockNoCreate(blockpos_last);
}
catch(InvalidPositionException &e)
{
return;
}
// Calculate relative position in block
v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
// Get node straight from the block (fast!)
MapNode *n = block->getNodePtr(relpos);
f32 oldlight = n->light;
f32 newlight = oldlight * LIGHT_DIMINISH_FACTOR;
// Loop through 6 neighbors
for(u16 i=0; i<6; i++){
// Get the position of the neighbor node
v3s16 n2pos = pos + dirs[i];
// Get the block where the node is located
v3s16 blockpos = getNodeBlockPos(n2pos);
try
{
/*
Only fetch a new block if the block position has changed
*/
if(blockpos != blockpos_last){
block = getBlockNoCreate(blockpos);
}
blockpos_last = blockpos;
// Find if block is in cache container
core::map<v3s16, MapBlock * >::Node *cacheblocknode;
cacheblocknode = modified_blocks.find(blockpos);
// If the block is not found in the cache
if(cacheblocknode == NULL)
{
/*
Add block to cache container. It could be a 'set' but
there is no such thing in Irrlicht.
We can use the pointer as the value of a map just fine,
it gets nicely cached that way, too.
*/
modified_blocks.insert(blockpos, block);
}
// Calculate relative position in block
v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
// Get node straight from the block (fast!)
MapNode *n2 = block->getNodePtr(relpos);
/*
If the neighbor is dimmer than what was specified
as oldlight (the light of the previous node)...
*/
if(n2->light_source() > oldlight / LIGHT_DIMINISH_FACTOR + 0.01)
{
n2->light = n2->light_source();
neighbour_cache.push_back(n2pos);
}
if(n2->light < newlight - 0.001)
{
if(n2->transparent())
{
n2->light = newlight;
// Cache and recurse at last step for maximum speed
neighbour_cache.push_back(n2pos);
}
}
}
catch(InvalidPositionException &e)
{
continue;
}
}
core::list<v3s16>::Iterator j = neighbour_cache.begin();
for(; j != neighbour_cache.end(); j++){
lightNeighbors(*j, modified_blocks);
}
}
v3s16 Map::getBrightestNeighbour(v3s16 p)
{
v3s16 dirs[6] = {
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
};
f32 brightest_light = -1.0;
v3s16 brightest_pos(0,0,0);
// Loop through 6 neighbors
for(u16 i=0; i<6; i++){
// Get the position of the neighbor node
v3s16 n2pos = p + dirs[i];
MapNode n2;
try{
n2 = getNode(n2pos);
}
catch(InvalidPositionException &e)
{
continue;
}
if(n2.light > brightest_light){
brightest_light = n2.light;
brightest_pos = n2pos;
}
}
if(brightest_light < -0.001)
throw;
return brightest_pos;
}
/*
Propagates sunlight down from a node.
Starting point gets sunlight.
Returns the lowest y value of where the sunlight went.
*/
s16 Map::propagateSunlight(v3s16 start,
core::map<v3s16, MapBlock*> & modified_blocks)
{
s16 y = start.Y;
for(; ; y--)
{
v3s16 pos(start.X, y, start.Z);
v3s16 blockpos = getNodeBlockPos(pos);
MapBlock *block;
try{
block = getBlockNoCreate(blockpos);
}
catch(InvalidPositionException &e)
{
break;
}
v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
MapNode *n = block->getNodePtr(relpos);
if(n->transparent())
{
n->light = 1.0;
modified_blocks.insert(blockpos, block);
}
else{
break;
}
}
return y + 1;
}
void Map::updateLighting(core::list< MapBlock*> & a_blocks,
core::map<v3s16, MapBlock*> & modified_blocks)
{
std::cout<<"Map::updateLighting(): "
<<a_blocks.getSize()<<" blocks"<<std::endl;
/*status.setTodo(a_blocks.getSize());
status.setDone(0);
status.setText(L"Updating lighting");*/
std::cout<<"Flooding direct sunlight"<<std::endl;
// Copy list
core::list< MapBlock * > temp_blocks = a_blocks;
/*
Go from the highest blocks to the lowest, propagating sunlight
through them.
*/
while(temp_blocks.empty() == false){
// Get block with highest position in Y
core::list< MapBlock * >::Iterator highest_i = temp_blocks.end();
v3s16 highest_pos(0,-32768,0);
core::list< MapBlock * >::Iterator i;
for(i = temp_blocks.begin(); i != temp_blocks.end(); i++)
{
MapBlock *block = *i;
v3s16 pos = block->getPosRelative();
if(highest_i == temp_blocks.end() || pos.Y > highest_pos.Y){
highest_i = i;
highest_pos = pos;
}
}
// Update sunlight in block
MapBlock *block = *highest_i;
block->propagateSunlight();
/*std::cout<<"block ("<<block->getPosRelative().X<<","
<<block->getPosRelative().Y<<","
<<block->getPosRelative().Z<<") ";
std::cout<<"touches_bottom="<<touches_bottom<<std::endl;*/
// Add to modified blocks
v3s16 blockpos = getNodeBlockPos(highest_pos);
modified_blocks.insert(blockpos, *highest_i);
// Remove block from list
temp_blocks.erase(highest_i);
}
// Add other light sources here
//
std::cout<<"Spreading light to remaining air"<<std::endl;
/*
Spread light to air that hasn't got light
TODO: This shouldn't have to be done to every block
Though it doesn't hurt much i guess.
*/
core::list< MapBlock * >::Iterator i = a_blocks.begin();
for(; i != a_blocks.end(); i++)
{
MapBlock *block = *i;
v3s16 pos = block->getPosRelative();
s16 xmin = pos.X;
s16 zmin = pos.Z;
s16 ymin = pos.Y;
s16 xmax = pos.X + MAP_BLOCKSIZE - 1;
s16 zmax = pos.Z + MAP_BLOCKSIZE - 1;
s16 ymax = pos.Y + MAP_BLOCKSIZE - 1;
for(s16 z=zmin; z<=zmax; z++){
for(s16 x=xmin; x<=xmax; x++){
for(s16 y=ymax; y>=ymin; y--){
v3s16 pos(x, y, z);
MapNode n;
try{
n = getNode(pos);
}
catch(InvalidPositionException &e)
{
break;
}
if(n.light > NO_LIGHT_MAX){
v3s16 pos(x,y,z);
lightNeighbors(pos, modified_blocks);
}
}
}
}
std::cout<<"X";
std::cout.flush();
//status.setDone(status.getDone()+1);
}
std::cout<<std::endl;
}
/*
This is called after changing a node from transparent to opaque.
The lighting value of the node should be left as-is after changing
other values. This sets the lighting value to 0.
*/
void Map::nodeAddedUpdate(v3s16 p, f32 lightwas)
{
std::cout<<"Map::nodeAddedUpdate()"<<std::endl;
core::list<v3s16> light_sources;
core::map<v3s16, MapBlock*> modified_blocks;
//MapNode n = getNode(p);
/*
From this node to nodes underneath:
If lighting is sunlight (1.0), unlight neighbours and
set lighting to 0.
Else discontinue.
*/
bool node_under_sunlight = true;
v3s16 toppos = p + v3s16(0,1,0);
/*
If there is a node at top and it doesn't have sunlight,
there has not been any sunlight going down.
Otherwise there probably is.
*/
try{
MapNode topnode = getNode(toppos);
if(topnode.light < 0.999)
node_under_sunlight = false;
}
catch(InvalidPositionException &e)
{
}
// Add the block of the added node to modified_blocks
v3s16 blockpos = getNodeBlockPos(p);
MapBlock * blockref = getBlockNoCreate(blockpos);
MapBlock *block = blockref;
assert(block != NULL);
modified_blocks.insert(blockpos, block);
// Unlight neighbours of node.
// This means setting light of all consequent dimmer nodes
// to 0.
if(isValidPosition(p) == false)
throw;
unLightNeighbors(p, lightwas, light_sources, modified_blocks);
MapNode n = getNode(p);
n.light = 0;
setNode(p, n);
if(node_under_sunlight)
{
s16 y = p.Y - 1;
for(;; y--){
std::cout<<"y="<<y<<std::endl;
v3s16 n2pos(p.X, y, p.Z);
MapNode n2;
try{
n2 = getNode(n2pos);
}
catch(InvalidPositionException &e)
{
break;
}
if(n2.light >= 0.999){
std::cout<<"doing"<<std::endl;
unLightNeighbors(n2pos, n2.light, light_sources, modified_blocks);
n2.light = 0;
setNode(n2pos, n2);
}
else
break;
}
}
core::list<v3s16>::Iterator j = light_sources.begin();
for(; j != light_sources.end(); j++)
{
lightNeighbors(*j, modified_blocks);
}
core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
for(; i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateFastFaces();
}
}
/*
*/
void Map::removeNodeAndUpdate(v3s16 p)
{
std::cout<<"Map::removeNodeAndUpdate()"<<std::endl;
core::map<v3s16, MapBlock*> modified_blocks;
bool node_under_sunlight = true;
v3s16 toppos = p + v3s16(0,1,0);
/*
If there is a node at top and it doesn't have sunlight,
there will be no sunlight going down.
*/
try{
MapNode topnode = getNode(toppos);
if(topnode.light < 0.999)
node_under_sunlight = false;
}
catch(InvalidPositionException &e)
{
}
/*
Unlight neighbors (in case the node is a light source)
*/
core::list<v3s16> light_sources;
unLightNeighbors(p, getNode(p).light,
light_sources, modified_blocks);
/*
Remove the node
*/
MapNode n;
n.d = MATERIAL_AIR;
setNode(p, n);
/*
Recalculate lighting
*/
core::list<v3s16>::Iterator j = light_sources.begin();
for(; j != light_sources.end(); j++)
{
lightNeighbors(*j, modified_blocks);
}
// Add the block of the removed node to modified_blocks
v3s16 blockpos = getNodeBlockPos(p);
MapBlock * blockref = getBlockNoCreate(blockpos);
MapBlock *block = blockref;
assert(block != NULL);
modified_blocks.insert(blockpos, block);
if(node_under_sunlight)
{
s16 ybottom = propagateSunlight(p, modified_blocks);
/*std::cout<<"Node was under sunlight. "
"Propagating sunlight";
std::cout<<" -> ybottom="<<ybottom<<std::endl;*/
s16 y = p.Y;
for(; y >= ybottom; y--)
{
v3s16 p2(p.X, y, p.Z);
/*std::cout<<"lighting neighbors of node ("
<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
<<std::endl;*/
lightNeighbors(p2, modified_blocks);
}
}
else
{
// Set the lighting of this node to 0
try{
MapNode n = getNode(p);
n.light = 0;
setNode(p, n);
}
catch(InvalidPositionException &e)
{
throw;
}
}
// Get the brightest neighbour node and propagate light from it
v3s16 n2p = getBrightestNeighbour(p);
try{
MapNode n2 = getNode(n2p);
lightNeighbors(n2p, modified_blocks);
}
catch(InvalidPositionException &e)
{
}
core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
for(; i.atEnd() == false; i++)
{
i.getNode()->getValue()->updateFastFaces();
}
}
core::aabbox3d<s16> Map::getDisplayedBlockArea()
{
camera_mutex.Lock();
core::aabbox3d<s16> box_nodes(floatToInt(camera_position));
camera_mutex.Unlock();
g_viewing_range_nodes_mutex.Lock();
s16 d = g_viewing_range_nodes;
g_viewing_range_nodes_mutex.Unlock();
box_nodes.MinEdge -= v3s16(d,d,d);
box_nodes.MaxEdge += v3s16(d,d,d);
return core::aabbox3d<s16>(
getNodeBlockPos(box_nodes.MinEdge),
getNodeBlockPos(box_nodes.MaxEdge));
}
void Map::renderMap(video::IVideoDriver* driver,
video::SMaterial *materials)
{
//std::cout<<"Rendering map..."<<std::endl;
/*
Collect all blocks that are in the view range
Should not optimize more here as we want to auto-update
all changed nodes in viewing range at the next step.
*/
core::list< MapBlock * > blocks_displayed;
core::list< MapBlock * >::Iterator bi;
#if 0
/*
Get all blocks
*/
core::map<v2s16, MapSector*>::Iterator si;
si = m_sectors.getIterator();
for(; si.atEnd() == false; si++)
{
MapSector *sector = si.getNode()->getValue();
core::list< MapBlock * > sectorblocks = sector->getBlocks();
core::list< MapBlock * >::Iterator i;
for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++){
blocks_displayed.push_back(*i);
}
}
#else
/*
Get visible blocks
*/
core::aabbox3d<s16> box_blocks = getDisplayedBlockArea();
for(s16 y=box_blocks.MaxEdge.Y; y>=box_blocks.MinEdge.Y; y--){
for(s16 z=box_blocks.MinEdge.Z; z<=box_blocks.MaxEdge.Z; z++){
for(s16 x=box_blocks.MinEdge.X; x<=box_blocks.MaxEdge.X; x++)
{
/*
Add block to list
*/
try{
MapBlock * blockref = getBlockNoCreate(v3s16(x,y,z));
blocks_displayed.push_back(blockref);
}
catch(InvalidPositionException &e)
{
}
}
}
}
#endif
u8 material_in_use;
material_in_use = MATERIAL_AIR;
if(blocks_displayed.getSize() == 0)
return;
/*
Draw all displayed faces
*/
u32 facecount = 0;
for(bi = blocks_displayed.begin(); bi != blocks_displayed.end(); bi++){
MapBlock *block = *bi;
/*
Compare block position to camera position, skip
if not seen on display
*/
#if 1
v3s16 blockpos_nodes = block->getPosRelative();
// Block center position
v3f blockpos(
((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
);
camera_mutex.Lock();
// Block position relative to camera
v3f blockpos_relative = blockpos - camera_position;
// Distance in camera direction (+=front, -=back)
f32 dforward = blockpos_relative.dotProduct(camera_direction);
camera_mutex.Unlock();
// Total distance
f32 d = blockpos_relative.getLength();
// Maximum radius of a block
f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
// If block is (nearly) touching the camera, don't
// bother checking further
if(d > block_max_radius * 1.5)
{
// Cosine of the angle between the camera direction
// and the block direction (camera_direction is an unit vector)
f32 cosangle = dforward / d;
// Compensate for the size of the block
// (as the block has to be shown even if it's a bit off FOV)
// This is an estimate.
cosangle += block_max_radius / dforward;
// If block is not in the field of view, skip it
if(cosangle < cos(FOV_ANGLE/2))
continue;
}
#endif
/*
Draw the faces of the block
*/
block->fastfaces_mutex.Lock();
core::list<FastFace*>::Iterator i =
block->fastfaces->begin();
for(; i != block->fastfaces->end(); i++)
{
FastFace *f = *i;
if(f->material != material_in_use){
driver->setMaterial(materials[f->material]);
material_in_use = f->material;
}
u16 indices[] = {0,1,2,3};
driver->drawVertexPrimitiveList(f->vertices, 4, indices, 1,
video::EVT_STANDARD, scene::EPT_QUADS, video::EIT_16BIT);
facecount++;
}
block->fastfaces_mutex.Unlock();
}
static s32 oldfacecount = 0;
// Cast needed for msvc
if(abs((long)(facecount - oldfacecount)) > 333){
std::cout<<"Rendered "<<facecount<<" faces"<<std::endl;
oldfacecount = facecount;
}
//std::cout<<"done"<<std::endl;
}
/*
Returns true if updated something
*/
bool Map::updateChangedVisibleArea()
{
//status.setDone(false);
/*
TODO: Update closest ones first
Update the lighting and the faces of changed blocks that are
to be displayed.
*/
core::aabbox3d<s16> box_blocks = getDisplayedBlockArea();
core::list< MapBlock * > blocks_changed;
core::list< MapBlock * >::Iterator bi;
for(s16 y=box_blocks.MaxEdge.Y; y>=box_blocks.MinEdge.Y; y--){
for(s16 z=box_blocks.MinEdge.Z; z<=box_blocks.MaxEdge.Z; z++){
for(s16 x=box_blocks.MinEdge.X; x<=box_blocks.MaxEdge.X; x++)
{
// This intentionally creates new blocks on demand
try
{
MapBlock * block = getBlock(v3s16(x,y,z));
if(block->getChangedFlag())
{
if(blocks_changed.empty() == true){
// Print initial message when first is found
std::cout<<"Updating changed MapBlocks at ";
}
v3s16 p = block->getPosRelative();
std::cout<<"("<<p.X<<","<<p.Y<<","<<p.Z<<") ";
std::cout.flush();
blocks_changed.push_back(block);
block->resetChangedFlag();
}
}
catch(InvalidPositionException &e)
{
}
catch(AsyncQueuedException &e)
{
}
}}}
// Quit if nothing has changed
if(blocks_changed.empty()){
//status.setReady(true);
return false;
}
std::cout<<std::endl;
std::cout<<"Map::updateChangedVisibleArea(): "
"there are changed blocks"<<std::endl;
//status.setReady(false);
core::map<v3s16, MapBlock*> modified_blocks;
std::cout<<"Updating lighting"<<std::endl;
updateLighting(blocks_changed, modified_blocks);
/*status.setText(L"Updating face cache");
status.setTodo(modified_blocks.size());
status.setDone(0);*/
std::cout<<"Updating face cache of changed blocks"<<std::endl;
core::map<v3s16, MapBlock * >::Iterator i = modified_blocks.getIterator();
for(; i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
block->updateFastFaces();
std::cout<<"X";
std::cout.flush();
//status.setDone(status.getDone()+1);
}
std::cout<<std::endl;
//status.setReady(true);
return true;
}
MapSector * MasterMap::getSector(v2s16 p2d)
{
// Check that it doesn't exist already
try{
MapSector * sref = getSectorNoGenerate(p2d);
return sref;
}
catch(InvalidPositionException &e)
{
}
/*std::cout<<"Map::getSector(("<<p2d.X<<","<<p2d.Y
<<")): generating"<<std::endl;*/
//std::cout<<"Generating heightmap..."<<std::endl;
f32 corners[4] = {
m_heightmap.getGroundHeight(p2d+v2s16(0,0)),
m_heightmap.getGroundHeight(p2d+v2s16(1,0)),
m_heightmap.getGroundHeight(p2d+v2s16(1,1)),
m_heightmap.getGroundHeight(p2d+v2s16(0,1)),
};
HeightmapBlockGenerator *gen;
gen = new HeightmapBlockGenerator(p2d, &m_hwrapper);
/*DummyHeightmap dummyheightmap;
gen = new HeightmapBlockGenerator(p2d, &dummyheightmap);*/
gen->m_heightmap->generateContinued(2.0, 0.2, corners);
//sector->getHeightmap()->generateContinued(0.0, 0.0, corners);
MapSector *sector = new MapSector(this, p2d, gen);
sector->setHeightmap(gen->m_heightmap);
m_sectors.insert(p2d, sector);
/*std::cout<<"Map::getSector(("<<p2d.X<<","<<p2d.Y
<<")): generated. Now returning."<<std::endl;*/
return sector;
}
ClientMap::ClientMap(
Client *client,
video::SMaterial *materials,
scene::ISceneNode* parent,
scene::ISceneManager* mgr,
s32 id
):
scene::ISceneNode(parent, mgr, id),
m_client(client),
m_materials(materials)
{
/*m_box = core::aabbox3d<f32>(0,0,0,
map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
/*m_box = core::aabbox3d<f32>(0,0,0,
map->getSizeNodes().X * BS,
map->getSizeNodes().Y * BS,
map->getSizeNodes().Z * BS);*/
m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
BS*1000000,BS*1000000,BS*1000000);
}
MapSector * ClientMap::getSector(v2s16 p2d)
{
// Check that it doesn't exist already
try{
MapSector *sector = getSectorNoGenerate(p2d);
return sector;
}
catch(InvalidPositionException &e)
{
}
/*
Create a ClientBlockGenerator to fill the MapSector's
blocks from data fetched by the client from the server.
*/
ClientBlockGenerator *gen;
gen = new ClientBlockGenerator(m_client);
MapSector *sector = new MapSector(this, p2d, gen);
m_sectors.insert(p2d, sector);
return sector;
}