2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
8 #include "jmutexautolock.h"
16 #define sleep_ms(x) Sleep(x)
19 #define sleep_ms(x) usleep(x*1000)
22 MapBlockPointerCache::MapBlockPointerCache(Map *map)
25 m_map->m_blockcachelock.cacheCreated();
27 m_from_cache_count = 0;
31 MapBlockPointerCache::~MapBlockPointerCache()
33 m_map->m_blockcachelock.cacheRemoved();
35 dstream<<"MapBlockPointerCache:"
36 <<" from_cache_count="<<m_from_cache_count
37 <<" from_map_count="<<m_from_map_count
41 MapBlock * MapBlockPointerCache::getBlockNoCreate(v3s16 p)
43 core::map<v3s16, MapBlock*>::Node *n = NULL;
53 // Throws InvalidPositionException if not found
54 MapBlock *b = m_map->getBlockNoCreate(p);
63 Map::Map(std::ostream &dout):
65 m_camera_position(0,0,0),
66 m_camera_direction(0,0,1),
71 m_sector_mutex.Init();
72 m_camera_mutex.Init();
73 assert(m_sector_mutex.IsInitialized());
74 assert(m_camera_mutex.IsInitialized());
76 // Get this so that the player can stay on it at first
77 //getSector(v2s16(0,0));
85 /*updater.setRun(false);
86 while(updater.IsRunning())
92 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
93 for(; i.atEnd() == false; i++)
95 MapSector *sector = i.getNode()->getValue();
100 /*bool Map::sectorExists(v2s16 p)
102 JMutexAutoLock lock(m_sector_mutex);
103 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
107 MapSector * Map::getSectorNoGenerate(v2s16 p)
109 JMutexAutoLock lock(m_sector_mutex);
111 if(m_sector_cache != NULL && p == m_sector_cache_p){
112 MapSector * sector = m_sector_cache;
113 // Reset inactivity timer
114 sector->usage_timer = 0.0;
118 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
119 // If sector doesn't exist, throw an exception
122 throw InvalidPositionException();
125 MapSector *sector = n->getValue();
127 // Cache the last result
128 m_sector_cache_p = p;
129 m_sector_cache = sector;
131 //MapSector * ref(sector);
133 // Reset inactivity timer
134 sector->usage_timer = 0.0;
138 MapBlock * Map::getBlockNoCreate(v3s16 p3d)
140 v2s16 p2d(p3d.X, p3d.Z);
141 MapSector * sector = getSectorNoGenerate(p2d);
143 MapBlock *block = sector->getBlockNoCreate(p3d.Y);
148 /*MapBlock * Map::getBlock(v3s16 p3d, bool generate)
150 dstream<<"Map::getBlock() with generate=true called"
152 v2s16 p2d(p3d.X, p3d.Z);
153 //MapSector * sector = getSector(p2d, generate);
154 MapSector * sector = getSectorNoGenerate(p2d);
157 throw InvalidPositionException();
159 return sector->getBlockNoCreate(p3d.Y);
162 f32 Map::getGroundHeight(v2s16 p, bool generate)
165 v2s16 sectorpos = getNodeSectorPos(p);
166 MapSector * sref = getSectorNoGenerate(sectorpos);
167 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
168 f32 y = sref->getGroundHeight(relpos);
171 catch(InvalidPositionException &e)
173 return GROUNDHEIGHT_NOTFOUND_SETVALUE;
177 void Map::setGroundHeight(v2s16 p, f32 y, bool generate)
179 /*m_dout<<DTIME<<"Map::setGroundHeight(("
181 <<"), "<<y<<")"<<std::endl;*/
182 v2s16 sectorpos = getNodeSectorPos(p);
183 MapSector * sref = getSectorNoGenerate(sectorpos);
184 v2s16 relpos = p - sectorpos * MAP_BLOCKSIZE;
185 //sref->mutex.Lock();
186 sref->setGroundHeight(relpos, y);
187 //sref->mutex.Unlock();
190 bool Map::isNodeUnderground(v3s16 p)
192 v3s16 blockpos = getNodeBlockPos(p);
194 MapBlock * block = getBlockNoCreate(blockpos);
195 return block->getIsUnderground();
197 catch(InvalidPositionException &e)
204 void Map::interpolate(v3s16 block,
205 core::map<v3s16, MapBlock*> & modified_blocks)
207 const v3s16 dirs[6] = {
208 v3s16(0,0,1), // back
210 v3s16(1,0,0), // right
211 v3s16(0,0,-1), // front
212 v3s16(0,-1,0), // bottom
213 v3s16(-1,0,0), // left
216 if(from_nodes.size() == 0)
219 u32 blockchangecount = 0;
221 core::map<v3s16, bool> lighted_nodes;
222 core::map<v3s16, bool>::Iterator j;
223 j = from_nodes.getIterator();
226 Initialize block cache
229 MapBlock *block = NULL;
230 // Cache this a bit, too
231 bool block_checked_in_modified = false;
233 for(; j.atEnd() == false; j++)
234 //for(; j != from_nodes.end(); j++)
236 v3s16 pos = j.getNode()->getKey();
238 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
239 v3s16 blockpos = getNodeBlockPos(pos);
241 // Only fetch a new block if the block position has changed
243 if(block == NULL || blockpos != blockpos_last){
244 block = getBlockNoCreate(blockpos);
245 blockpos_last = blockpos;
247 block_checked_in_modified = false;
251 catch(InvalidPositionException &e)
259 // Calculate relative position in block
260 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
262 // Get node straight from the block
263 MapNode n = block->getNode(relpos);
265 u8 oldlight = n.getLight();
266 u8 newlight = diminish_light(oldlight);
268 // Loop through 6 neighbors
269 for(u16 i=0; i<6; i++){
270 // Get the position of the neighbor node
271 v3s16 n2pos = pos + dirs[i];
273 // Get the block where the node is located
274 v3s16 blockpos = getNodeBlockPos(n2pos);
278 // Only fetch a new block if the block position has changed
280 if(block == NULL || blockpos != blockpos_last){
281 block = getBlockNoCreate(blockpos);
282 blockpos_last = blockpos;
284 block_checked_in_modified = false;
288 catch(InvalidPositionException &e)
293 // Calculate relative position in block
294 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
295 // Get node straight from the block
296 MapNode n2 = block->getNode(relpos);
298 bool changed = false;
300 If the neighbor is brighter than the current node,
301 add to list (it will light up this node on its turn)
303 if(n2.getLight() > undiminish_light(oldlight))
305 lighted_nodes.insert(n2pos, true);
306 //lighted_nodes.push_back(n2pos);
310 If the neighbor is dimmer than how much light this node
311 would spread on it, add to list
313 if(n2.getLight() < newlight)
315 if(n2.light_propagates())
317 n2.setLight(newlight);
318 block->setNode(relpos, n2);
319 lighted_nodes.insert(n2pos, true);
320 //lighted_nodes.push_back(n2pos);
325 // Add to modified_blocks
326 if(changed == true && block_checked_in_modified == false)
328 // If the block is not found in modified_blocks, add.
329 if(modified_blocks.find(blockpos) == NULL)
331 modified_blocks.insert(blockpos, block);
333 block_checked_in_modified = true;
336 catch(InvalidPositionException &e)
343 /*dstream<<"spreadLight(): Changed block "
344 <<blockchangecount<<" times"
345 <<" for "<<from_nodes.size()<<" nodes"
348 if(lighted_nodes.size() > 0)
349 spreadLight(lighted_nodes, modified_blocks);
354 Goes recursively through the neighbours of the node.
356 Alters only transparent nodes.
358 If the lighting of the neighbour is lower than the lighting of
359 the node was (before changing it to 0 at the step before), the
360 lighting of the neighbour is set to 0 and then the same stuff
361 repeats for the neighbour.
363 The ending nodes of the routine are stored in light_sources.
364 This is useful when a light is removed. In such case, this
365 routine can be called for the light node and then again for
366 light_sources to re-light the area without the removed light.
368 values of from_nodes are lighting values.
370 void Map::unspreadLight(core::map<v3s16, u8> & from_nodes,
371 core::map<v3s16, bool> & light_sources,
372 core::map<v3s16, MapBlock*> & modified_blocks)
375 v3s16(0,0,1), // back
377 v3s16(1,0,0), // right
378 v3s16(0,0,-1), // front
379 v3s16(0,-1,0), // bottom
380 v3s16(-1,0,0), // left
383 if(from_nodes.size() == 0)
386 u32 blockchangecount = 0;
388 core::map<v3s16, u8> unlighted_nodes;
389 core::map<v3s16, u8>::Iterator j;
390 j = from_nodes.getIterator();
393 Initialize block cache
396 MapBlock *block = NULL;
397 // Cache this a bit, too
398 bool block_checked_in_modified = false;
400 for(; j.atEnd() == false; j++)
402 v3s16 pos = j.getNode()->getKey();
403 v3s16 blockpos = getNodeBlockPos(pos);
405 // Only fetch a new block if the block position has changed
407 if(block == NULL || blockpos != blockpos_last){
408 block = getBlockNoCreate(blockpos);
409 blockpos_last = blockpos;
411 block_checked_in_modified = false;
415 catch(InvalidPositionException &e)
423 // Calculate relative position in block
424 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
426 // Get node straight from the block
427 MapNode n = block->getNode(relpos);
429 u8 oldlight = j.getNode()->getValue();
431 // Loop through 6 neighbors
432 for(u16 i=0; i<6; i++)
434 // Get the position of the neighbor node
435 v3s16 n2pos = pos + dirs[i];
437 // Get the block where the node is located
438 v3s16 blockpos = getNodeBlockPos(n2pos);
442 // Only fetch a new block if the block position has changed
444 if(block == NULL || blockpos != blockpos_last){
445 block = getBlockNoCreate(blockpos);
446 blockpos_last = blockpos;
448 block_checked_in_modified = false;
452 catch(InvalidPositionException &e)
457 // Calculate relative position in block
458 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
459 // Get node straight from the block
460 MapNode n2 = block->getNode(relpos);
462 bool changed = false;
464 //TODO: Optimize output by optimizing light_sources?
467 If the neighbor is dimmer than what was specified
468 as oldlight (the light of the previous node)
470 if(n2.getLight() < oldlight)
473 And the neighbor is transparent and it has some light
475 if(n2.light_propagates() && n2.getLight() != 0)
478 Set light to 0 and add to queue
481 u8 current_light = n2.getLight();
483 block->setNode(relpos, n2);
485 unlighted_nodes.insert(n2pos, current_light);
489 Remove from light_sources if it is there
490 NOTE: This doesn't happen nearly at all
492 /*if(light_sources.find(n2pos))
494 std::cout<<"Removed from light_sources"<<std::endl;
495 light_sources.remove(n2pos);
500 light_sources.insert(n2pos, true);
503 // Add to modified_blocks
504 if(changed == true && block_checked_in_modified == false)
506 // If the block is not found in modified_blocks, add.
507 if(modified_blocks.find(blockpos) == NULL)
509 modified_blocks.insert(blockpos, block);
511 block_checked_in_modified = true;
514 catch(InvalidPositionException &e)
521 /*dstream<<"unspreadLight(): Changed block "
522 <<blockchangecount<<" times"
523 <<" for "<<from_nodes.size()<<" nodes"
526 if(unlighted_nodes.size() > 0)
527 unspreadLight(unlighted_nodes, light_sources, modified_blocks);
531 A single-node wrapper of the above
533 void Map::unLightNeighbors(v3s16 pos, u8 lightwas,
534 core::map<v3s16, bool> & light_sources,
535 core::map<v3s16, MapBlock*> & modified_blocks)
537 core::map<v3s16, u8> from_nodes;
538 from_nodes.insert(pos, lightwas);
540 unspreadLight(from_nodes, light_sources, modified_blocks);
544 Lights neighbors of from_nodes, collects all them and then
547 void Map::spreadLight(core::map<v3s16, bool> & from_nodes,
548 core::map<v3s16, MapBlock*> & modified_blocks)
550 const v3s16 dirs[6] = {
551 v3s16(0,0,1), // back
553 v3s16(1,0,0), // right
554 v3s16(0,0,-1), // front
555 v3s16(0,-1,0), // bottom
556 v3s16(-1,0,0), // left
559 if(from_nodes.size() == 0)
562 u32 blockchangecount = 0;
564 core::map<v3s16, bool> lighted_nodes;
565 core::map<v3s16, bool>::Iterator j;
566 j = from_nodes.getIterator();
569 Initialize block cache
572 MapBlock *block = NULL;
573 // Cache this a bit, too
574 bool block_checked_in_modified = false;
576 for(; j.atEnd() == false; j++)
577 //for(; j != from_nodes.end(); j++)
579 v3s16 pos = j.getNode()->getKey();
581 //dstream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
582 v3s16 blockpos = getNodeBlockPos(pos);
584 // Only fetch a new block if the block position has changed
586 if(block == NULL || blockpos != blockpos_last){
587 block = getBlockNoCreate(blockpos);
588 blockpos_last = blockpos;
590 block_checked_in_modified = false;
594 catch(InvalidPositionException &e)
602 // Calculate relative position in block
603 v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE;
605 // Get node straight from the block
606 MapNode n = block->getNode(relpos);
608 u8 oldlight = n.getLight();
609 u8 newlight = diminish_light(oldlight);
611 // Loop through 6 neighbors
612 for(u16 i=0; i<6; i++){
613 // Get the position of the neighbor node
614 v3s16 n2pos = pos + dirs[i];
616 // Get the block where the node is located
617 v3s16 blockpos = getNodeBlockPos(n2pos);
621 // Only fetch a new block if the block position has changed
623 if(block == NULL || blockpos != blockpos_last){
624 block = getBlockNoCreate(blockpos);
625 blockpos_last = blockpos;
627 block_checked_in_modified = false;
631 catch(InvalidPositionException &e)
636 // Calculate relative position in block
637 v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE;
638 // Get node straight from the block
639 MapNode n2 = block->getNode(relpos);
641 bool changed = false;
643 If the neighbor is brighter than the current node,
644 add to list (it will light up this node on its turn)
646 if(n2.getLight() > undiminish_light(oldlight))
648 lighted_nodes.insert(n2pos, true);
649 //lighted_nodes.push_back(n2pos);
653 If the neighbor is dimmer than how much light this node
654 would spread on it, add to list
656 if(n2.getLight() < newlight)
658 if(n2.light_propagates())
660 n2.setLight(newlight);
661 block->setNode(relpos, n2);
662 lighted_nodes.insert(n2pos, true);
663 //lighted_nodes.push_back(n2pos);
668 // Add to modified_blocks
669 if(changed == true && block_checked_in_modified == false)
671 // If the block is not found in modified_blocks, add.
672 if(modified_blocks.find(blockpos) == NULL)
674 modified_blocks.insert(blockpos, block);
676 block_checked_in_modified = true;
679 catch(InvalidPositionException &e)
686 /*dstream<<"spreadLight(): Changed block "
687 <<blockchangecount<<" times"
688 <<" for "<<from_nodes.size()<<" nodes"
691 if(lighted_nodes.size() > 0)
692 spreadLight(lighted_nodes, modified_blocks);
696 A single-node source variation of the above.
698 void Map::lightNeighbors(v3s16 pos,
699 core::map<v3s16, MapBlock*> & modified_blocks)
701 core::map<v3s16, bool> from_nodes;
702 from_nodes.insert(pos, true);
703 spreadLight(from_nodes, modified_blocks);
706 v3s16 Map::getBrightestNeighbour(v3s16 p)
709 v3s16(0,0,1), // back
711 v3s16(1,0,0), // right
712 v3s16(0,0,-1), // front
713 v3s16(0,-1,0), // bottom
714 v3s16(-1,0,0), // left
717 u8 brightest_light = 0;
718 v3s16 brightest_pos(0,0,0);
719 bool found_something = false;
721 // Loop through 6 neighbors
722 for(u16 i=0; i<6; i++){
723 // Get the position of the neighbor node
724 v3s16 n2pos = p + dirs[i];
729 catch(InvalidPositionException &e)
733 if(n2.getLight() > brightest_light || found_something == false){
734 brightest_light = n2.getLight();
735 brightest_pos = n2pos;
736 found_something = true;
740 if(found_something == false)
741 throw InvalidPositionException();
743 return brightest_pos;
747 Propagates sunlight down from a node.
748 Starting point gets sunlight.
750 Returns the lowest y value of where the sunlight went.
752 s16 Map::propagateSunlight(v3s16 start,
753 core::map<v3s16, MapBlock*> & modified_blocks)
758 v3s16 pos(start.X, y, start.Z);
760 v3s16 blockpos = getNodeBlockPos(pos);
763 block = getBlockNoCreate(blockpos);
765 catch(InvalidPositionException &e)
770 v3s16 relpos = pos - blockpos*MAP_BLOCKSIZE;
771 MapNode n = block->getNode(relpos);
773 if(n.sunlight_propagates())
775 n.setLight(LIGHT_SUN);
776 block->setNode(relpos, n);
778 modified_blocks.insert(blockpos, block);
787 void Map::updateLighting(core::map<v3s16, MapBlock*> & a_blocks,
788 core::map<v3s16, MapBlock*> & modified_blocks)
790 /*m_dout<<DTIME<<"Map::updateLighting(): "
791 <<a_blocks.getSize()<<" blocks... ";*/
795 u32 count_was = modified_blocks.size();
797 /*core::list<MapBlock *>::Iterator i = a_blocks.begin();
798 for(; i != a_blocks.end(); i++)
800 MapBlock *block = *i;*/
802 core::map<v3s16, bool> light_sources;
804 core::map<v3s16, u8> unlight_from;
806 core::map<v3s16, MapBlock*>::Iterator i;
807 i = a_blocks.getIterator();
808 for(; i.atEnd() == false; i++)
810 MapBlock *block = i.getNode()->getValue();
814 // Don't bother with dummy blocks.
818 v3s16 pos = block->getPos();
819 modified_blocks.insert(pos, block);
822 Clear all light from block
824 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
825 for(s16 x=0; x<MAP_BLOCKSIZE; x++)
826 for(s16 y=0; y<MAP_BLOCKSIZE; y++)
831 MapNode n = block->getNode(v3s16(x,y,z));
832 u8 oldlight = n.getLight();
834 block->setNode(v3s16(x,y,z), n);
836 // Collect borders for unlighting
837 if(x==0 || x == MAP_BLOCKSIZE-1
838 || y==0 || y == MAP_BLOCKSIZE-1
839 || z==0 || z == MAP_BLOCKSIZE-1)
841 v3s16 p_map = p + v3s16(
844 MAP_BLOCKSIZE*pos.Z);
845 unlight_from.insert(p_map, oldlight);
848 catch(InvalidPositionException &e)
851 This would happen when dealing with a
855 dstream<<"updateLighting(): InvalidPositionException"
860 bool bottom_valid = block->propagateSunlight(light_sources);
862 // If bottom is valid, we're done.
866 /*dstream<<"Bottom for sunlight-propagated block ("
867 <<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
870 // Else get the block below and loop to it
874 block = getBlockNoCreate(pos);
876 catch(InvalidPositionException &e)
885 //TimeTaker timer("unspreadLight", g_device);
886 unspreadLight(unlight_from, light_sources, modified_blocks);
891 u32 diff = modified_blocks.size() - count_was;
892 count_was = modified_blocks.size();
893 dstream<<"unspreadLight modified "<<diff<<std::endl;
896 // TODO: Spread light from propagated sunlight?
897 // Yes, add it to light_sources... somehow.
898 // It has to be added at somewhere above, in the loop.
902 //TimeTaker timer("spreadLight", g_device);
903 spreadLight(light_sources, modified_blocks);
908 u32 diff = modified_blocks.size() - count_was;
909 count_was = modified_blocks.size();
910 dstream<<"spreadLight modified "<<diff<<std::endl;
913 //m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
917 This is called after changing a node from transparent to opaque.
918 The lighting value of the node should be left as-is after changing
919 other values. This sets the lighting value to 0.
921 /*void Map::nodeAddedUpdate(v3s16 p, u8 lightwas,
922 core::map<v3s16, MapBlock*> &modified_blocks)*/
923 void Map::addNodeAndUpdate(v3s16 p, MapNode n,
924 core::map<v3s16, MapBlock*> &modified_blocks)
927 m_dout<<DTIME<<"Map::nodeAddedUpdate(): p=("
928 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
930 u8 lightwas = getNode(p).getLight();
932 //core::list<v3s16> light_sources;
933 core::map<v3s16, bool> light_sources;
934 //MapNode n = getNode(p);
937 From this node to nodes underneath:
938 If lighting is sunlight (1.0), unlight neighbours and
943 bool node_under_sunlight = true;
945 v3s16 toppos = p + v3s16(0,1,0);
948 If there is a node at top and it doesn't have sunlight,
949 there has not been any sunlight going down.
951 Otherwise there probably is.
954 MapNode topnode = getNode(toppos);
956 if(topnode.getLight() != LIGHT_SUN)
957 node_under_sunlight = false;
959 catch(InvalidPositionException &e)
963 // Add the block of the added node to modified_blocks
964 v3s16 blockpos = getNodeBlockPos(p);
965 MapBlock * block = getBlockNoCreate(blockpos);
966 assert(block != NULL);
967 modified_blocks.insert(blockpos, block);
969 if(isValidPosition(p) == false)
972 // Unlight neighbours of node.
973 // This means setting light of all consequent dimmer nodes
975 // This also collects the nodes at the border which will spread
976 // light again into this.
977 unLightNeighbors(p, lightwas, light_sources, modified_blocks);
983 If node is under sunlight, take all sunlighted nodes under
984 it and clear light from them and from where the light has
987 if(node_under_sunlight)
991 //m_dout<<DTIME<<"y="<<y<<std::endl;
992 v3s16 n2pos(p.X, y, p.Z);
998 catch(InvalidPositionException &e)
1003 if(n2.getLight() == LIGHT_SUN)
1005 //m_dout<<DTIME<<"doing"<<std::endl;
1006 unLightNeighbors(n2pos, n2.getLight(), light_sources, modified_blocks);
1016 Spread light from all nodes that might be capable of doing so
1017 TODO: Convert to spreadLight
1019 spreadLight(light_sources, modified_blocks);
1024 void Map::removeNodeAndUpdate(v3s16 p,
1025 core::map<v3s16, MapBlock*> &modified_blocks)
1027 /*PrintInfo(m_dout);
1028 m_dout<<DTIME<<"Map::removeNodeAndUpdate(): p=("
1029 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
1031 bool node_under_sunlight = true;
1033 v3s16 toppos = p + v3s16(0,1,0);
1036 If there is a node at top and it doesn't have sunlight,
1037 there will be no sunlight going down.
1040 MapNode topnode = getNode(toppos);
1042 if(topnode.getLight() != LIGHT_SUN)
1043 node_under_sunlight = false;
1045 catch(InvalidPositionException &e)
1050 Unlight neighbors (in case the node is a light source)
1052 //core::list<v3s16> light_sources;
1053 core::map<v3s16, bool> light_sources;
1054 unLightNeighbors(p, getNode(p).getLight(),
1055 light_sources, modified_blocks);
1066 Recalculate lighting
1068 spreadLight(light_sources, modified_blocks);
1070 // Add the block of the removed node to modified_blocks
1071 v3s16 blockpos = getNodeBlockPos(p);
1072 MapBlock * block = getBlockNoCreate(blockpos);
1073 assert(block != NULL);
1074 modified_blocks.insert(blockpos, block);
1077 If the removed node was under sunlight, propagate the
1078 sunlight down from it and then light all neighbors
1079 of the propagated blocks.
1081 if(node_under_sunlight)
1083 s16 ybottom = propagateSunlight(p, modified_blocks);
1084 /*m_dout<<DTIME<<"Node was under sunlight. "
1085 "Propagating sunlight";
1086 m_dout<<DTIME<<" -> ybottom="<<ybottom<<std::endl;*/
1088 for(; y >= ybottom; y--)
1090 v3s16 p2(p.X, y, p.Z);
1091 /*m_dout<<DTIME<<"lighting neighbors of node ("
1092 <<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1094 lightNeighbors(p2, modified_blocks);
1099 // Set the lighting of this node to 0
1101 MapNode n = getNode(p);
1105 catch(InvalidPositionException &e)
1111 // Get the brightest neighbour node and propagate light from it
1112 v3s16 n2p = getBrightestNeighbour(p);
1114 MapNode n2 = getNode(n2p);
1115 lightNeighbors(n2p, modified_blocks);
1117 catch(InvalidPositionException &e)
1122 void Map::updateMeshes(v3s16 blockpos)
1124 assert(mapType() == MAPTYPE_CLIENT);
1127 v3s16 p = blockpos + v3s16(0,0,0);
1128 MapBlock *b = getBlockNoCreate(p);
1131 catch(InvalidPositionException &e){}
1133 v3s16 p = blockpos + v3s16(-1,0,0);
1134 MapBlock *b = getBlockNoCreate(p);
1137 catch(InvalidPositionException &e){}
1139 v3s16 p = blockpos + v3s16(0,-1,0);
1140 MapBlock *b = getBlockNoCreate(p);
1143 catch(InvalidPositionException &e){}
1145 v3s16 p = blockpos + v3s16(0,0,-1);
1146 MapBlock *b = getBlockNoCreate(p);
1149 catch(InvalidPositionException &e){}
1153 Updates usage timers
1155 void Map::timerUpdate(float dtime)
1157 JMutexAutoLock lock(m_sector_mutex);
1159 core::map<v2s16, MapSector*>::Iterator si;
1161 si = m_sectors.getIterator();
1162 for(; si.atEnd() == false; si++)
1164 MapSector *sector = si.getNode()->getValue();
1165 sector->usage_timer += dtime;
1169 void Map::deleteSectors(core::list<v2s16> &list, bool only_blocks)
1172 Wait for caches to be removed before continuing.
1174 This disables the existence of caches while locked
1176 SharedPtr<JMutexAutoLock> cachelock(m_blockcachelock.waitCaches());
1178 core::list<v2s16>::Iterator j;
1179 for(j=list.begin(); j!=list.end(); j++)
1181 MapSector *sector = m_sectors[*j];
1184 sector->deleteBlocks();
1189 If sector is in sector cache, remove it from there
1191 if(m_sector_cache == sector)
1193 m_sector_cache = NULL;
1196 Remove from map and delete
1198 m_sectors.remove(*j);
1204 u32 Map::deleteUnusedSectors(float timeout, bool only_blocks,
1205 core::list<v3s16> *deleted_blocks)
1207 JMutexAutoLock lock(m_sector_mutex);
1209 core::list<v2s16> sector_deletion_queue;
1210 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
1211 for(; i.atEnd() == false; i++)
1213 MapSector *sector = i.getNode()->getValue();
1215 Delete sector from memory if it hasn't been used in a long time
1217 if(sector->usage_timer > timeout)
1219 sector_deletion_queue.push_back(i.getNode()->getKey());
1221 if(deleted_blocks != NULL)
1223 // Collect positions of blocks of sector
1224 MapSector *sector = i.getNode()->getValue();
1225 core::list<MapBlock*> blocks;
1226 sector->getBlocks(blocks);
1227 for(core::list<MapBlock*>::Iterator i = blocks.begin();
1228 i != blocks.end(); i++)
1230 deleted_blocks->push_back((*i)->getPos());
1235 deleteSectors(sector_deletion_queue, only_blocks);
1236 return sector_deletion_queue.getSize();
1239 void Map::PrintInfo(std::ostream &out)
1248 ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
1252 m_savedir = savedir;
1253 m_map_saving_enabled = false;
1257 // If directory exists, check contents and load if possible
1258 if(fs::PathExists(m_savedir))
1260 // If directory is empty, it is safe to save into it.
1261 if(fs::GetDirListing(m_savedir).size() == 0)
1263 dstream<<DTIME<<"Server: Empty save directory is valid."
1265 m_map_saving_enabled = true;
1269 // Load master heightmap
1270 loadMasterHeightmap();
1272 // Load sector (0,0) and throw and exception on fail
1273 if(loadSectorFull(v2s16(0,0)) == false)
1274 throw LoadError("Failed to load sector (0,0)");
1276 dstream<<DTIME<<"Server: Successfully loaded master "
1277 "heightmap and sector (0,0) from "<<savedir<<
1278 ", assuming valid save directory."
1281 m_map_saving_enabled = true;
1282 // Map loaded, not creating new one
1286 // If directory doesn't exist, it is safe to save to it
1288 m_map_saving_enabled = true;
1291 catch(std::exception &e)
1293 dstream<<DTIME<<"Server: Failed to load map from "<<savedir
1294 <<", exception: "<<e.what()<<std::endl;
1295 dstream<<DTIME<<"Please remove the map or fix it."<<std::endl;
1296 dstream<<DTIME<<"WARNING: Map saving will be disabled."<<std::endl;
1299 dstream<<DTIME<<"Initializing new map."<<std::endl;
1301 // Create master heightmap
1302 ValueGenerator *maxgen =
1303 ValueGenerator::deSerialize(hmp.randmax);
1304 ValueGenerator *factorgen =
1305 ValueGenerator::deSerialize(hmp.randfactor);
1306 ValueGenerator *basegen =
1307 ValueGenerator::deSerialize(hmp.base);
1308 m_heightmap = new UnlimitedHeightmap
1309 (hmp.blocksize, maxgen, factorgen, basegen);
1311 // Set map parameters
1314 // Create zero sector
1315 emergeSector(v2s16(0,0));
1317 // Initially write whole map
1321 ServerMap::~ServerMap()
1325 if(m_map_saving_enabled)
1328 // Save only changed parts
1330 dstream<<DTIME<<"Server: saved map to "<<m_savedir<<std::endl;
1334 dstream<<DTIME<<"Server: map not saved"<<std::endl;
1337 catch(std::exception &e)
1339 dstream<<DTIME<<"Server: Failed to save map to "<<m_savedir
1340 <<", exception: "<<e.what()<<std::endl;
1343 if(m_heightmap != NULL)
1347 MapSector * ServerMap::emergeSector(v2s16 p2d)
1349 DSTACK("%s: p2d=(%d,%d)",
1352 // Check that it doesn't exist already
1354 return getSectorNoGenerate(p2d);
1356 catch(InvalidPositionException &e)
1361 Try to load the sector from disk.
1363 if(loadSectorFull(p2d) == true)
1365 return getSectorNoGenerate(p2d);
1369 If there is no master heightmap, throw.
1371 if(m_heightmap == NULL)
1373 throw InvalidPositionException("emergeSector(): no heightmap");
1377 Do not generate over-limit
1379 if(p2d.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1380 || p2d.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1381 || p2d.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
1382 || p2d.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
1383 throw InvalidPositionException("emergeSector(): pos. over limit");
1386 Generate sector and heightmaps
1389 // Number of heightmaps in sector in each direction
1390 u16 hm_split = SECTOR_HEIGHTMAP_SPLIT;
1392 // Heightmap side width
1393 s16 hm_d = MAP_BLOCKSIZE / hm_split;
1395 ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
1397 /*dstream<<"Generating sector ("<<p2d.X<<","<<p2d.Y<<")"
1398 " heightmaps and objects"<<std::endl;*/
1400 // Loop through sub-heightmaps
1401 for(s16 y=0; y<hm_split; y++)
1402 for(s16 x=0; x<hm_split; x++)
1404 v2s16 p_in_sector = v2s16(x,y);
1405 v2s16 mhm_p = p2d * hm_split + p_in_sector;
1407 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)),
1408 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)),
1409 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)),
1410 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)),
1413 /*dstream<<"p_in_sector=("<<p_in_sector.X<<","<<p_in_sector.Y<<")"
1414 <<" mhm_p=("<<mhm_p.X<<","<<mhm_p.Y<<")"
1417 FixedHeightmap *hm = new FixedHeightmap(&m_hwrapper,
1419 sector->setHeightmap(p_in_sector, hm);
1421 //TODO: Make these values configurable
1422 hm->generateContinued(1.0, 0.2, corners);
1423 //hm->generateContinued(2.0, 0.2, corners);
1433 core::map<v3s16, u8> *objects = new core::map<v3s16, u8>;
1434 sector->setObjects(objects);
1436 v2s16 mhm_p = p2d * hm_split;
1438 m_heightmap->getGroundHeight(mhm_p+v2s16(0,0)*hm_split),
1439 m_heightmap->getGroundHeight(mhm_p+v2s16(1,0)*hm_split),
1440 m_heightmap->getGroundHeight(mhm_p+v2s16(1,1)*hm_split),
1441 m_heightmap->getGroundHeight(mhm_p+v2s16(0,1)*hm_split),
1444 float avgheight = (corners[0]+corners[1]+corners[2]+corners[3])/4.0;
1445 float avgslope = 0.0;
1446 avgslope += fabs(avgheight - corners[0]);
1447 avgslope += fabs(avgheight - corners[1]);
1448 avgslope += fabs(avgheight - corners[2]);
1449 avgslope += fabs(avgheight - corners[3]);
1451 avgslope /= MAP_BLOCKSIZE;
1452 //dstream<<"avgslope="<<avgslope<<std::endl;
1454 float pitness = 0.0;
1456 a = m_heightmap->getSlope(p2d+v2s16(0,0));
1459 a = m_heightmap->getSlope(p2d+v2s16(0,1));
1462 a = m_heightmap->getSlope(p2d+v2s16(1,1));
1465 a = m_heightmap->getSlope(p2d+v2s16(1,0));
1469 pitness /= MAP_BLOCKSIZE;
1470 //dstream<<"pitness="<<pitness<<std::endl;
1473 Plant some trees if there is not much slope
1476 // Avgslope is the derivative of a hill
1477 float t = avgslope * avgslope;
1478 float a = MAP_BLOCKSIZE * m_params.plants_amount;
1481 tree_max = a / (t/0.03);
1484 u32 count = (rand()%(tree_max+1));
1485 //u32 count = tree_max;
1486 for(u32 i=0; i<count; i++)
1488 s16 x = (rand()%(MAP_BLOCKSIZE-2))+1;
1489 s16 z = (rand()%(MAP_BLOCKSIZE-2))+1;
1490 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1493 objects->insert(v3s16(x, y, z),
1494 SECTOR_OBJECT_TREE_1);
1498 Plant some bushes if sector is pit-like
1501 // Pitness usually goes at around -0.5...0.5
1503 u32 a = MAP_BLOCKSIZE * 3.0 * m_params.plants_amount;
1505 bush_max = (pitness*a*4);
1508 u32 count = (rand()%(bush_max+1));
1509 for(u32 i=0; i<count; i++)
1511 s16 x = rand()%(MAP_BLOCKSIZE-0)+0;
1512 s16 z = rand()%(MAP_BLOCKSIZE-0)+0;
1513 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1516 objects->insert(v3s16(x, y, z),
1517 SECTOR_OBJECT_BUSH_1);
1521 Add ravine (randomly)
1523 if(m_params.ravines_amount != 0)
1525 if(rand()%(s32)(10.0 / m_params.ravines_amount) == 0)
1528 s16 x = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1529 s16 z = rand()%(MAP_BLOCKSIZE-s*2-1)+s;
1532 s16 y = sector->getGroundHeight(v2s16(x,z))+1;
1533 objects->insert(v3s16(x, y, z),
1534 SECTOR_OBJECT_RAVINE);
1541 JMutexAutoLock lock(m_sector_mutex);
1542 m_sectors.insert(p2d, sector);
1547 MapBlock * ServerMap::emergeBlock(
1549 bool only_from_disk,
1550 core::map<v3s16, MapBlock*> &changed_blocks,
1551 core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
1554 DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
1556 p.X, p.Y, p.Z, only_from_disk);
1558 /*dstream<<"ServerMap::emergeBlock(): "
1559 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
1560 <<", only_from_disk="<<only_from_disk<<std::endl;*/
1561 v2s16 p2d(p.X, p.Z);
1564 This will create or load a sector if not found in memory.
1565 If block exists on disk, it will be loaded.
1567 NOTE: On old save formats, this will be slow, as it generates
1568 lighting on blocks for them.
1570 ServerMapSector *sector = (ServerMapSector*)emergeSector(p2d);
1571 assert(sector->getId() == MAPSECTOR_SERVER);
1573 // Try to get a block from the sector
1574 MapBlock *block = NULL;
1575 bool not_on_disk = false;
1577 block = sector->getBlockNoCreate(block_y);
1578 if(block->isDummy() == true)
1583 catch(InvalidPositionException &e)
1589 If block was not found on disk and not going to generate a
1590 new one, make sure there is a dummy block in place.
1592 if(not_on_disk && only_from_disk)
1596 // Create dummy block
1597 block = new MapBlock(this, p, true);
1599 // Add block to sector
1600 sector->insertBlock(block);
1606 //dstream<<"Not found on disk, generating."<<std::endl;
1609 Do not generate over-limit
1611 if(blockpos_over_limit(p))
1612 throw InvalidPositionException("emergeBlock(): pos. over limit");
1617 Go on generating the block.
1619 TODO: If a dungeon gets generated so that it's side gets
1620 revealed to the outside air, the lighting should be
1625 If block doesn't exist, create one.
1626 If it exists, it is a dummy. In that case unDummify() it.
1630 block = sector->createBlankBlockNoInsert(block_y);
1634 // Remove the block so that nobody can get a half-generated one.
1635 sector->removeBlock(block);
1636 // Allocate the block to be a proper one.
1640 // Randomize a bit. This makes dungeons.
1641 /*bool low_block_is_empty = false;
1643 low_block_is_empty = true;*/
1646 bool underground_emptiness[ued*ued*ued];
1647 for(s32 i=0; i<ued*ued*ued; i++)
1649 underground_emptiness[i] = ((rand() % 4) == 0);
1652 // This is the basic material of what the visible flat ground
1654 u8 material = MATERIAL_GRASS;
1656 s32 lowest_ground_y = 32767;
1659 //sector->printHeightmaps();
1661 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1662 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1664 //dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
1665 float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
1666 assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
1667 s16 surface_y = surface_y_f;
1668 //avg_ground_y += surface_y;
1669 if(surface_y < lowest_ground_y)
1670 lowest_ground_y = surface_y;
1672 s32 surface_depth = 0;
1674 float slope = sector->getSlope(v2s16(x0,z0)).getLength();
1676 float min_slope = 0.45;
1677 float max_slope = 0.85;
1678 float min_slope_depth = 5.0;
1679 float max_slope_depth = 0;
1680 if(slope < min_slope)
1681 surface_depth = min_slope_depth;
1682 else if(slope > max_slope)
1683 surface_depth = max_slope_depth;
1685 surface_depth = (1.-(slope-min_slope)/max_slope) * min_slope_depth;
1687 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1689 s16 real_y = block_y * MAP_BLOCKSIZE + y0;
1694 NOTE: If there are some man-made structures above the
1695 newly created block, they won't be taken into account.
1697 if(real_y > surface_y)
1698 n.setLight(LIGHT_SUN);
1702 // If node is very low
1703 if(real_y <= surface_y - 7){
1705 if(underground_emptiness[
1706 ued*ued*(z0*ued/MAP_BLOCKSIZE)
1707 +ued*(y0*ued/MAP_BLOCKSIZE)
1708 +(x0*ued/MAP_BLOCKSIZE)])
1714 n.d = MATERIAL_STONE;
1717 // If node is under surface level
1718 else if(real_y <= surface_y - surface_depth)
1719 n.d = MATERIAL_STONE;
1720 // If node is at or under heightmap y
1721 else if(real_y <= surface_y)
1723 // If under water level, it's mud
1724 if(real_y < WATER_LEVEL)
1726 // Else it's the main material
1730 // If node is over heightmap y
1732 // If under water level, it's water
1733 if(real_y < WATER_LEVEL)
1735 n.d = MATERIAL_WATER;
1736 n.setLight(diminish_light(LIGHT_SUN, WATER_LEVEL-real_y+1));
1742 block->setNode(v3s16(x0,y0,z0), n);
1747 Calculate is_underground
1749 // Probably underground if the highest part of block is under lowest
1751 bool is_underground = (block_y+1) * MAP_BLOCKSIZE < lowest_ground_y;
1752 block->setIsUnderground(is_underground);
1755 Force lighting update if underground.
1756 This is needed because of ravines.
1761 lighting_invalidated_blocks[block->getPos()] = block;
1770 s16 underground_level = lowest_ground_y/MAP_BLOCKSIZE - block_y;
1771 for(s16 i=0; i<underground_level*3; i++)
1776 (rand()%(MAP_BLOCKSIZE-2))+1,
1777 (rand()%(MAP_BLOCKSIZE-2))+1,
1778 (rand()%(MAP_BLOCKSIZE-2))+1
1782 n.d = MATERIAL_MESE;
1784 if(is_ground_material(block->getNode(cp).d))
1786 block->setNode(cp, n);
1788 for(u16 i=0; i<26; i++)
1790 if(is_ground_material(block->getNode(cp+g_26dirs[i]).d))
1792 block->setNode(cp+g_26dirs[i], n);
1799 Create a few rats in empty blocks underground
1803 //for(u16 i=0; i<2; i++)
1806 (rand()%(MAP_BLOCKSIZE-2))+1,
1807 (rand()%(MAP_BLOCKSIZE-2))+1,
1808 (rand()%(MAP_BLOCKSIZE-2))+1
1811 // Check that the place is empty
1812 //if(!is_ground_material(block->getNode(cp).d))
1815 RatObject *obj = new RatObject(NULL, -1, intToFloat(cp));
1816 block->addObject(obj);
1822 Add block to sector.
1824 sector->insertBlock(block);
1827 Do some interpolation for dungeons
1832 TimeTaker timer("interpolation", g_device);
1834 MapVoxelManipulator vmanip(this);
1836 v3s16 relpos = block->getPosRelative();
1838 vmanip.interpolate(VoxelArea(relpos-v3s16(1,1,1),
1839 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE+1)));
1840 /*vmanip.interpolate(VoxelArea(relpos,
1841 relpos+v3s16(1,1,1)*(MAP_BLOCKSIZE-1)));*/
1843 core::map<v3s16, MapBlock*> modified_blocks;
1844 vmanip.blitBack(modified_blocks);
1845 dstream<<"blitBack modified "<<modified_blocks.size()
1846 <<" blocks"<<std::endl;
1848 // Add modified blocks to changed_blocks and lighting_invalidated_blocks
1849 for(core::map<v3s16, MapBlock*>::Iterator
1850 i = modified_blocks.getIterator();
1851 i.atEnd() == false; i++)
1853 MapBlock *block = i.getNode()->getValue();
1855 changed_blocks.insert(block->getPos(), block);
1856 //lighting_invalidated_blocks.insert(block->getPos(), block);
1866 // An y-wise container of changed blocks
1867 core::map<s16, MapBlock*> changed_blocks_sector;
1870 Check if any sector's objects can be placed now.
1873 core::map<v3s16, u8> *objects = sector->getObjects();
1874 core::list<v3s16> objects_to_remove;
1875 for(core::map<v3s16, u8>::Iterator i = objects->getIterator();
1876 i.atEnd() == false; i++)
1878 v3s16 p = i.getNode()->getKey();
1880 u8 d = i.getNode()->getValue();
1882 //v3s16 p = p_sector - v3s16(0, block_y*MAP_BLOCKSIZE, 0);
1887 if(d == SECTOR_OBJECT_TEST)
1889 if(sector->isValidArea(p + v3s16(0,0,0),
1890 p + v3s16(0,0,0), &changed_blocks_sector))
1893 n.d = MATERIAL_LIGHT;
1894 sector->setNode(p, n);
1895 objects_to_remove.push_back(p);
1898 else if(d == SECTOR_OBJECT_TREE_1)
1900 v3s16 p_min = p + v3s16(-1,0,-1);
1901 v3s16 p_max = p + v3s16(1,4,1);
1902 if(sector->isValidArea(p_min, p_max,
1903 &changed_blocks_sector))
1906 n.d = MATERIAL_TREE;
1907 sector->setNode(p+v3s16(0,0,0), n);
1908 sector->setNode(p+v3s16(0,1,0), n);
1909 sector->setNode(p+v3s16(0,2,0), n);
1910 sector->setNode(p+v3s16(0,3,0), n);
1912 n.d = MATERIAL_LEAVES;
1914 sector->setNode(p+v3s16(0,4,0), n);
1916 sector->setNode(p+v3s16(-1,4,0), n);
1917 sector->setNode(p+v3s16(1,4,0), n);
1918 sector->setNode(p+v3s16(0,4,-1), n);
1919 sector->setNode(p+v3s16(0,4,1), n);
1920 sector->setNode(p+v3s16(1,4,1), n);
1921 sector->setNode(p+v3s16(-1,4,1), n);
1922 sector->setNode(p+v3s16(-1,4,-1), n);
1923 sector->setNode(p+v3s16(1,4,-1), n);
1925 sector->setNode(p+v3s16(-1,3,0), n);
1926 sector->setNode(p+v3s16(1,3,0), n);
1927 sector->setNode(p+v3s16(0,3,-1), n);
1928 sector->setNode(p+v3s16(0,3,1), n);
1929 sector->setNode(p+v3s16(1,3,1), n);
1930 sector->setNode(p+v3s16(-1,3,1), n);
1931 sector->setNode(p+v3s16(-1,3,-1), n);
1932 sector->setNode(p+v3s16(1,3,-1), n);
1934 objects_to_remove.push_back(p);
1936 // Lighting has to be recalculated for this one.
1937 sector->getBlocksInArea(p_min, p_max,
1938 lighting_invalidated_blocks);
1941 else if(d == SECTOR_OBJECT_BUSH_1)
1943 if(sector->isValidArea(p + v3s16(0,0,0),
1944 p + v3s16(0,0,0), &changed_blocks_sector))
1947 n.d = MATERIAL_LEAVES;
1948 sector->setNode(p+v3s16(0,0,0), n);
1950 objects_to_remove.push_back(p);
1953 else if(d == SECTOR_OBJECT_RAVINE)
1956 v3s16 p_min = p + v3s16(-6,maxdepth,-6);
1957 v3s16 p_max = p + v3s16(6,6,6);
1958 if(sector->isValidArea(p_min, p_max,
1959 &changed_blocks_sector))
1962 n.d = MATERIAL_STONE;
1964 n2.d = MATERIAL_AIR;
1965 s16 depth = maxdepth + (rand()%10);
1967 s16 minz = -6 - (-2);
1969 for(s16 x=-6; x<=6; x++)
1971 z += -1 + (rand()%3);
1976 for(s16 y=depth+(rand()%2); y<=6; y++)
1978 /*std::cout<<"("<<p2.X<<","<<p2.Y<<","<<p2.Z<<")"
1981 v3s16 p2 = p + v3s16(x,y,z-2);
1982 if(is_ground_material(sector->getNode(p2).d))
1983 sector->setNode(p2, n);
1986 v3s16 p2 = p + v3s16(x,y,z-1);
1987 if(is_ground_material(sector->getNode(p2).d))
1988 sector->setNode(p2, n2);
1991 v3s16 p2 = p + v3s16(x,y,z+0);
1992 if(is_ground_material(sector->getNode(p2).d))
1993 sector->setNode(p2, n2);
1996 v3s16 p2 = p + v3s16(x,y,z+1);
1997 if(is_ground_material(sector->getNode(p2).d))
1998 sector->setNode(p2, n);
2001 //if(sector->getNode(p+v3s16(x,y,z+1)).solidness()==2)
2002 //if(p.Y+y <= sector->getGroundHeight(p2d+v2s16(x,z-2))+0.5)
2006 objects_to_remove.push_back(p);
2008 // Lighting has to be recalculated for this one.
2009 sector->getBlocksInArea(p_min, p_max,
2010 lighting_invalidated_blocks);
2015 dstream<<"ServerMap::emergeBlock(): "
2016 "Invalid heightmap object"
2021 catch(InvalidPositionException &e)
2023 dstream<<"WARNING: "<<__FUNCTION_NAME
2024 <<": while inserting object "<<(int)d
2025 <<" to ("<<p.X<<","<<p.Y<<","<<p.Z<<"):"
2026 <<" InvalidPositionException.what()="
2027 <<e.what()<<std::endl;
2028 // This is not too fatal and seems to happen sometimes.
2033 for(core::list<v3s16>::Iterator i = objects_to_remove.begin();
2034 i != objects_to_remove.end(); i++)
2036 objects->remove(*i);
2039 for(core::map<s16, MapBlock*>::Iterator
2040 i = changed_blocks_sector.getIterator();
2041 i.atEnd() == false; i++)
2043 MapBlock *block = i.getNode()->getValue();
2045 changed_blocks.insert(block->getPos(), block);
2051 void ServerMap::createDir(std::string path)
2053 if(fs::CreateDir(path) == false)
2055 m_dout<<DTIME<<"ServerMap: Failed to create directory "
2056 <<"\""<<path<<"\""<<std::endl;
2057 throw BaseException("ServerMap failed to create directory");
2061 std::string ServerMap::getSectorSubDir(v2s16 pos)
2064 snprintf(cc, 9, "%.4x%.4x",
2065 (unsigned int)pos.X&0xffff,
2066 (unsigned int)pos.Y&0xffff);
2068 return std::string(cc);
2071 std::string ServerMap::getSectorDir(v2s16 pos)
2073 return m_savedir + "/sectors/" + getSectorSubDir(pos);
2076 v2s16 ServerMap::getSectorPos(std::string dirname)
2078 if(dirname.size() != 8)
2079 throw InvalidFilenameException("Invalid sector directory name");
2081 int r = sscanf(dirname.c_str(), "%4x%4x", &x, &y);
2083 throw InvalidFilenameException("Invalid sector directory name");
2084 v2s16 pos((s16)x, (s16)y);
2088 v3s16 ServerMap::getBlockPos(std::string sectordir, std::string blockfile)
2090 v2s16 p2d = getSectorPos(sectordir);
2092 if(blockfile.size() != 4){
2093 throw InvalidFilenameException("Invalid block filename");
2096 int r = sscanf(blockfile.c_str(), "%4x", &y);
2098 throw InvalidFilenameException("Invalid block filename");
2099 return v3s16(p2d.X, y, p2d.Y);
2103 #define ENABLE_SECTOR_SAVING 1
2104 #define ENABLE_SECTOR_LOADING 1
2105 #define ENABLE_BLOCK_SAVING 1
2106 #define ENABLE_BLOCK_LOADING 1
2108 void ServerMap::save(bool only_changed)
2110 DSTACK(__FUNCTION_NAME);
2111 if(m_map_saving_enabled == false)
2113 dstream<<DTIME<<"WARNING: Not saving map, saving disabled."<<std::endl;
2117 if(only_changed == false)
2118 dstream<<DTIME<<"ServerMap: Saving whole map, this can take time."
2121 saveMasterHeightmap();
2123 u32 sector_meta_count = 0;
2124 u32 block_count = 0;
2127 JMutexAutoLock lock(m_sector_mutex);
2129 core::map<v2s16, MapSector*>::Iterator i = m_sectors.getIterator();
2130 for(; i.atEnd() == false; i++)
2132 ServerMapSector *sector = (ServerMapSector*)i.getNode()->getValue();
2133 assert(sector->getId() == MAPSECTOR_SERVER);
2135 if(ENABLE_SECTOR_SAVING)
2137 if(sector->differs_from_disk || only_changed == false)
2139 saveSectorMeta(sector);
2140 sector_meta_count++;
2143 if(ENABLE_BLOCK_SAVING)
2145 core::list<MapBlock*> blocks;
2146 sector->getBlocks(blocks);
2147 core::list<MapBlock*>::Iterator j;
2148 for(j=blocks.begin(); j!=blocks.end(); j++)
2150 MapBlock *block = *j;
2151 if(block->getChangedFlag() || only_changed == false)
2162 u32 deleted_count = 0;
2163 deleted_count = deleteUnusedSectors
2164 (SERVERMAP_DELETE_UNUSED_SECTORS_TIMEOUT);
2167 Only print if something happened or saved whole map
2169 if(only_changed == false || sector_meta_count != 0
2170 || block_count != 0 || deleted_count != 0)
2172 dstream<<DTIME<<"ServerMap: Written: "
2173 <<sector_meta_count<<" sector metadata files, "
2174 <<block_count<<" block files, "
2175 <<deleted_count<<" sectors unloaded from memory."
2180 void ServerMap::loadAll()
2182 DSTACK(__FUNCTION_NAME);
2183 dstream<<DTIME<<"ServerMap: Loading map..."<<std::endl;
2185 loadMasterHeightmap();
2187 std::vector<fs::DirListNode> list = fs::GetDirListing(m_savedir+"/sectors/");
2189 dstream<<DTIME<<"There are "<<list.size()<<" sectors."<<std::endl;
2191 JMutexAutoLock lock(m_sector_mutex);
2194 s32 printed_counter = -100000;
2195 s32 count = list.size();
2197 std::vector<fs::DirListNode>::iterator i;
2198 for(i=list.begin(); i!=list.end(); i++)
2200 if(counter > printed_counter + 10)
2202 dstream<<DTIME<<counter<<"/"<<count<<std::endl;
2203 printed_counter = counter;
2207 MapSector *sector = NULL;
2209 // We want directories
2213 sector = loadSectorMeta(i->name);
2215 catch(InvalidFilenameException &e)
2217 // This catches unknown crap in directory
2220 if(ENABLE_BLOCK_LOADING)
2222 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2223 (m_savedir+"/sectors/"+i->name);
2224 std::vector<fs::DirListNode>::iterator i2;
2225 for(i2=list2.begin(); i2!=list2.end(); i2++)
2231 loadBlock(i->name, i2->name, sector);
2233 catch(InvalidFilenameException &e)
2235 // This catches unknown crap in directory
2240 dstream<<DTIME<<"ServerMap: Map loaded."<<std::endl;
2243 void ServerMap::saveMasterHeightmap()
2245 DSTACK(__FUNCTION_NAME);
2246 createDir(m_savedir);
2248 std::string fullpath = m_savedir + "/master_heightmap";
2249 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2250 if(o.good() == false)
2251 throw FileNotGoodException("Cannot open master heightmap");
2253 // Format used for writing
2254 u8 version = SER_FMT_VER_HIGHEST;
2257 SharedBuffer<u8> hmdata = m_heightmap->serialize(version);
2259 [0] u8 serialization version
2260 [1] X master heightmap
2262 u32 fullsize = 1 + hmdata.getSize();
2263 SharedBuffer<u8> data(fullsize);
2266 memcpy(&data[1], *hmdata, hmdata.getSize());
2268 o.write((const char*)*data, fullsize);
2271 m_heightmap->serialize(o, version);
2274 void ServerMap::loadMasterHeightmap()
2276 DSTACK(__FUNCTION_NAME);
2277 std::string fullpath = m_savedir + "/master_heightmap";
2278 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2279 if(is.good() == false)
2280 throw FileNotGoodException("Cannot open master heightmap");
2282 if(m_heightmap != NULL)
2285 m_heightmap = UnlimitedHeightmap::deSerialize(is);
2288 void ServerMap::saveSectorMeta(ServerMapSector *sector)
2290 DSTACK(__FUNCTION_NAME);
2291 // Format used for writing
2292 u8 version = SER_FMT_VER_HIGHEST;
2294 v2s16 pos = sector->getPos();
2295 createDir(m_savedir);
2296 createDir(m_savedir+"/sectors");
2297 std::string dir = getSectorDir(pos);
2300 std::string fullpath = dir + "/heightmap";
2301 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2302 if(o.good() == false)
2303 throw FileNotGoodException("Cannot open master heightmap");
2305 sector->serialize(o, version);
2307 sector->differs_from_disk = false;
2310 MapSector* ServerMap::loadSectorMeta(std::string dirname)
2312 DSTACK(__FUNCTION_NAME);
2314 v2s16 p2d = getSectorPos(dirname);
2315 std::string dir = m_savedir + "/sectors/" + dirname;
2317 std::string fullpath = dir + "/heightmap";
2318 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2319 if(is.good() == false)
2320 throw FileNotGoodException("Cannot open sector heightmap");
2322 ServerMapSector *sector = ServerMapSector::deSerialize
2323 (is, this, p2d, &m_hwrapper, m_sectors);
2325 sector->differs_from_disk = false;
2330 bool ServerMap::loadSectorFull(v2s16 p2d)
2332 DSTACK(__FUNCTION_NAME);
2333 std::string sectorsubdir = getSectorSubDir(p2d);
2335 MapSector *sector = NULL;
2337 JMutexAutoLock lock(m_sector_mutex);
2340 sector = loadSectorMeta(sectorsubdir);
2342 catch(InvalidFilenameException &e)
2346 catch(FileNotGoodException &e)
2350 catch(std::exception &e)
2355 if(ENABLE_BLOCK_LOADING)
2357 std::vector<fs::DirListNode> list2 = fs::GetDirListing
2358 (m_savedir+"/sectors/"+sectorsubdir);
2359 std::vector<fs::DirListNode>::iterator i2;
2360 for(i2=list2.begin(); i2!=list2.end(); i2++)
2366 loadBlock(sectorsubdir, i2->name, sector);
2368 catch(InvalidFilenameException &e)
2370 // This catches unknown crap in directory
2378 bool ServerMap::deFlushSector(v2s16 p2d)
2380 DSTACK(__FUNCTION_NAME);
2381 // See if it already exists in memory
2383 MapSector *sector = getSectorNoGenerate(p2d);
2386 catch(InvalidPositionException &e)
2389 Try to load the sector from disk.
2391 if(loadSectorFull(p2d) == true)
2400 void ServerMap::saveBlock(MapBlock *block)
2402 DSTACK(__FUNCTION_NAME);
2404 Dummy blocks are not written
2406 if(block->isDummy())
2408 /*v3s16 p = block->getPos();
2409 dstream<<"ServerMap::saveBlock(): WARNING: Not writing dummy block "
2410 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2414 // Format used for writing
2415 u8 version = SER_FMT_VER_HIGHEST;
2417 v3s16 p3d = block->getPos();
2418 v2s16 p2d(p3d.X, p3d.Z);
2419 createDir(m_savedir);
2420 createDir(m_savedir+"/sectors");
2421 std::string dir = getSectorDir(p2d);
2424 // Block file is map/sectors/xxxxxxxx/xxxx
2426 snprintf(cc, 5, "%.4x", (unsigned int)p3d.Y&0xffff);
2427 std::string fullpath = dir + "/" + cc;
2428 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
2429 if(o.good() == false)
2430 throw FileNotGoodException("Cannot open block data");
2433 [0] u8 serialization version
2436 o.write((char*)&version, 1);
2438 block->serialize(o, version);
2441 Versions up from 9 have block objects.
2445 block->serializeObjects(o, version);
2448 // We just wrote it to the disk
2449 block->resetChangedFlag();
2452 void ServerMap::loadBlock(std::string sectordir, std::string blockfile, MapSector *sector)
2454 DSTACK(__FUNCTION_NAME);
2455 // Block file is map/sectors/xxxxxxxx/xxxx
2456 std::string fullpath = m_savedir+"/sectors/"+sectordir+"/"+blockfile;
2457 std::ifstream is(fullpath.c_str(), std::ios_base::binary);
2458 if(is.good() == false)
2459 throw FileNotGoodException("Cannot open block file");
2461 v3s16 p3d = getBlockPos(sectordir, blockfile);
2462 v2s16 p2d(p3d.X, p3d.Z);
2464 assert(sector->getPos() == p2d);
2466 u8 version = SER_FMT_VER_INVALID;
2467 is.read((char*)&version, 1);
2469 /*u32 block_size = MapBlock::serializedLength(version);
2470 SharedBuffer<u8> data(block_size);
2471 is.read((char*)*data, block_size);*/
2473 // This will always return a sector because we're the server
2474 //MapSector *sector = emergeSector(p2d);
2476 MapBlock *block = NULL;
2477 bool created_new = false;
2479 block = sector->getBlockNoCreate(p3d.Y);
2481 catch(InvalidPositionException &e)
2483 block = sector->createBlankBlockNoInsert(p3d.Y);
2487 block->deSerialize(is, version);
2490 Versions up from 9 have block objects.
2494 block->updateObjects(is, version, NULL);
2498 sector->insertBlock(block);
2501 Convert old formats to new and save
2504 // Save old format blocks in new format
2505 if(version < SER_FMT_VER_HIGHEST)
2510 // We just loaded it from the disk, so it's up-to-date.
2511 block->resetChangedFlag();
2514 // Gets from master heightmap
2515 void ServerMap::getSectorCorners(v2s16 p2d, s16 *corners)
2517 assert(m_heightmap != NULL);
2525 corners[0] = m_heightmap->getGroundHeight
2526 ((p2d+v2s16(0,0))*SECTOR_HEIGHTMAP_SPLIT);
2527 corners[1] = m_heightmap->getGroundHeight
2528 ((p2d+v2s16(1,0))*SECTOR_HEIGHTMAP_SPLIT);
2529 corners[2] = m_heightmap->getGroundHeight
2530 ((p2d+v2s16(1,1))*SECTOR_HEIGHTMAP_SPLIT);
2531 corners[3] = m_heightmap->getGroundHeight
2532 ((p2d+v2s16(0,1))*SECTOR_HEIGHTMAP_SPLIT);
2535 void ServerMap::PrintInfo(std::ostream &out)
2544 ClientMap::ClientMap(
2546 video::SMaterial *materials,
2547 scene::ISceneNode* parent,
2548 scene::ISceneManager* mgr,
2552 scene::ISceneNode(parent, mgr, id),
2554 m_materials(materials),
2557 /*m_box = core::aabbox3d<f32>(0,0,0,
2558 map->getW()*BS, map->getH()*BS, map->getD()*BS);*/
2559 /*m_box = core::aabbox3d<f32>(0,0,0,
2560 map->getSizeNodes().X * BS,
2561 map->getSizeNodes().Y * BS,
2562 map->getSizeNodes().Z * BS);*/
2563 m_box = core::aabbox3d<f32>(-BS*1000000,-BS*1000000,-BS*1000000,
2564 BS*1000000,BS*1000000,BS*1000000);
2569 ClientMap::~ClientMap()
2571 JMutexAutoLock lock(mesh_mutex);
2580 MapSector * ClientMap::emergeSector(v2s16 p2d)
2582 DSTACK(__FUNCTION_NAME);
2583 // Check that it doesn't exist already
2585 return getSectorNoGenerate(p2d);
2587 catch(InvalidPositionException &e)
2591 // Create a sector with no heightmaps
2592 ClientMapSector *sector = new ClientMapSector(this, p2d);
2595 JMutexAutoLock lock(m_sector_mutex);
2596 m_sectors.insert(p2d, sector);
2602 void ClientMap::deSerializeSector(v2s16 p2d, std::istream &is)
2604 DSTACK(__FUNCTION_NAME);
2605 ClientMapSector *sector = NULL;
2607 JMutexAutoLock lock(m_sector_mutex);
2609 core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p2d);
2613 sector = (ClientMapSector*)n->getValue();
2614 assert(sector->getId() == MAPSECTOR_CLIENT);
2618 sector = new ClientMapSector(this, p2d);
2620 JMutexAutoLock lock(m_sector_mutex);
2621 m_sectors.insert(p2d, sector);
2625 sector->deSerialize(is);
2628 void ClientMap::renderMap(video::IVideoDriver* driver,
2629 video::SMaterial *materials, s32 pass)
2631 //m_dout<<DTIME<<"Rendering map..."<<std::endl;
2632 DSTACK(__FUNCTION_NAME);
2634 bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT;
2637 Draw master heightmap mesh
2641 JMutexAutoLock lock(mesh_mutex);
2644 u32 c = mesh->getMeshBufferCount();
2646 for(u32 i=0; i<c; i++)
2648 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
2649 const video::SMaterial& material = buf->getMaterial();
2650 video::IMaterialRenderer* rnd =
2651 driver->getMaterialRenderer(material.MaterialType);
2652 bool transparent = (rnd && rnd->isTransparent());
2653 // Render transparent on transparent pass and likewise.
2654 if(transparent == is_transparent_pass)
2656 driver->setMaterial(buf->getMaterial());
2657 driver->drawMeshBuffer(buf);
2665 Get time for measuring timeout.
2667 Measuring time is very useful for long delays when the
2668 machine is swapping a lot.
2670 int time1 = time(0);
2673 Collect all blocks that are in the view range
2675 Should not optimize more here as we want to auto-update
2676 all changed nodes in viewing range at the next step.
2679 s16 viewing_range_nodes;
2680 bool viewing_range_all;
2682 JMutexAutoLock lock(g_range_mutex);
2683 viewing_range_nodes = g_viewing_range_nodes;
2684 viewing_range_all = g_viewing_range_all;
2687 m_camera_mutex.Lock();
2688 v3f camera_position = m_camera_position;
2689 v3f camera_direction = m_camera_direction;
2690 m_camera_mutex.Unlock();
2693 Get all blocks and draw all visible ones
2696 v3s16 cam_pos_nodes(
2697 camera_position.X / BS,
2698 camera_position.Y / BS,
2699 camera_position.Z / BS);
2701 v3s16 box_nodes_d = viewing_range_nodes * v3s16(1,1,1);
2703 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2704 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2706 // Take a fair amount as we will be dropping more out later
2708 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2709 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2710 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2712 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2713 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2714 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2716 u32 vertex_count = 0;
2718 core::map<v2s16, MapSector*>::Iterator si;
2720 //NOTE: The sectors map should be locked but we're not doing it
2721 // because it'd cause too much delays
2723 si = m_sectors.getIterator();
2724 for(; si.atEnd() == false; si++)
2727 static int timecheck_counter = 0;
2728 timecheck_counter++;
2729 if(timecheck_counter > 50)
2731 int time2 = time(0);
2732 if(time2 > time1 + 4)
2734 dstream<<"ClientMap::renderMap(): "
2735 "Rendering takes ages, returning."
2742 MapSector *sector = si.getNode()->getValue();
2743 v2s16 sp = sector->getPos();
2745 if(viewing_range_all == false)
2747 if(sp.X < p_blocks_min.X
2748 || sp.X > p_blocks_max.X
2749 || sp.Y < p_blocks_min.Z
2750 || sp.Y > p_blocks_max.Z)
2754 core::list< MapBlock * > sectorblocks;
2755 sector->getBlocks(sectorblocks);
2761 core::list< MapBlock * >::Iterator i;
2762 for(i=sectorblocks.begin(); i!=sectorblocks.end(); i++)
2764 MapBlock *block = *i;
2767 Compare block position to camera position, skip
2768 if not seen on display
2771 v3s16 blockpos_nodes = block->getPosRelative();
2773 // Block center position
2775 ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS,
2776 ((float)blockpos_nodes.Y + MAP_BLOCKSIZE/2) * BS,
2777 ((float)blockpos_nodes.Z + MAP_BLOCKSIZE/2) * BS
2780 // Block position relative to camera
2781 v3f blockpos_relative = blockpos - camera_position;
2783 // Distance in camera direction (+=front, -=back)
2784 f32 dforward = blockpos_relative.dotProduct(camera_direction);
2787 f32 d = blockpos_relative.getLength();
2789 if(viewing_range_all == false)
2791 // If block is far away, don't draw it
2792 if(d > viewing_range_nodes * BS)
2796 // Maximum radius of a block
2797 f32 block_max_radius = 0.5*1.44*1.44*MAP_BLOCKSIZE*BS;
2799 // If block is (nearly) touching the camera, don't
2800 // bother validating further (that is, render it anyway)
2801 if(d > block_max_radius * 1.5)
2803 // Cosine of the angle between the camera direction
2804 // and the block direction (camera_direction is an unit vector)
2805 f32 cosangle = dforward / d;
2807 // Compensate for the size of the block
2808 // (as the block has to be shown even if it's a bit off FOV)
2809 // This is an estimate.
2810 cosangle += block_max_radius / dforward;
2812 // If block is not in the field of view, skip it
2813 //if(cosangle < cos(FOV_ANGLE/2))
2814 if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
2819 Draw the faces of the block
2823 JMutexAutoLock lock(block->mesh_mutex);
2825 // Cancel if block has no mesh
2826 if(block->mesh == NULL)
2829 u32 c = block->mesh->getMeshBufferCount();
2831 for(u32 i=0; i<c; i++)
2833 scene::IMeshBuffer *buf = block->mesh->getMeshBuffer(i);
2834 const video::SMaterial& material = buf->getMaterial();
2835 video::IMaterialRenderer* rnd =
2836 driver->getMaterialRenderer(material.MaterialType);
2837 bool transparent = (rnd && rnd->isTransparent());
2838 // Render transparent on transparent pass and likewise.
2839 if(transparent == is_transparent_pass)
2841 driver->setMaterial(buf->getMaterial());
2842 driver->drawMeshBuffer(buf);
2843 vertex_count += buf->getVertexCount();
2847 } // foreach sectorblocks
2850 /*dstream<<"renderMap(): is_transparent_pass="<<is_transparent_pass
2851 <<", rendered "<<vertex_count<<" vertices."<<std::endl;*/
2854 void ClientMap::updateMesh()
2857 DSTACK(__FUNCTION_NAME);
2860 Check what sectors don't draw anything useful at ground level
2861 and create a mesh of the rough heightmap at those positions.
2864 m_camera_mutex.Lock();
2865 v3f camera_position = m_camera_position;
2866 v3f camera_direction = m_camera_direction;
2867 m_camera_mutex.Unlock();
2869 v3s16 cam_pos_nodes(
2870 camera_position.X / BS,
2871 camera_position.Y / BS,
2872 camera_position.Z / BS);
2874 v3s16 box_nodes_d = HEIGHTMAP_RANGE_NODES * v3s16(1,1,1);
2876 v3s16 p_nodes_min = cam_pos_nodes - box_nodes_d;
2877 v3s16 p_nodes_max = cam_pos_nodes + box_nodes_d;
2879 // Take a fair amount as we will be dropping more out later
2881 p_nodes_min.X / MAP_BLOCKSIZE - 1,
2882 p_nodes_min.Y / MAP_BLOCKSIZE - 1,
2883 p_nodes_min.Z / MAP_BLOCKSIZE - 1);
2885 p_nodes_max.X / MAP_BLOCKSIZE + 1,
2886 p_nodes_max.Y / MAP_BLOCKSIZE + 1,
2887 p_nodes_max.Z / MAP_BLOCKSIZE + 1);
2893 scene::SMesh *mesh_new = new scene::SMesh();
2894 //scene::IMeshBuffer *buf = NULL;
2895 scene::SMeshBuffer *buf = NULL;
2897 u8 material_in_use = 0;
2900 Loop through sectors
2903 for(core::map<v2s16, MapSector*>::Iterator
2904 si = m_sectors.getIterator();
2905 si.atEnd() == false; si++)
2907 MapSector *sector = si.getNode()->getValue();
2909 if(sector->getId() != MAPSECTOR_CLIENT)
2911 dstream<<"WARNING: Client has a non-client sector"
2916 ClientMapSector *cs = (ClientMapSector*)sector;
2918 v2s16 sp = sector->getPos();
2920 if(sp.X < p_blocks_min.X
2921 || sp.X > p_blocks_max.X
2922 || sp.Y < p_blocks_min.Z
2923 || sp.Y > p_blocks_max.Z)
2927 Get some ground level info
2939 s16 cn_avg = (cn[0]+cn[1]+cn[2]+cn[3])/4;
2941 s16 cn_max = -32768;
2942 for(s16 i=0; i<4; i++)
2949 s16 cn_slope = cn_max - cn_min;
2952 Generate this part of the heightmap mesh
2956 if(cn_avg + MAP_BLOCKSIZE/4 <= WATER_LEVEL)
2958 else if(cn_slope <= MAP_BLOCKSIZE)
2963 if(material != material_in_use || buf == NULL)
2965 // Try to get a meshbuffer associated with the material
2966 buf = (scene::SMeshBuffer*)mesh_new->getMeshBuffer
2967 (g_mesh_materials[material]);
2968 // If not found, create one
2971 // This is a "Standard MeshBuffer",
2972 // it's a typedeffed CMeshBuffer<video::S3DVertex>
2973 buf = new scene::SMeshBuffer();
2976 buf->Material = g_mesh_materials[material];
2978 //buf->setHardwareMappingHint(scene::EHM_STATIC);
2980 mesh_new->addMeshBuffer(buf);
2984 material_in_use = material;
2987 // Sector side width in floating-point units
2988 f32 sd = BS * MAP_BLOCKSIZE;
2989 // Sector position in global floating-point units
2990 v3f spf = v3f((f32)sp.X, 0, (f32)sp.Y) * sd;
2992 //video::SColor c(255,255,255,255);
2994 video::SColor c(255,cc,cc,cc);
2996 video::S3DVertex vertices[4] =
2998 video::S3DVertex(spf.X, (f32)BS*cn[0],spf.Z, 0,0,0, c, 0,1),
2999 video::S3DVertex(spf.X+sd,(f32)BS*cn[1],spf.Z, 0,0,0, c, 1,1),
3000 video::S3DVertex(spf.X+sd,(f32)BS*cn[2],spf.Z+sd,0,0,0, c, 1,0),
3001 video::S3DVertex(spf.X, (f32)BS*cn[3],spf.Z+sd,0,0,0, c, 0,0),
3003 u16 indices[] = {0,1,2,2,3,0};
3005 buf->append(vertices, 4, indices, 6);
3009 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
3017 scene::SMesh *mesh_old = mesh;
3024 mesh_mutex.Unlock();
3026 if(mesh_old != NULL)
3028 /*dstream<<"mesh_old refcount="<<mesh_old->getReferenceCount()
3030 scene::IMeshBuffer *buf = mesh_new->getMeshBuffer
3031 (g_materials[MATERIAL_GRASS]);
3033 dstream<<"grass buf refcount="<<buf->getReferenceCount()
3040 dstream<<"WARNING: There was no old master heightmap mesh"<<std::endl;
3045 void ClientMap::PrintInfo(std::ostream &out)