3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "jmutexautolock.h"
31 #define sleep_ms(x) Sleep(x)
34 #define sleep_ms(x) usleep(x*1000)
37 MapBlockPointerCache::MapBlockPointerCache(Map *map)
40 m_map->m_blockcachelock.cacheCreated();
42 m_from_cache_count = 0;
46 MapBlockPointerCache::~MapBlockPointerCache()
48 m_map->m_blockcachelock.cacheRemoved();
50 dstream<<"MapBlockPointerCache:"
51 <<" from_cache_count="<<m_from_cache_count
52 <<" from_map_count="<<m_from_map_count
56 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
58 core::map<v3s16, MapBlock*>::Node *n = NULL;
68 // Throws InvalidPositionException if not found
69 MapBlock *b = m_map->getBlockNoCreate(p);
78 Map::Map(std::ostream &dout):
80 m_camera_position(0,0,0),
81 m_camera_direction(0,0,1),
86 m_sector_mutex.Init();
87 m_camera_mutex.Init();
88 assert(m_sector_mutex.IsInitialized());
89 assert(m_camera_mutex.IsInitialized());
91 // Get this so that the player can stay on it at first
92 //getSector(v2s16(0,0));
100 /*updater.setRun(false);
101 while(updater.IsRunning())
107 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
108 for(; i.atEnd() == false; i++)
110 MapSector *sector = i.getNode()->getValue();
115 /*bool Map::sectorExists(v2s16 p)
117 JMutexAutoLock lock(m_sector_mutex);
118 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
122 MapSector * Map::getSectorNoGenerate(v2s16 p)
124 JMutexAutoLock lock(m_sector_mutex);
126 if(m_sector_cache != NULL && p == m_sector_cache_p){
127 MapSector * sector = m_sector_cache;
128 // Reset inactivity timer
129 sector->usage_timer = 0.0;
133 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
134 // If sector doesn't exist, throw an exception
137 throw InvalidPositionException();
140 MapSector *sector = n->getValue();
142 // Cache the last result
143 m_sector_cache_p = p;
144 m_sector_cache = sector;
146 //MapSector * ref(sector);
148 // Reset inactivity timer
149 sector->usage_timer = 0.0;
153 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
155 v2s16 p2d(p3d.X, p3d.Z);
156 MapSector * sector = getSectorNoGenerate(p2d);
158 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
163 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
165 dstream<<"Map::getBlock() with generate=true called"
167 v2s16 p2d(p3d.X, p3d.Z);
168 //MapSector * sector = getSector(p2d, generate);
169 MapSector * sector = getSectorNoGenerate(p2d);
172 throw InvalidPositionException();
174 return sector->getBlockNoCreate(p3d.Y);
177 f32 Map::getGroundHeight(v2s16 p, bool generate)
180 v2s16 sectorpos = getNodeSectorPos(p);
181 MapSector * sref = getSectorNoGenerate(sectorpos);
182 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
183 f32 y = sref->getGroundHeight(relpos);
186 catch(InvalidPositionException &e)
188 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
192 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
194 /*m_dout<<DTIME<<"Map::setGroundHeight(("
196 <<"), "<<y<<")"<<std::endl;*/
197 v2s16 sectorpos = getNodeSectorPos(p);
198 MapSector * sref = getSectorNoGenerate(sectorpos);
199 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
200 //sref->mutex.Lock();
201 sref->setGroundHeight(relpos, y);
202 //sref->mutex.Unlock();
205 bool Map::isNodeUnderground(v3s16 p)
207 v3s16 blockpos = getNodeBlockPos(p);
209 MapBlock * block = getBlockNoCreate(blockpos);
210 return block->getIsUnderground();
212 catch(InvalidPositionException &e)
219 void Map::interpolate(v3s16 block,
220 core::map<v3s16, MapBlock*> & modified_blocks)
222 const v3s16 dirs[6] = {
223 v3s16(0,0,1), // back
225 v3s16(1,0,0), // right
226 v3s16(0,0,-1), // front
227 v3s16(0,-1,0), // bottom
228 v3s16(-1,0,0), // left
231 if(from_nodes.size() == 0)
234 u32 blockchangecount = 0;
236 core::map<v3s16, bool> lighted_nodes;
237 core::map<v3s16, bool>::Iterator j;
238 j = from_nodes.getIterator();
241 Initialize block cache
244 MapBlock *block = NULL;
245 // Cache this a bit, too
246 bool block_checked_in_modified = false;
248 for(; j.atEnd() == false; j++)
249 //for(; j != from_nodes.end(); j++)
251 v3s16 pos = j.getNode()->getKey();
253 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
254 v3s16 blockpos = getNodeBlockPos(pos);
256 // Only fetch a new block if the block position has changed
258 if(block == NULL || blockpos != blockpos_last){
259 block = getBlockNoCreate(blockpos);
260 blockpos_last = blockpos;
262 block_checked_in_modified = false;
266 catch(InvalidPositionException &e)
274 // Calculate relative position in block
275 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
277 // Get node straight from the block
278 MapNode n = block->getNode(relpos);
280 u8 oldlight = n.getLight();
281 u8 newlight = diminish_light(oldlight);
283 // Loop through 6 neighbors
284 for(u16 i=0; i<6; i++){
285 // Get the position of the neighbor node
286 v3s16 n2pos = pos + dirs[i];
288 // Get the block where the node is located
289 v3s16 blockpos = getNodeBlockPos(n2pos);
293 // Only fetch a new block if the block position has changed
295 if(block == NULL || blockpos != blockpos_last){
296 block = getBlockNoCreate(blockpos);
297 blockpos_last = blockpos;
299 block_checked_in_modified = false;
303 catch(InvalidPositionException &e)
308 // Calculate relative position in block
309 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
310 // Get node straight from the block
311 MapNode n2 = block->getNode(relpos);
313 bool changed = false;
315 If the neighbor is brighter than the current node,
316 add to list (it will light up this node on its turn)
318 if(n2.getLight() > undiminish_light(oldlight))
320 lighted_nodes.insert(n2pos, true);
321 //lighted_nodes.push_back(n2pos);
325 If the neighbor is dimmer than how much light this node
326 would spread on it, add to list
328 if(n2.getLight() < newlight)
330 if(n2.light_propagates())
332 n2.setLight(newlight);
333 block->setNode(relpos, n2);
334 lighted_nodes.insert(n2pos, true);
335 //lighted_nodes.push_back(n2pos);
340 // Add to modified_blocks
341 if(changed == true && block_checked_in_modified == false)
343 // If the block is not found in modified_blocks, add.
344 if(modified_blocks.find(blockpos) == NULL)
346 modified_blocks.insert(blockpos, block);
348 block_checked_in_modified = true;
351 catch(InvalidPositionException &e)
358 /*dstream<<"spreadLight(): Changed block "
359 <<blockchangecount<<" times"
360 <<" for "<<from_nodes.size()<<" nodes"
363 if(lighted_nodes.size() > 0)
364 spreadLight(lighted_nodes, modified_blocks);
369 Goes recursively through the neighbours of the node.
371 Alters only transparent nodes.
373 If the lighting of the neighbour is lower than the lighting of
374 the node was (before changing it to 0 at the step before), the
375 lighting of the neighbour is set to 0 and then the same stuff
376 repeats for the neighbour.
378 The ending nodes of the routine are stored in light_sources.
379 This is useful when a light is removed. In such case, this
380 routine can be called for the light node and then again for
381 light_sources to re-light the area without the removed light.
383 values of from_nodes are lighting values.
385 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
386 core::map<v3s16, bool> & light_sources,
387 core::map<v3s16, MapBlock*> & modified_blocks)
390 v3s16(0,0,1), // back
392 v3s16(1,0,0), // right
393 v3s16(0,0,-1), // front
394 v3s16(0,-1,0), // bottom
395 v3s16(-1,0,0), // left
398 if(from_nodes.size() == 0)
401 u32 blockchangecount = 0;
403 core::map<v3s16, u8> unlighted_nodes;
404 core::map<v3s16, u8>::Iterator j;
405 j = from_nodes.getIterator();
408 Initialize block cache
411 MapBlock *block = NULL;
412 // Cache this a bit, too
413 bool block_checked_in_modified = false;
415 for(; j.atEnd() == false; j++)
417 v3s16 pos = j.getNode()->getKey();
418 v3s16 blockpos = getNodeBlockPos(pos);
420 // Only fetch a new block if the block position has changed
422 if(block == NULL || blockpos != blockpos_last){
423 block = getBlockNoCreate(blockpos);
424 blockpos_last = blockpos;
426 block_checked_in_modified = false;
430 catch(InvalidPositionException &e)
438 // Calculate relative position in block
439 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
441 // Get node straight from the block
442 MapNode n = block->getNode(relpos);
444 u8 oldlight = j.getNode()->getValue();
446 // Loop through 6 neighbors
447 for(u16 i=0; i<6; i++)
449 // Get the position of the neighbor node
450 v3s16 n2pos = pos + dirs[i];
452 // Get the block where the node is located
453 v3s16 blockpos = getNodeBlockPos(n2pos);
457 // Only fetch a new block if the block position has changed
459 if(block == NULL || blockpos != blockpos_last){
460 block = getBlockNoCreate(blockpos);
461 blockpos_last = blockpos;
463 block_checked_in_modified = false;
467 catch(InvalidPositionException &e)
472 // Calculate relative position in block
473 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
474 // Get node straight from the block
475 MapNode n2 = block->getNode(relpos);
477 bool changed = false;
479 //TODO: Optimize output by optimizing light_sources?
482 If the neighbor is dimmer than what was specified
483 as oldlight (the light of the previous node)
485 if(n2.getLight() < oldlight)
488 And the neighbor is transparent and it has some light
490 if(n2.light_propagates() && n2.getLight() != 0)
493 Set light to 0 and add to queue
496 u8 current_light = n2.getLight();
498 block->setNode(relpos, n2);
500 unlighted_nodes.insert(n2pos, current_light);
504 Remove from light_sources if it is there
505 NOTE: This doesn't happen nearly at all
507 /*if(light_sources.find(n2pos))
509 std::cout<<"Removed from light_sources"<<std::endl;
510 light_sources.remove(n2pos);
515 light_sources.insert(n2pos, true);
518 // Add to modified_blocks
519 if(changed == true && block_checked_in_modified == false)
521 // If the block is not found in modified_blocks, add.
522 if(modified_blocks.find(blockpos) == NULL)
524 modified_blocks.insert(blockpos, block);
526 block_checked_in_modified = true;
529 catch(InvalidPositionException &e)
536 /*dstream<<"unspreadLight(): Changed block "
537 <<blockchangecount<<" times"
538 <<" for "<<from_nodes.size()<<" nodes"
541 if(unlighted_nodes.size() > 0)
542 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
546 A single-node wrapper of the above
548 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
549 core::map<v3s16, bool> & light_sources,
550 core::map<v3s16, MapBlock*> & modified_blocks)
552 core::map<v3s16, u8> from_nodes;
553 from_nodes.insert(pos, lightwas);
555 unspreadLight(from_nodes, light_sources, modified_blocks);
559 Lights neighbors of from_nodes, collects all them and then
562 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
563 core::map<v3s16, MapBlock*> & modified_blocks)
565 const v3s16 dirs[6] = {
566 v3s16(0,0,1), // back
568 v3s16(1,0,0), // right
569 v3s16(0,0,-1), // front
570 v3s16(0,-1,0), // bottom
571 v3s16(-1,0,0), // left
574 if(from_nodes.size() == 0)
577 u32 blockchangecount = 0;
579 core::map<v3s16, bool> lighted_nodes;
580 core::map<v3s16, bool>::Iterator j;
581 j = from_nodes.getIterator();
584 Initialize block cache
587 MapBlock *block = NULL;
588 // Cache this a bit, too
589 bool block_checked_in_modified = false;
591 for(; j.atEnd() == false; j++)
592 //for(; j != from_nodes.end(); j++)
594 v3s16 pos = j.getNode()->getKey();
596 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
597 v3s16 blockpos = getNodeBlockPos(pos);
599 // Only fetch a new block if the block position has changed
601 if(block == NULL || blockpos != blockpos_last){
602 block = getBlockNoCreate(blockpos);
603 blockpos_last = blockpos;
605 block_checked_in_modified = false;
609 catch(InvalidPositionException &e)
617 // Calculate relative position in block
618 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
620 // Get node straight from the block
621 MapNode n = block->getNode(relpos);
623 u8 oldlight = n.getLight();
624 u8 newlight = diminish_light(oldlight);
626 // Loop through 6 neighbors
627 for(u16 i=0; i<6; i++){
628 // Get the position of the neighbor node
629 v3s16 n2pos = pos + dirs[i];
631 // Get the block where the node is located
632 v3s16 blockpos = getNodeBlockPos(n2pos);
636 // Only fetch a new block if the block position has changed
638 if(block == NULL || blockpos != blockpos_last){
639 block = getBlockNoCreate(blockpos);
640 blockpos_last = blockpos;
642 block_checked_in_modified = false;
646 catch(InvalidPositionException &e)
651 // Calculate relative position in block
652 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
653 // Get node straight from the block
654 MapNode n2 = block->getNode(relpos);
656 bool changed = false;
658 If the neighbor is brighter than the current node,
659 add to list (it will light up this node on its turn)
661 if(n2.getLight() > undiminish_light(oldlight))
663 lighted_nodes.insert(n2pos, true);
664 //lighted_nodes.push_back(n2pos);
668 If the neighbor is dimmer than how much light this node
669 would spread on it, add to list
671 if(n2.getLight() < newlight)
673 if(n2.light_propagates())
675 n2.setLight(newlight);
676 block->setNode(relpos, n2);
677 lighted_nodes.insert(n2pos, true);
678 //lighted_nodes.push_back(n2pos);
683 // Add to modified_blocks
684 if(changed == true && block_checked_in_modified == false)
686 // If the block is not found in modified_blocks, add.
687 if(modified_blocks.find(blockpos) == NULL)
689 modified_blocks.insert(blockpos, block);
691 block_checked_in_modified = true;
694 catch(InvalidPositionException &e)
701 /*dstream<<"spreadLight(): Changed block "
702 <<blockchangecount<<" times"
703 <<" for "<<from_nodes.size()<<" nodes"
706 if(lighted_nodes.size() > 0)
707 spreadLight(lighted_nodes, modified_blocks);
711 A single-node source variation of the above.
713 void Map::lightNeighbors(v3s16 pos,
714 core::map<v3s16, MapBlock*> & modified_blocks)
716 core::map<v3s16, bool> from_nodes;
717 from_nodes.insert(pos, true);
718 spreadLight(from_nodes, modified_blocks);
721 v3s16 Map::getBrightestNeighbour(v3s16 p)
724 v3s16(0,0,1), // back
726 v3s16(1,0,0), // right
727 v3s16(0,0,-1), // front
728 v3s16(0,-1,0), // bottom
729 v3s16(-1,0,0), // left
732 u8 brightest_light = 0;
733 v3s16 brightest_pos(0,0,0);
734 bool found_something = false;
736 // Loop through 6 neighbors
737 for(u16 i=0; i<6; i++){
738 // Get the position of the neighbor node
739 v3s16 n2pos = p + dirs[i];
744 catch(InvalidPositionException &e)
748 if(n2.getLight() > brightest_light || found_something == false){
749 brightest_light = n2.getLight();
750 brightest_pos = n2pos;
751 found_something = true;
755 if(found_something == false)
756 throw InvalidPositionException();
758 return brightest_pos;
762 Propagates sunlight down from a node.
763 Starting point gets sunlight.
765 Returns the lowest y value of where the sunlight went.
767 s16 Map::propagateSunlight(v3s16 start,
768 core::map<v3s16, MapBlock*> & modified_blocks)
773 v3s16 pos(start.X, y, start.Z);
775 v3s16 blockpos = getNodeBlockPos(pos);
778 block = getBlockNoCreate(blockpos);
780 catch(InvalidPositionException &e)
785 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
786 MapNode n = block->getNode(relpos);
788 if(n.sunlight_propagates())
790 n.setLight(LIGHT_SUN);
791 block->setNode(relpos, n);
793 modified_blocks.insert(blockpos, block);
802 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
803 core::map<v3s16, MapBlock*> & modified_blocks)
805 /*m_dout<<DTIME<<"Map::updateLighting(): "
806 <<a_blocks.getSize()<<" blocks... ";*/
810 u32 count_was = modified_blocks.size();
812 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
813 for(; i != a_blocks.end(); i++)
815 MapBlock *block = *i;*/
817 core::map<v3s16, bool> light_sources;
819 core::map<v3s16, u8> unlight_from;
821 core::map<v3s16, MapBlock*>::Iterator i;
822 i = a_blocks.getIterator();
823 for(; i.atEnd() == false; i++)
825 MapBlock *block = i.getNode()->getValue();
829 // Don't bother with dummy blocks.
833 v3s16 pos = block->getPos();
834 modified_blocks.insert(pos, block);
837 Clear all light from block
839 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
840 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
841 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
846 MapNode n = block->getNode(v3s16(x,y,z));
847 u8 oldlight = n.getLight();
849 block->setNode(v3s16(x,y,z), n);
851 // Collect borders for unlighting
852 if(x==0 || x == MAP_BLOCKSIZE-1
853 || y==0 || y == MAP_BLOCKSIZE-1
854 || z==0 || z == MAP_BLOCKSIZE-1)
856 v3s16 p_map = p + v3s16(
859 MAP_BLOCKSIZE*pos.Z);
860 unlight_from.insert(p_map, oldlight);
863 catch(InvalidPositionException &e)
866 This would happen when dealing with a
870 dstream<<"updateLighting(): InvalidPositionException"
875 bool bottom_valid = block->propagateSunlight(light_sources);
877 // If bottom is valid, we're done.
881 /*dstream<<"Bottom for sunlight-propagated block ("
882 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
885 // Else get the block below and loop to it
889 block = getBlockNoCreate(pos);
891 catch(InvalidPositionException &e)
900 //TimeTaker timer("unspreadLight", g_device);
901 unspreadLight(unlight_from, light_sources, modified_blocks);
906 u32 diff = modified_blocks.size() - count_was;
907 count_was = modified_blocks.size();
908 dstream<<"unspreadLight modified "<<diff<<std::endl;
911 // TODO: Spread light from propagated sunlight?
912 // Yes, add it to light_sources... somehow.
913 // It has to be added at somewhere above, in the loop.
917 //TimeTaker timer("spreadLight", g_device);
918 spreadLight(light_sources, modified_blocks);
923 u32 diff = modified_blocks.size() - count_was;
924 count_was = modified_blocks.size();
925 dstream<<"spreadLight modified "<<diff<<std::endl;
928 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
932 This is called after changing a node from transparent to opaque.
933 The lighting value of the node should be left as-is after changing
934 other values. This sets the lighting value to 0.
936 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
937 core::map<v3s16, MapBlock*> &modified_blocks)*/
938 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
939 core::map<v3s16, MapBlock*> &modified_blocks)
942 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
943 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
945 u8 lightwas = getNode(p).getLight();
947 //core::list<v3s16> light_sources;
948 core::map<v3s16, bool> light_sources;
949 //MapNode n = getNode(p);
952 From this node to nodes underneath:
953 If lighting is sunlight (1.0), unlight neighbours and
958 bool node_under_sunlight = true;
960 v3s16 toppos = p + v3s16(0,1,0);
963 If there is a node at top and it doesn't have sunlight,
964 there has not been any sunlight going down.
966 Otherwise there probably is.
969 MapNode topnode = getNode(toppos);
971 if(topnode.getLight() != LIGHT_SUN)
972 node_under_sunlight = false;
974 catch(InvalidPositionException &e)
978 // Add the block of the added node to modified_blocks
979 v3s16 blockpos = getNodeBlockPos(p);
980 MapBlock * block = getBlockNoCreate(blockpos);
981 assert(block != NULL);
982 modified_blocks.insert(blockpos, block);
984 if(isValidPosition(p) == false)
987 // Unlight neighbours of node.
988 // This means setting light of all consequent dimmer nodes
990 // This also collects the nodes at the border which will spread
991 // light again into this.
992 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
998 If node is under sunlight, take all sunlighted nodes under
999 it and clear light from them and from where the light has
1002 if(node_under_sunlight)
1006 //m_dout<<DTIME<<"y="<<y<<std::endl;
1007 v3s16 n2pos(p.X, y, p.Z);
1011 n2 = getNode(n2pos);
1013 catch(InvalidPositionException &e)
1018 if(n2.getLight() == LIGHT_SUN)
1020 //m_dout<<DTIME<<"doing"<<std::endl;
1021 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
1031 Spread light from all nodes that might be capable of doing so
1032 TODO: Convert to spreadLight
1034 spreadLight(light_sources, modified_blocks);
1039 void Map::removeNodeAndUpdate(v3s16 p,
1040 core::map<v3s16, MapBlock*> &modified_blocks)
1042 /*PrintInfo(m_dout);
1043 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1044 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1046 bool node_under_sunlight = true;
1048 v3s16 toppos = p + v3s16(0,1,0);
1050 // Node will be replaced with this
1051 u8 replace_material = MATERIAL_AIR;
1055 Find out with what material the node will be replaced.
1056 It will be replaced with the mostly seen buildable_to.
1060 v3s16(0,0,1), // back
1061 v3s16(0,1,0), // top
1062 v3s16(1,0,0), // right
1063 v3s16(0,0,-1), // front
1064 v3s16(0,-1,0), // bottom
1065 v3s16(-1,0,0), // left
1068 core::map<u8, u16> neighbor_rankings;
1070 for(u32 i=0; i<sizeof(dirs)/sizeof(dirs[0]); i++)
1073 MapNode n2 = getNode(p + dirs[i]);
1075 if(material_buildable_to(n2.d))
1077 if(neighbor_rankings.find(n2.d) == NULL)
1078 neighbor_rankings[n2.d] = 1;
1080 neighbor_rankings[n2.d]
1081 = neighbor_rankings[n2.d] + 1;
1084 catch(InvalidPositionException &e)
1089 u16 highest_ranking = 0;
1091 for(core::map<u8, u16>::Iterator
1092 i = neighbor_rankings.getIterator();
1093 i.atEnd() == false; i++)
1095 u8 m = i.getNode()->getKey();
1096 u8 c = i.getNode()->getValue();
1098 c > highest_ranking ||
1099 // Prefer something else than air
1100 (c >= highest_ranking && m != MATERIAL_AIR)
1104 replace_material = m;
1105 highest_ranking = c;
1111 If there is a node at top and it doesn't have sunlight,
1112 there will be no sunlight going down.
1115 MapNode topnode = getNode(toppos);
1117 if(topnode.getLight() != LIGHT_SUN)
1118 node_under_sunlight = false;
1120 catch(InvalidPositionException &e)
1125 Unlight neighbors (in case the node is a light source)
1127 //core::list<v3s16> light_sources;
1128 core::map<v3s16, bool> light_sources;
1129 unLightNeighbors(p, getNode(p).getLight(),
1130 light_sources, modified_blocks);
1136 n.d = replace_material;
1141 Recalculate lighting
1143 spreadLight(light_sources, modified_blocks);
1145 // Add the block of the removed node to modified_blocks
1146 v3s16 blockpos = getNodeBlockPos(p);
1147 MapBlock * block = getBlockNoCreate(blockpos);
1148 assert(block != NULL);
1149 modified_blocks.insert(blockpos, block);
1152 If the removed node was under sunlight, propagate the
1153 sunlight down from it and then light all neighbors
1154 of the propagated blocks.
1156 if(node_under_sunlight)
1158 s16 ybottom = propagateSunlight(p, modified_blocks);
1159 /*m_dout<<DTIME<<"Node was under sunlight. "
1160 "Propagating sunlight";
1161 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1163 for(; y >= ybottom; y--)
1165 v3s16 p2(p.X, y, p.Z);
1166 /*m_dout<<DTIME<<"lighting neighbors of node ("
1167 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1169 lightNeighbors(p2, modified_blocks);
1174 // Set the lighting of this node to 0
1176 MapNode n = getNode(p);
1180 catch(InvalidPositionException &e)
1186 // Get the brightest neighbour node and propagate light from it
1187 v3s16 n2p = getBrightestNeighbour(p);
1189 MapNode n2 = getNode(n2p);
1190 lightNeighbors(n2p, modified_blocks);
1192 catch(InvalidPositionException &e)
1197 void Map::updateMeshes(v3s16 blockpos)
1199 assert(mapType() == MAPTYPE_CLIENT);
1202 v3s16 p = blockpos + v3s16(0,0,0);
1203 MapBlock *b = getBlockNoCreate(p);
1206 catch(InvalidPositionException &e){}
1208 v3s16 p = blockpos + v3s16(-1,0,0);
1209 MapBlock *b = getBlockNoCreate(p);
1212 catch(InvalidPositionException &e){}
1214 v3s16 p = blockpos + v3s16(0,-1,0);
1215 MapBlock *b = getBlockNoCreate(p);
1218 catch(InvalidPositionException &e){}
1220 v3s16 p = blockpos + v3s16(0,0,-1);
1221 MapBlock *b = getBlockNoCreate(p);
1224 catch(InvalidPositionException &e){}
1228 Updates usage timers
1230 void Map::timerUpdate(float dtime)
1232 JMutexAutoLock lock(m_sector_mutex);
1234 core::map<v2s16, MapSector*>::Iterator si;
1236 si = m_sectors.getIterator();
1237 for(; si.atEnd() == false; si++)
1239 MapSector *sector = si.getNode()->getValue();
1240 sector->usage_timer += dtime;
1244 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1247 Wait for caches to be removed before continuing.
1249 This disables the existence of caches while locked
1251 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1253 core::list<v2s16>::Iterator j;
1254 for(j=list.begin(); j!=list.end(); j++)
1256 MapSector *sector = m_sectors[*j];
1259 sector->deleteBlocks();
1264 If sector is in sector cache, remove it from there
1266 if(m_sector_cache == sector)
1268 m_sector_cache = NULL;
1271 Remove from map and delete
1273 m_sectors.remove(*j);
1279 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1280 core::list<v3s16> *deleted_blocks)
1282 JMutexAutoLock lock(m_sector_mutex);
1284 core::list<v2s16> sector_deletion_queue;
1285 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1286 for(; i.atEnd() == false; i++)
1288 MapSector *sector = i.getNode()->getValue();
1290 Delete sector from memory if it hasn't been used in a long time
1292 if(sector->usage_timer > timeout)
1294 sector_deletion_queue.push_back(i.getNode()->getKey());
1296 if(deleted_blocks != NULL)
1298 // Collect positions of blocks of sector
1299 MapSector *sector = i.getNode()->getValue();
1300 core::list<MapBlock*> blocks;
1301 sector->getBlocks(blocks);
1302 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1303 i != blocks.end(); i++)
1305 deleted_blocks->push_back((*i)->getPos());
1310 deleteSectors(sector_deletion_queue, only_blocks);
1311 return sector_deletion_queue.getSize();
1314 void Map::PrintInfo(std::ostream &out)
1323 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1327 m_savedir = savedir;
1328 m_map_saving_enabled = false;
1332 // If directory exists, check contents and load if possible
1333 if(fs::PathExists(m_savedir))
1335 // If directory is empty, it is safe to save into it.
1336 if(fs::GetDirListing(m_savedir).size() == 0)
1338 dstream<<DTIME<<"Server: Empty save directory is valid."
1340 m_map_saving_enabled = true;
1344 // Load master heightmap
1345 loadMasterHeightmap();
1347 // Load sector (0,0) and throw and exception on fail
1348 if(loadSectorFull(v2s16(0,0)) == false)
1349 throw LoadError("Failed to load sector (0,0)");
1351 dstream<<DTIME<<"Server: Successfully loaded master "
1352 "heightmap and sector (0,0) from "<<savedir<<
1353 ", assuming valid save directory."
1356 m_map_saving_enabled = true;
1357 // Map loaded, not creating new one
1361 // If directory doesn't exist, it is safe to save to it
1363 m_map_saving_enabled = true;
1366 catch(std::exception &e)
1368 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1369 <<", exception: "<<e.what()<<std::endl;
1370 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1371 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1374 dstream<<DTIME<<"Initializing new map."<<std::endl;
1376 // Create master heightmap
1377 ValueGenerator *maxgen =
1378 ValueGenerator::deSerialize(hmp.randmax);
1379 ValueGenerator *factorgen =
1380 ValueGenerator::deSerialize(hmp.randfactor);
1381 ValueGenerator *basegen =
1382 ValueGenerator::deSerialize(hmp.base);
1383 m_heightmap = new UnlimitedHeightmap
1384 (hmp.blocksize, maxgen, factorgen, basegen);
1386 // Set map parameters
1389 // Create zero sector
1390 emergeSector(v2s16(0,0));
1392 // Initially write whole map
1396 ServerMap::~ServerMap()
1400 if(m_map_saving_enabled)
1403 // Save only changed parts
1405 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1409 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1412 catch(std::exception &e)
1414 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1415 <<", exception: "<<e.what()<<std::endl;
1418 if(m_heightmap != NULL)
1422 MapSector * ServerMap::emergeSector(v2s16 p2d)
1424 DSTACK("%s: p2d=(%d,%d)",
1427 // Check that it doesn't exist already
1429 return getSectorNoGenerate(p2d);
1431 catch(InvalidPositionException &e)
1436 Try to load the sector from disk.
1438 if(loadSectorFull(p2d) == true)
1440 return getSectorNoGenerate(p2d);
1444 If there is no master heightmap, throw.
1446 if(m_heightmap == NULL)
1448 throw InvalidPositionException("emergeSector(): no heightmap");
1452 Do not generate over-limit
1454 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1455 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1456 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1457 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1458 throw InvalidPositionException("emergeSector(): pos. over limit");
1461 Generate sector and heightmaps
1464 // Number of heightmaps in sector in each direction
1465 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1467 // Heightmap side width
1468 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1470 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1472 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1473 " heightmaps and objects"<<std::endl;*/
1475 // Loop through sub-heightmaps
1476 for(s16 y=0; y<hm_split; y++)
1477 for(s16 x=0; x<hm_split; x++)
1479 v2s16 p_in_sector = v2s16(x,y);
1480 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1482 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1483 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1484 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1485 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1488 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1489 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1492 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1494 sector->setHeightmap(p_in_sector, hm);
1496 //TODO: Make these values configurable
1497 hm->generateContinued(1.0, 0.2, corners);
1498 //hm->generateContinued(2.0, 0.2, corners);
1508 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1509 sector->setObjects(objects);
1511 v2s16 mhm_p = p2d * hm_split;
1513 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1514 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1515 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1516 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1519 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1520 float avgslope = 0.0;
1521 avgslope += fabs(avgheight - corners[0]);
1522 avgslope += fabs(avgheight - corners[1]);
1523 avgslope += fabs(avgheight - corners[2]);
1524 avgslope += fabs(avgheight - corners[3]);
1526 avgslope /= MAP_BLOCKSIZE;
1527 //dstream<<"avgslope="<<avgslope<<std::endl;
1529 float pitness = 0.0;
1531 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1534 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1537 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1540 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1544 pitness /= MAP_BLOCKSIZE;
1545 //dstream<<"pitness="<<pitness<<std::endl;
1548 Plant some trees if there is not much slope
1551 // Avgslope is the derivative of a hill
1552 float t = avgslope * avgslope;
1553 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1556 tree_max = a / (t/0.03);
1559 u32 count = (rand()%(tree_max+1));
1560 //u32 count = tree_max;
1561 for(u32 i=0; i<count; i++)
1563 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1564 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1565 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1568 objects->insert(v3s16(x, y, z),
1569 SECTOR_OBJECT_TREE_1);
1573 Plant some bushes if sector is pit-like
1576 // Pitness usually goes at around -0.5...0.5
1578 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1580 bush_max = (pitness*a*4);
1583 u32 count = (rand()%(bush_max+1));
1584 for(u32 i=0; i<count; i++)
1586 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1587 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1588 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1591 objects->insert(v3s16(x, y, z),
1592 SECTOR_OBJECT_BUSH_1);
1596 Add ravine (randomly)
1598 if(m_params.ravines_amount != 0)
1600 if(rand()%(s32)(20.0 / m_params.ravines_amount) == 0)
1603 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1604 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1607 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1608 objects->insert(v3s16(x, y, z),
1609 SECTOR_OBJECT_RAVINE);
1616 JMutexAutoLock lock(m_sector_mutex);
1617 m_sectors.insert(p2d, sector);
1622 MapBlock * ServerMap::emergeBlock(
1624 bool only_from_disk,
1625 core::map<v3s16, MapBlock*> &changed_blocks,
1626 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1629 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1631 p.X, p.Y, p.Z, only_from_disk);
1633 /*dstream<<"ServerMap::emergeBlock(): "
1634 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1635 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1636 v2s16 p2d(p.X, p.Z);
1639 This will create or load a sector if not found in memory.
1640 If block exists on disk, it will be loaded.
1642 NOTE: On old save formats, this will be slow, as it generates
1643 lighting on blocks for them.
1645 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1646 assert(sector->getId() == MAPSECTOR_SERVER);
1648 // Try to get a block from the sector
1649 MapBlock *block = NULL;
1650 bool not_on_disk = false;
1652 block = sector->getBlockNoCreate(block_y);
1653 if(block->isDummy() == true)
1658 catch(InvalidPositionException &e)
1664 If block was not found on disk and not going to generate a
1665 new one, make sure there is a dummy block in place.
1667 if(not_on_disk && only_from_disk)
1671 // Create dummy block
1672 block = new MapBlock(this, p, true);
1674 // Add block to sector
1675 sector->insertBlock(block);
1681 //dstream<<"Not found on disk, generating."<<std::endl;
1684 Do not generate over-limit
1686 if(blockpos_over_limit(p))
1687 throw InvalidPositionException("emergeBlock(): pos. over limit");
1692 Go on generating the block.
1694 TODO: If a dungeon gets generated so that it's side gets
1695 revealed to the outside air, the lighting should be
1700 If block doesn't exist, create one.
1701 If it exists, it is a dummy. In that case unDummify() it.
1705 block = sector->createBlankBlockNoInsert(block_y);
1709 // Remove the block so that nobody can get a half-generated one.
1710 sector->removeBlock(block);
1711 // Allocate the block to be a proper one.
1715 // Randomize a bit. This makes dungeons.
1716 /*bool low_block_is_empty = false;
1718 low_block_is_empty = true;*/
1721 bool underground_emptiness[ued*ued*ued];
1722 for(s32 i=0; i<ued*ued*ued; i++)
1724 underground_emptiness[i] = ((rand() % 4) == 0);
1727 // This is the basic material of what the visible flat ground
1729 u8 material = MATERIAL_GRASS;
1731 s32 lowest_ground_y = 32767;
1734 //sector->printHeightmaps();
1736 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1737 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1739 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1741 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1742 //assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1743 if(surface_y_f < GROUNDHEIGHT_VALID_MINVALUE)
1745 dstream<<"WARNING: Surface height not found in sector "
1746 "for block that is being emerged"<<std::endl;
1750 s16 surface_y = surface_y_f;
1751 //avg_ground_y += surface_y;
1752 if(surface_y < lowest_ground_y)
1753 lowest_ground_y = surface_y;
1755 s32 surface_depth = 0;
1757 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1759 float min_slope = 0.45;
1760 float max_slope = 0.85;
1761 float min_slope_depth = 5.0;
1762 float max_slope_depth = 0;
1763 if(slope < min_slope)
1764 surface_depth = min_slope_depth;
1765 else if(slope > max_slope)
1766 surface_depth = max_slope_depth;
1768 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1770 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1772 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1777 NOTE: If there are some man-made structures above the
1778 newly created block, they won't be taken into account.
1780 if(real_y > surface_y)
1781 n.setLight(LIGHT_SUN);
1785 // If node is very low
1786 if(real_y <= surface_y - 7){
1788 if(underground_emptiness[
1789 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1790 +ued*(y0*ued/MAP_BLOCKSIZE)
1791 +(x0*ued/MAP_BLOCKSIZE)])
1797 n.d = MATERIAL_STONE;
1800 // If node is under surface level
1801 else if(real_y <= surface_y - surface_depth)
1802 n.d = MATERIAL_STONE;
1803 // If node is at or under heightmap y
1804 else if(real_y <= surface_y)
1806 // If under water level, it's mud
1807 if(real_y < WATER_LEVEL)
1809 // Else it's the main material
1813 // If node is over heightmap y
1815 // If under water level, it's water
1816 if(real_y < WATER_LEVEL)
1818 n.d = MATERIAL_WATER;
1819 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1825 block->setNode(v3s16(x0,y0,z0), n);
1830 Calculate is_underground
1832 // Probably underground if the highest part of block is under lowest
1834 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1835 block->setIsUnderground(is_underground);
1838 Force lighting update if underground.
1839 This is needed because of ravines.
1844 lighting_invalidated_blocks[block->getPos()] = block;
1853 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1854 for(s16 i=0; i<underground_level*3; i++)
1859 (rand()%(MAP_BLOCKSIZE-2))+1,
1860 (rand()%(MAP_BLOCKSIZE-2))+1,
1861 (rand()%(MAP_BLOCKSIZE-2))+1
1865 n.d = MATERIAL_MESE;
1867 if(is_ground_material(block->getNode(cp).d))
1869 block->setNode(cp, n);
1871 for(u16 i=0; i<26; i++)
1873 if(is_ground_material(block->getNode(cp+g_26dirs[i]).d))
1875 block->setNode(cp+g_26dirs[i], n);
1882 Create a few rats in empty blocks underground
1886 //for(u16 i=0; i<2; i++)
1889 (rand()%(MAP_BLOCKSIZE-2))+1,
1890 (rand()%(MAP_BLOCKSIZE-2))+1,
1891 (rand()%(MAP_BLOCKSIZE-2))+1
1894 // Check that the place is empty
1895 //if(!is_ground_material(block->getNode(cp).d))
1898 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1899 block->addObject(obj);
1905 Add block to sector.
1907 sector->insertBlock(block);
1910 Do some interpolation for dungeons
1915 TimeTaker timer("interpolation", g_device);
1917 MapVoxelManipulator vmanip(this);
1919 v3s16 relpos = block->getPosRelative();
1921 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1922 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1923 /*vmanip.interpolate(VoxelArea(relpos,
1924 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1926 core::map<v3s16, MapBlock*> modified_blocks;
1927 vmanip.blitBack(modified_blocks);
1928 dstream<<"blitBack modified "<<modified_blocks.size()
1929 <<" blocks"<<std::endl;
1931 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1932 for(core::map<v3s16, MapBlock*>::Iterator
1933 i = modified_blocks.getIterator();
1934 i.atEnd() == false; i++)
1936 MapBlock *block = i.getNode()->getValue();
1938 changed_blocks.insert(block->getPos(), block);
1939 //lighting_invalidated_blocks.insert(block->getPos(), block);
1949 // An y-wise container of changed blocks
1950 core::map<s16, MapBlock*> changed_blocks_sector;
1953 Check if any sector's objects can be placed now.
1956 core::map<v3s16, u8> *objects = sector->getObjects();
1957 core::list<v3s16> objects_to_remove;
1958 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1959 i.atEnd() == false; i++)
1961 v3s16 p = i.getNode()->getKey();
1963 u8 d = i.getNode()->getValue();
1965 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1970 if(d == SECTOR_OBJECT_TEST)
1972 if(sector->isValidArea(p + v3s16(0,0,0),
1973 p + v3s16(0,0,0), &changed_blocks_sector))
1976 n.d = MATERIAL_LIGHT;
1977 sector->setNode(p, n);
1978 objects_to_remove.push_back(p);
1981 else if(d == SECTOR_OBJECT_TREE_1)
1983 v3s16 p_min = p + v3s16(-1,0,-1);
1984 v3s16 p_max = p + v3s16(1,4,1);
1985 if(sector->isValidArea(p_min, p_max,
1986 &changed_blocks_sector))
1989 n.d = MATERIAL_TREE;
1990 sector->setNode(p+v3s16(0,0,0), n);
1991 sector->setNode(p+v3s16(0,1,0), n);
1992 sector->setNode(p+v3s16(0,2,0), n);
1993 sector->setNode(p+v3s16(0,3,0), n);
1995 n.d = MATERIAL_LEAVES;
1997 sector->setNode(p+v3s16(0,4,0), n);
1999 sector->setNode(p+v3s16(-1,4,0), n);
2000 sector->setNode(p+v3s16(1,4,0), n);
2001 sector->setNode(p+v3s16(0,4,-1), n);
2002 sector->setNode(p+v3s16(0,4,1), n);
2003 sector->setNode(p+v3s16(1,4,1), n);
2004 sector->setNode(p+v3s16(-1,4,1), n);
2005 sector->setNode(p+v3s16(-1,4,-1), n);
2006 sector->setNode(p+v3s16(1,4,-1), n);
2008 sector->setNode(p+v3s16(-1,3,0), n);
2009 sector->setNode(p+v3s16(1,3,0), n);
2010 sector->setNode(p+v3s16(0,3,-1), n);
2011 sector->setNode(p+v3s16(0,3,1), n);
2012 sector->setNode(p+v3s16(1,3,1), n);
2013 sector->setNode(p+v3s16(-1,3,1), n);
2014 sector->setNode(p+v3s16(-1,3,-1), n);
2015 sector->setNode(p+v3s16(1,3,-1), n);
2017 objects_to_remove.push_back(p);
2019 // Lighting has to be recalculated for this one.
2020 sector->getBlocksInArea(p_min, p_max,
2021 lighting_invalidated_blocks);
2024 else if(d == SECTOR_OBJECT_BUSH_1)
2026 if(sector->isValidArea(p + v3s16(0,0,0),
2027 p + v3s16(0,0,0), &changed_blocks_sector))
2030 n.d = MATERIAL_LEAVES;
2031 sector->setNode(p+v3s16(0,0,0), n);
2033 objects_to_remove.push_back(p);
2036 else if(d == SECTOR_OBJECT_RAVINE)
2039 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
2040 v3s16 p_max = p + v3s16(6,6,6);
2041 if(sector->isValidArea(p_min, p_max,
2042 &changed_blocks_sector))
2045 n.d = MATERIAL_STONE;
2047 n2.d = MATERIAL_AIR;
2048 s16 depth = maxdepth + (rand()%10);
2050 s16 minz = -6 - (-2);
2052 for(s16 x=-6; x<=6; x++)
2054 z += -1 + (rand()%3);
2059 for(s16 y=depth+(rand()%2); y<=6; y++)
2061 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
2064 v3s16 p2 = p + v3s16(x,y,z-2);
2065 if(is_ground_material(sector->getNode(p2).d))
2066 sector->setNode(p2, n);
2069 v3s16 p2 = p + v3s16(x,y,z-1);
2070 if(is_ground_material(sector->getNode(p2).d))
2071 sector->setNode(p2, n2);
2074 v3s16 p2 = p + v3s16(x,y,z+0);
2075 if(is_ground_material(sector->getNode(p2).d))
2076 sector->setNode(p2, n2);
2079 v3s16 p2 = p + v3s16(x,y,z+1);
2080 if(is_ground_material(sector->getNode(p2).d))
2081 sector->setNode(p2, n);
2084 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2085 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2089 objects_to_remove.push_back(p);
2091 // Lighting has to be recalculated for this one.
2092 sector->getBlocksInArea(p_min, p_max,
2093 lighting_invalidated_blocks);
2098 dstream<<"ServerMap::emergeBlock(): "
2099 "Invalid heightmap object"
2104 catch(InvalidPositionException &e)
2106 dstream<<"WARNING: "<<__FUNCTION_NAME
2107 <<": while inserting object "<<(int)d
2108 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2109 <<" InvalidPositionException.what()="
2110 <<e.what()<<std::endl;
2111 // This is not too fatal and seems to happen sometimes.
2116 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2117 i != objects_to_remove.end(); i++)
2119 objects->remove(*i);
2122 for(core::map<s16, MapBlock*>::Iterator
2123 i = changed_blocks_sector.getIterator();
2124 i.atEnd() == false; i++)
2126 MapBlock *block = i.getNode()->getValue();
2128 changed_blocks.insert(block->getPos(), block);
2134 void ServerMap::createDir(std::string path)
2136 if(fs::CreateDir(path) == false)
2138 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2139 <<"\""<<path<<"\""<<std::endl;
2140 throw BaseException("ServerMap failed to create directory");
2144 std::string ServerMap::getSectorSubDir(v2s16 pos)
2147 snprintf(cc, 9, "%.4x%.4x",
2148 (unsigned int)pos.X&0xffff,
2149 (unsigned int)pos.Y&0xffff);
2151 return std::string(cc);
2154 std::string ServerMap::getSectorDir(v2s16 pos)
2156 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2159 v2s16 ServerMap::getSectorPos(std::string dirname)
2161 if(dirname.size() != 8)
2162 throw InvalidFilenameException("Invalid sector directory name");
2164 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2166 throw InvalidFilenameException("Invalid sector directory name");
2167 v2s16 pos((s16)x, (s16)y);
2171 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2173 v2s16 p2d = getSectorPos(sectordir);
2175 if(blockfile.size() != 4){
2176 throw InvalidFilenameException("Invalid block filename");
2179 int r = sscanf(blockfile.c_str(), "%4x", &y);
2181 throw InvalidFilenameException("Invalid block filename");
2182 return v3s16(p2d.X, y, p2d.Y);
2186 #define ENABLE_SECTOR_SAVING 1
2187 #define ENABLE_SECTOR_LOADING 1
2188 #define ENABLE_BLOCK_SAVING 1
2189 #define ENABLE_BLOCK_LOADING 1
2191 void ServerMap::save(bool only_changed)
2193 DSTACK(__FUNCTION_NAME);
2194 if(m_map_saving_enabled == false)
2196 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2200 if(only_changed == false)
2201 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2204 saveMasterHeightmap();
2206 u32 sector_meta_count = 0;
2207 u32 block_count = 0;
2210 JMutexAutoLock lock(m_sector_mutex);
2212 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2213 for(; i.atEnd() == false; i++)
2215 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2216 assert(sector->getId() == MAPSECTOR_SERVER);
2218 if(ENABLE_SECTOR_SAVING)
2220 if(sector->differs_from_disk || only_changed == false)
2222 saveSectorMeta(sector);
2223 sector_meta_count++;
2226 if(ENABLE_BLOCK_SAVING)
2228 core::list<MapBlock*> blocks;
2229 sector->getBlocks(blocks);
2230 core::list<MapBlock*>::Iterator j;
2231 for(j=blocks.begin(); j!=blocks.end(); j++)
2233 MapBlock *block = *j;
2234 if(block->getChangedFlag() || only_changed == false)
2245 u32 deleted_count = 0;
2246 deleted_count = deleteUnusedSectors
2247 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2250 Only print if something happened or saved whole map
2252 if(only_changed == false || sector_meta_count != 0
2253 || block_count != 0 || deleted_count != 0)
2255 dstream<<DTIME<<"ServerMap: Written: "
2256 <<sector_meta_count<<" sector metadata files, "
2257 <<block_count<<" block files, "
2258 <<deleted_count<<" sectors unloaded from memory."
2263 void ServerMap::loadAll()
2265 DSTACK(__FUNCTION_NAME);
2266 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2268 loadMasterHeightmap();
2270 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2272 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2274 JMutexAutoLock lock(m_sector_mutex);
2277 s32 printed_counter = -100000;
2278 s32 count = list.size();
2280 std::vector<fs::DirListNode>::iterator i;
2281 for(i=list.begin(); i!=list.end(); i++)
2283 if(counter > printed_counter + 10)
2285 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2286 printed_counter = counter;
2290 MapSector *sector = NULL;
2292 // We want directories
2296 sector = loadSectorMeta(i->name);
2298 catch(InvalidFilenameException &e)
2300 // This catches unknown crap in directory
2303 if(ENABLE_BLOCK_LOADING)
2305 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2306 (m_savedir+"/sectors/"+i->name);
2307 std::vector<fs::DirListNode>::iterator i2;
2308 for(i2=list2.begin(); i2!=list2.end(); i2++)
2314 loadBlock(i->name, i2->name, sector);
2316 catch(InvalidFilenameException &e)
2318 // This catches unknown crap in directory
2323 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2326 void ServerMap::saveMasterHeightmap()
2328 DSTACK(__FUNCTION_NAME);
2329 createDir(m_savedir);
2331 std::string fullpath = m_savedir + "/master_heightmap";
2332 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2333 if(o.good() == false)
2334 throw FileNotGoodException("Cannot open master heightmap");
2336 // Format used for writing
2337 u8 version = SER_FMT_VER_HIGHEST;
2340 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2342 [0] u8 serialization version
2343 [1] X master heightmap
2345 u32 fullsize = 1 + hmdata.getSize();
2346 SharedBuffer<u8> data(fullsize);
2349 memcpy(&data[1], *hmdata, hmdata.getSize());
2351 o.write((const char*)*data, fullsize);
2354 m_heightmap->serialize(o, version);
2357 void ServerMap::loadMasterHeightmap()
2359 DSTACK(__FUNCTION_NAME);
2360 std::string fullpath = m_savedir + "/master_heightmap";
2361 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2362 if(is.good() == false)
2363 throw FileNotGoodException("Cannot open master heightmap");
2365 if(m_heightmap != NULL)
2368 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2371 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2373 DSTACK(__FUNCTION_NAME);
2374 // Format used for writing
2375 u8 version = SER_FMT_VER_HIGHEST;
2377 v2s16 pos = sector->getPos();
2378 createDir(m_savedir);
2379 createDir(m_savedir+"/sectors");
2380 std::string dir = getSectorDir(pos);
2383 std::string fullpath = dir + "/heightmap";
2384 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2385 if(o.good() == false)
2386 throw FileNotGoodException("Cannot open master heightmap");
2388 sector->serialize(o, version);
2390 sector->differs_from_disk = false;
2393 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2395 DSTACK(__FUNCTION_NAME);
2397 v2s16 p2d = getSectorPos(dirname);
2398 std::string dir = m_savedir + "/sectors/" + dirname;
2400 std::string fullpath = dir + "/heightmap";
2401 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2402 if(is.good() == false)
2403 throw FileNotGoodException("Cannot open sector heightmap");
2405 ServerMapSector *sector = ServerMapSector::deSerialize
2406 (is, this, p2d, &m_hwrapper, m_sectors);
2408 sector->differs_from_disk = false;
2413 bool ServerMap::loadSectorFull(v2s16 p2d)
2415 DSTACK(__FUNCTION_NAME);
2416 std::string sectorsubdir = getSectorSubDir(p2d);
2418 MapSector *sector = NULL;
2420 JMutexAutoLock lock(m_sector_mutex);
2423 sector = loadSectorMeta(sectorsubdir);
2425 catch(InvalidFilenameException &e)
2429 catch(FileNotGoodException &e)
2433 catch(std::exception &e)
2438 if(ENABLE_BLOCK_LOADING)
2440 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2441 (m_savedir+"/sectors/"+sectorsubdir);
2442 std::vector<fs::DirListNode>::iterator i2;
2443 for(i2=list2.begin(); i2!=list2.end(); i2++)
2449 loadBlock(sectorsubdir, i2->name, sector);
2451 catch(InvalidFilenameException &e)
2453 // This catches unknown crap in directory
2461 bool ServerMap::deFlushSector(v2s16 p2d)
2463 DSTACK(__FUNCTION_NAME);
2464 // See if it already exists in memory
2466 MapSector *sector = getSectorNoGenerate(p2d);
2469 catch(InvalidPositionException &e)
2472 Try to load the sector from disk.
2474 if(loadSectorFull(p2d) == true)
2483 void ServerMap::saveBlock(MapBlock *block)
2485 DSTACK(__FUNCTION_NAME);
2487 Dummy blocks are not written
2489 if(block->isDummy())
2491 /*v3s16 p = block->getPos();
2492 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2493 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2497 // Format used for writing
2498 u8 version = SER_FMT_VER_HIGHEST;
2500 v3s16 p3d = block->getPos();
2501 v2s16 p2d(p3d.X, p3d.Z);
2502 createDir(m_savedir);
2503 createDir(m_savedir+"/sectors");
2504 std::string dir = getSectorDir(p2d);
2507 // Block file is map/sectors/xxxxxxxx/xxxx
2509 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2510 std::string fullpath = dir + "/" + cc;
2511 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2512 if(o.good() == false)
2513 throw FileNotGoodException("Cannot open block data");
2516 [0] u8 serialization version
2519 o.write((char*)&version, 1);
2521 block->serialize(o, version);
2524 Versions up from 9 have block objects.
2528 block->serializeObjects(o, version);
2531 // We just wrote it to the disk
2532 block->resetChangedFlag();
2535 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2537 DSTACK(__FUNCTION_NAME);
2541 // Block file is map/sectors/xxxxxxxx/xxxx
2542 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2543 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2544 if(is.good() == false)
2545 throw FileNotGoodException("Cannot open block file");
2547 v3s16 p3d = getBlockPos(sectordir, blockfile);
2548 v2s16 p2d(p3d.X, p3d.Z);
2550 assert(sector->getPos() == p2d);
2552 u8 version = SER_FMT_VER_INVALID;
2553 is.read((char*)&version, 1);
2555 /*u32 block_size = MapBlock::serializedLength(version);
2556 SharedBuffer<u8> data(block_size);
2557 is.read((char*)*data, block_size);*/
2559 // This will always return a sector because we're the server
2560 //MapSector *sector = emergeSector(p2d);
2562 MapBlock *block = NULL;
2563 bool created_new = false;
2565 block = sector->getBlockNoCreate(p3d.Y);
2567 catch(InvalidPositionException &e)
2569 block = sector->createBlankBlockNoInsert(p3d.Y);
2573 // deserialize block data
2574 block->deSerialize(is, version);
2577 Versions up from 9 have block objects.
2581 block->updateObjects(is, version, NULL);
2585 sector->insertBlock(block);
2588 Convert old formats to new and save
2591 // Save old format blocks in new format
2592 if(version < SER_FMT_VER_HIGHEST)
2597 // We just loaded it from the disk, so it's up-to-date.
2598 block->resetChangedFlag();
2601 catch(SerializationError &e)
2603 dstream<<"WARNING: Invalid block data on disk "
2604 "(SerializationError). Ignoring."
2609 // Gets from master heightmap
2610 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2612 assert(m_heightmap != NULL);
2620 corners[0] = m_heightmap->getGroundHeight
2621 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2622 corners[1] = m_heightmap->getGroundHeight
2623 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2624 corners[2] = m_heightmap->getGroundHeight
2625 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2626 corners[3] = m_heightmap->getGroundHeight
2627 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2630 void ServerMap::PrintInfo(std::ostream &out)
2639 ClientMap::ClientMap(
2641 video::SMaterial *materials,
2642 scene::ISceneNode* parent,
2643 scene::ISceneManager* mgr,
2647 scene::ISceneNode(parent, mgr, id),
2649 m_materials(materials),
2652 /*m_box = core::aabbox3d<f32>(0,0,0,
2653 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2654 /*m_box = core::aabbox3d<f32>(0,0,0,
2655 map->getSizeNodes().X * BS,
2656 map->getSizeNodes().Y * BS,
2657 map->getSizeNodes().Z * BS);*/
2658 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2659 BS*1000000,BS*1000000,BS*1000000);
2664 ClientMap::~ClientMap()
2666 JMutexAutoLock lock(mesh_mutex);
2675 MapSector * ClientMap::emergeSector(v2s16 p2d)
2677 DSTACK(__FUNCTION_NAME);
2678 // Check that it doesn't exist already
2680 return getSectorNoGenerate(p2d);
2682 catch(InvalidPositionException &e)
2686 // Create a sector with no heightmaps
2687 ClientMapSector *sector = new ClientMapSector(this, p2d);
2690 JMutexAutoLock lock(m_sector_mutex);
2691 m_sectors.insert(p2d, sector);
2697 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2699 DSTACK(__FUNCTION_NAME);
2700 ClientMapSector *sector = NULL;
2702 JMutexAutoLock lock(m_sector_mutex);
2704 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2708 sector = (ClientMapSector*)n->getValue();
2709 assert(sector->getId() == MAPSECTOR_CLIENT);
2713 sector = new ClientMapSector(this, p2d);
2715 JMutexAutoLock lock(m_sector_mutex);
2716 m_sectors.insert(p2d, sector);
2720 sector->deSerialize(is);
2723 void ClientMap::renderMap(video::IVideoDriver* driver,
2724 video::SMaterial *materials, s32 pass)
2726 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2727 DSTACK(__FUNCTION_NAME);
2729 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2732 Draw master heightmap mesh
2736 JMutexAutoLock lock(mesh_mutex);
2739 u32 c = mesh->getMeshBufferCount();
2741 for(u32 i=0; i<c; i++)
2743 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2744 const video::SMaterial& material = buf->getMaterial();
2745 video::IMaterialRenderer* rnd =
2746 driver->getMaterialRenderer(material.MaterialType);
2747 bool transparent = (rnd && rnd->isTransparent());
2748 // Render transparent on transparent pass and likewise.
2749 if(transparent == is_transparent_pass)
2751 driver->setMaterial(buf->getMaterial());
2752 driver->drawMeshBuffer(buf);
2760 Get time for measuring timeout.
2762 Measuring time is very useful for long delays when the
2763 machine is swapping a lot.
2765 int time1 = time(0);
2768 Collect all blocks that are in the view range
2770 Should not optimize more here as we want to auto-update
2771 all changed nodes in viewing range at the next step.
2774 s16 viewing_range_nodes;
2775 bool viewing_range_all;
2777 JMutexAutoLock lock(g_range_mutex);
2778 viewing_range_nodes = g_viewing_range_nodes;
2779 viewing_range_all = g_viewing_range_all;
2782 m_camera_mutex.Lock();
2783 v3f camera_position = m_camera_position;
2784 v3f camera_direction = m_camera_direction;
2785 m_camera_mutex.Unlock();
2788 Get all blocks and draw all visible ones
2791 v3s16 cam_pos_nodes(
2792 camera_position.X / BS,
2793 camera_position.Y / BS,
2794 camera_position.Z / BS);
2796 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2798 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2799 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2801 // Take a fair amount as we will be dropping more out later
2803 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2804 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2805 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2807 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2808 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2809 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2811 u32 vertex_count = 0;
2813 core::map<v2s16, MapSector*>::Iterator si;
2815 //NOTE: The sectors map should be locked but we're not doing it
2816 // because it'd cause too much delays
2818 si = m_sectors.getIterator();
2819 for(; si.atEnd() == false; si++)
2822 static int timecheck_counter = 0;
2823 timecheck_counter++;
2824 if(timecheck_counter > 50)
2826 int time2 = time(0);
2827 if(time2 > time1 + 4)
2829 dstream<<"ClientMap::renderMap(): "
2830 "Rendering takes ages, returning."
2837 MapSector *sector = si.getNode()->getValue();
2838 v2s16 sp = sector->getPos();
2840 if(viewing_range_all == false)
2842 if(sp.X < p_blocks_min.X
2843 || sp.X > p_blocks_max.X
2844 || sp.Y < p_blocks_min.Z
2845 || sp.Y > p_blocks_max.Z)
2849 core::list< MapBlock * > sectorblocks;
2850 sector->getBlocks(sectorblocks);
2856 core::list< MapBlock * >::Iterator i;
2857 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2859 MapBlock *block = *i;
2862 Compare block position to camera position, skip
2863 if not seen on display
2866 v3s16 blockpos_nodes = block->getPosRelative();
2868 // Block center position
2870 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2871 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2872 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2875 // Block position relative to camera
2876 v3f blockpos_relative = blockpos - camera_position;
2878 // Distance in camera direction (+=front, -=back)
2879 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2882 f32 d = blockpos_relative.getLength();
2884 if(viewing_range_all == false)
2886 // If block is far away, don't draw it
2887 if(d > viewing_range_nodes * BS)
2891 // Maximum radius of a block
2892 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2894 // If block is (nearly) touching the camera, don't
2895 // bother validating further (that is, render it anyway)
2896 if(d > block_max_radius * 1.5)
2898 // Cosine of the angle between the camera direction
2899 // and the block direction (camera_direction is an unit vector)
2900 f32 cosangle = dforward / d;
2902 // Compensate for the size of the block
2903 // (as the block has to be shown even if it's a bit off FOV)
2904 // This is an estimate.
2905 cosangle += block_max_radius / dforward;
2907 // If block is not in the field of view, skip it
2908 //if(cosangle < cos(FOV_ANGLE/2))
2909 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2914 Draw the faces of the block
2918 JMutexAutoLock lock(block->mesh_mutex);
2920 // Cancel if block has no mesh
2921 if(block->mesh == NULL)
2924 u32 c = block->mesh->getMeshBufferCount();
2926 for(u32 i=0; i<c; i++)
2928 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2929 const video::SMaterial& material = buf->getMaterial();
2930 video::IMaterialRenderer* rnd =
2931 driver->getMaterialRenderer(material.MaterialType);
2932 bool transparent = (rnd && rnd->isTransparent());
2933 // Render transparent on transparent pass and likewise.
2934 if(transparent == is_transparent_pass)
2936 driver->setMaterial(buf->getMaterial());
2937 driver->drawMeshBuffer(buf);
2938 vertex_count += buf->getVertexCount();
2942 } // foreach sectorblocks
2945 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2946 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2949 void ClientMap::updateMesh()
2952 DSTACK(__FUNCTION_NAME);
2955 Check what sectors don't draw anything useful at ground level
2956 and create a mesh of the rough heightmap at those positions.
2959 m_camera_mutex.Lock();
2960 v3f camera_position = m_camera_position;
2961 v3f camera_direction = m_camera_direction;
2962 m_camera_mutex.Unlock();
2964 v3s16 cam_pos_nodes(
2965 camera_position.X / BS,
2966 camera_position.Y / BS,
2967 camera_position.Z / BS);
2969 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2971 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2972 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2974 // Take a fair amount as we will be dropping more out later
2976 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2977 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2978 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2980 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2981 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2982 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2988 scene::SMesh *mesh_new = new scene::SMesh();
2989 //scene::IMeshBuffer *buf = NULL;
2990 scene::SMeshBuffer *buf = NULL;
2992 u8 material_in_use = 0;
2995 Loop through sectors
2998 for(core::map<v2s16, MapSector*>::Iterator
2999 si = m_sectors.getIterator();
3000 si.atEnd() == false; si++)
3002 MapSector *sector = si.getNode()->getValue();
3004 if(sector->getId() != MAPSECTOR_CLIENT)
3006 dstream<<"WARNING: Client has a non-client sector"
3011 ClientMapSector *cs = (ClientMapSector*)sector;
3013 v2s16 sp = sector->getPos();
3015 if(sp.X < p_blocks_min.X
3016 || sp.X > p_blocks_max.X
3017 || sp.Y < p_blocks_min.Z
3018 || sp.Y > p_blocks_max.Z)
3022 Get some ground level info
3034 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
3036 s16 cn_max = -32768;
3037 for(s16 i=0; i<4; i++)
3044 s16 cn_slope = cn_max - cn_min;
3047 Generate this part of the heightmap mesh
3051 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
3053 else if(cn_slope <= MAP_BLOCKSIZE)
3058 if(material != material_in_use || buf == NULL)
3060 // Try to get a meshbuffer associated with the material
3061 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
3062 (g_mesh_materials[material]);
3063 // If not found, create one
3066 // This is a "Standard MeshBuffer",
3067 // it's a typedeffed CMeshBuffer<video::S3DVertex>
3068 buf = new scene::SMeshBuffer();
3071 buf->Material = g_mesh_materials[material];
3073 //buf->setHardwareMappingHint(scene::EHM_STATIC);
3075 mesh_new->addMeshBuffer(buf);
3079 material_in_use = material;
3082 // Sector side width in floating-point units
3083 f32 sd = BS * MAP_BLOCKSIZE;
3084 // Sector position in global floating-point units
3085 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
3087 //video::SColor c(255,255,255,255);
3089 video::SColor c(255,cc,cc,cc);
3091 video::S3DVertex vertices[4] =
3093 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
3094 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3095 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3096 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3098 u16 indices[] = {0,1,2,2,3,0};
3100 buf->append(vertices, 4, indices, 6);
3104 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3112 scene::SMesh *mesh_old = mesh;
3119 mesh_mutex.Unlock();
3121 if(mesh_old != NULL)
3123 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3125 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3126 (g_materials[MATERIAL_GRASS]);
3128 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3135 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3140 void ClientMap::PrintInfo(std::ostream &out)