encapsulate light recalculation logic inside LightUpdate classes

now we can do a standard light recalculation from anywhere.
This commit is contained in:
Dylan K. Taylor 2019-07-04 19:55:48 +01:00
parent 3d118a415c
commit ceb6529ee3
4 changed files with 69 additions and 34 deletions

View File

@ -1397,39 +1397,10 @@ class World implements ChunkManager{
public function updateBlockSkyLight(int $x, int $y, int $z){
$this->timings->doBlockSkyLightUpdates->startTiming();
$oldHeightMap = $this->getHeightMap($x, $z);
$source = $this->getBlockAt($x, $y, $z);
$yPlusOne = $y + 1;
if($yPlusOne === $oldHeightMap){ //Block changed directly beneath the heightmap. Check if a block was removed or changed to a different light-filter.
$newHeightMap = $this->getChunk($x >> 4, $z >> 4)->recalculateHeightMapColumn($x & 0x0f, $z & 0x0f);
}elseif($yPlusOne > $oldHeightMap){ //Block changed above the heightmap.
if($source->getLightFilter() > 0 or $source->diffusesSkyLight()){
$this->setHeightMap($x, $z, $yPlusOne);
$newHeightMap = $yPlusOne;
}else{ //Block changed which has no effect on direct sky light, for example placing or removing glass.
$this->timings->doBlockSkyLightUpdates->stopTiming();
return;
}
}else{ //Block changed below heightmap
$newHeightMap = $oldHeightMap;
}
if($this->skyLightUpdate === null){
$this->skyLightUpdate = new SkyLightUpdate($this);
}
if($newHeightMap > $oldHeightMap){ //Heightmap increase, block placed, remove sky light
for($i = $y; $i >= $oldHeightMap; --$i){
$this->skyLightUpdate->setAndUpdateLight($x, $i, $z, 0); //Remove all light beneath, adjacent recalculation will handle the rest.
}
}elseif($newHeightMap < $oldHeightMap){ //Heightmap decrease, block changed or removed, add sky light
for($i = $y; $i >= $newHeightMap; --$i){
$this->skyLightUpdate->setAndUpdateLight($x, $i, $z, 15);
}
}else{ //No heightmap change, block changed "underground"
$this->skyLightUpdate->setAndUpdateLight($x, $y, $z, max(0, $this->getHighestAdjacentBlockSkyLight($x, $y, $z) - BlockFactory::$lightFilter[($source->getId() << 4) | $source->getMeta()]));
}
$this->skyLightUpdate->recalculateNode($x, $y, $z);
$this->timings->doBlockSkyLightUpdates->stopTiming();
}
@ -1457,13 +1428,10 @@ class World implements ChunkManager{
public function updateBlockLight(int $x, int $y, int $z){
$this->timings->doBlockLightUpdates->startTiming();
$block = $this->getBlockAt($x, $y, $z);
$newLevel = max($block->getLightLevel(), $this->getHighestAdjacentBlockLight($x, $y, $z) - BlockFactory::$lightFilter[($block->getId() << 4) | $block->getMeta()]);
if($this->blockLightUpdate === null){
$this->blockLightUpdate = new BlockLightUpdate($this);
}
$this->blockLightUpdate->setAndUpdateLight($x, $y, $z, $newLevel);
$this->blockLightUpdate->recalculateNode($x, $y, $z);
$this->timings->doBlockLightUpdates->stopTiming();
}

View File

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace pocketmine\world\light;
use pocketmine\block\BlockFactory;
use function max;
class BlockLightUpdate extends LightUpdate{
public function getLight(int $x, int $y, int $z) : int{
@ -32,4 +35,9 @@ class BlockLightUpdate extends LightUpdate{
public function setLight(int $x, int $y, int $z, int $level) : void{
$this->subChunkHandler->currentSubChunk->setBlockLight($x & 0x0f, $y & 0x0f, $z & 0x0f, $level);
}
public function recalculateNode(int $x, int $y, int $z) : void{
$block = $this->world->getBlockAt($x, $y, $z);
$this->setAndUpdateLight($x, $y, $z, max($block->getLightLevel(), $this->getHighestAdjacentLight($x, $y, $z) - BlockFactory::$lightFilter[$block->getFullId()]));
}
}

View File

@ -27,6 +27,7 @@ use pocketmine\block\BlockFactory;
use pocketmine\world\ChunkManager;
use pocketmine\world\utils\SubChunkIteratorManager;
use pocketmine\world\World;
use function max;
//TODO: make light updates asynchronous
abstract class LightUpdate{
@ -61,6 +62,25 @@ abstract class LightUpdate{
abstract protected function setLight(int $x, int $y, int $z, int $level) : void;
abstract public function recalculateNode(int $x, int $y, int $z) : void;
protected function getHighestAdjacentLight(int $x, int $y, int $z) : int{
$adjacent = 0;
foreach([
[$x + 1, $y, $z],
[$x - 1, $y, $z],
[$x, $y + 1, $z],
[$x, $y - 1, $z],
[$x, $y, $z + 1],
[$x, $y, $z - 1]
] as [$x1, $y1, $z1]){
if($this->subChunkHandler->moveTo($x1, $y1, $z1, false) and ($adjacent = max($adjacent, $this->getLight($x1, $y1, $z1))) === 15){
break;
}
}
return $adjacent;
}
public function setAndUpdateLight(int $x, int $y, int $z, int $newLevel) : void{
$this->updateNodes[World::blockHash($x, $y, $z)] = [$x, $y, $z, $newLevel];
}

View File

@ -23,6 +23,9 @@ declare(strict_types=1);
namespace pocketmine\world\light;
use pocketmine\block\BlockFactory;
use function max;
class SkyLightUpdate extends LightUpdate{
public function getLight(int $x, int $y, int $z) : int{
@ -32,4 +35,40 @@ class SkyLightUpdate extends LightUpdate{
public function setLight(int $x, int $y, int $z, int $level) : void{
$this->subChunkHandler->currentSubChunk->setBlockSkyLight($x & 0x0f, $y & 0x0f, $z & 0x0f, $level);
}
public function recalculateNode(int $x, int $y, int $z) : void{
$chunk = $this->world->getChunk($x >> 4, $z >> 4);
if($chunk === null){
return;
}
$oldHeightMap = $chunk->getHeightMap($x & 0xf, $z & 0xf);
$source = $this->world->getBlockAt($x, $y, $z);
$yPlusOne = $y + 1;
if($yPlusOne === $oldHeightMap){ //Block changed directly beneath the heightmap. Check if a block was removed or changed to a different light-filter.
$newHeightMap = $chunk->recalculateHeightMapColumn($x & 0x0f, $z & 0x0f);
}elseif($yPlusOne > $oldHeightMap){ //Block changed above the heightmap.
if($source->getLightFilter() > 0 or $source->diffusesSkyLight()){
$chunk->setHeightMap($x & 0xf, $z & 0xf, $yPlusOne);
$newHeightMap = $yPlusOne;
}else{ //Block changed which has no effect on direct sky light, for example placing or removing glass.
return;
}
}else{ //Block changed below heightmap
$newHeightMap = $oldHeightMap;
}
if($newHeightMap > $oldHeightMap){ //Heightmap increase, block placed, remove sky light
for($i = $y; $i >= $oldHeightMap; --$i){
$this->setAndUpdateLight($x, $i, $z, 0); //Remove all light beneath, adjacent recalculation will handle the rest.
}
}elseif($newHeightMap < $oldHeightMap){ //Heightmap decrease, block changed or removed, add sky light
for($i = $y; $i >= $newHeightMap; --$i){
$this->setAndUpdateLight($x, $i, $z, 15);
}
}else{ //No heightmap change, block changed "underground"
$this->setAndUpdateLight($x, $y, $z, max(0, $this->getHighestAdjacentLight($x, $y, $z) - BlockFactory::$lightFilter[$source->getFullId()]));
}
}
}