Binary file not shown.
@@ -316,41 +316,8 @@ public class SharedApi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// chunk light baking is disabled since profiling revealed it used
|
// sky lighting is populated later at the data source level
|
||||||
// roughly the same amount of time as generating the lighting ourselves and
|
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
|
||||||
// was much more likely to have issues with corrupt (all black or all bright) chunks
|
|
||||||
boolean tryUsingMcLightingEngine = false;
|
|
||||||
if (tryUsingMcLightingEngine)
|
|
||||||
{
|
|
||||||
// Save or populate the chunk wrapper's lighting
|
|
||||||
// this is done so we don't have to worry about MC unloading the lighting data for this chunk
|
|
||||||
boolean chunkLightPopulated = false;
|
|
||||||
boolean onlyUseDhLighting = Config.Client.Advanced.LodBuilding.onlyUseDhLightingEngine.get();
|
|
||||||
if (!onlyUseDhLighting && chunkWrapper.isLightCorrect())
|
|
||||||
{
|
|
||||||
// If MC's lighting engine isn't thread safe this may cause the server thread to lag
|
|
||||||
chunkLightPopulated = chunkWrapper.bakeDhLightingUsingMcLightingEngine(dhLevel.getLevelWrapper());
|
|
||||||
if (!chunkLightPopulated)
|
|
||||||
{
|
|
||||||
// clear any existing data to prevent partial or corrupt lighting
|
|
||||||
// when re-generating it
|
|
||||||
chunkWrapper.clearDhBlockLighting();
|
|
||||||
chunkWrapper.clearDhSkyLighting();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// something went wrong during the baking process so we have to generate the lighting ourselves
|
|
||||||
if (!chunkLightPopulated)
|
|
||||||
{
|
|
||||||
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dhLevel.updateBeaconBeamsForChunk(chunkWrapper, nearbyChunkList);
|
dhLevel.updateBeaconBeamsForChunk(chunkWrapper, nearbyChunkList);
|
||||||
dhLevel.updateChunkAsync(chunkWrapper, newChunkHash);
|
dhLevel.updateChunkAsync(chunkWrapper, newChunkHash);
|
||||||
|
|||||||
+1
-1
@@ -144,7 +144,7 @@ public class FullDataSourceV2 implements IDataSource<IDhLevel>
|
|||||||
this.columnWorldCompressionMode = columnWorldCompressionMode;
|
this.columnWorldCompressionMode = columnWorldCompressionMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FullDataSourceV2 createFromChunk(IChunkWrapper chunkWrapper) { return LodDataBuilder.createGeneratedDataSource(chunkWrapper); }
|
public static FullDataSourceV2 createFromChunk(IChunkWrapper chunkWrapper) { return LodDataBuilder.createFromChunk(chunkWrapper); }
|
||||||
|
|
||||||
public static FullDataSourceV2 createFromLegacyDataSourceV1(FullDataSourceV1 legacyData)
|
public static FullDataSourceV2 createFromLegacyDataSourceV1(FullDataSourceV1 legacyData)
|
||||||
{
|
{
|
||||||
|
|||||||
+7
-11
@@ -59,12 +59,10 @@ public class LodDataBuilder
|
|||||||
// converters //
|
// converters //
|
||||||
//============//
|
//============//
|
||||||
|
|
||||||
public static FullDataSourceV2 createGeneratedDataSource(IChunkWrapper chunkWrapper)
|
public static FullDataSourceV2 createFromChunk(IChunkWrapper chunkWrapper)
|
||||||
{
|
{
|
||||||
if (!canGenerateLodFromChunk(chunkWrapper))
|
// only block lighting is needed here, sky lighting is populated at the data source stage
|
||||||
{
|
LodUtil.assertTrue(chunkWrapper.isDhBlockLightingCorrect());
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -153,8 +151,8 @@ public class LodDataBuilder
|
|||||||
if (lastY < chunkWrapper.getMaxBuildHeight())
|
if (lastY < chunkWrapper.getMaxBuildHeight())
|
||||||
{
|
{
|
||||||
// FIXME: The lastY +1 offset is to reproduce the old behavior. Remove this when we get per-face lighting
|
// FIXME: The lastY +1 offset is to reproduce the old behavior. Remove this when we get per-face lighting
|
||||||
blockLight = (byte) chunkWrapper.getBlockLight(relBlockX, lastY + 1, relBlockZ);
|
blockLight = (byte) chunkWrapper.getDhBlockLight(relBlockX, lastY + 1, relBlockZ);
|
||||||
skyLight = (byte) chunkWrapper.getSkyLight(relBlockX, lastY + 1, relBlockZ);
|
skyLight = (byte) chunkWrapper.getDhSkyLight(relBlockX, lastY + 1, relBlockZ);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -195,8 +193,8 @@ public class LodDataBuilder
|
|||||||
{
|
{
|
||||||
IBiomeWrapper newBiome = chunkWrapper.getBiome(relBlockX, y, relBlockZ);
|
IBiomeWrapper newBiome = chunkWrapper.getBiome(relBlockX, y, relBlockZ);
|
||||||
IBlockStateWrapper newBlockState = previousBlockState = chunkWrapper.getBlockState(relBlockX, y, relBlockZ, mcBlockPos, previousBlockState);
|
IBlockStateWrapper newBlockState = previousBlockState = chunkWrapper.getBlockState(relBlockX, y, relBlockZ, mcBlockPos, previousBlockState);
|
||||||
byte newBlockLight = (byte) chunkWrapper.getBlockLight(relBlockX, y + 1, relBlockZ);
|
byte newBlockLight = (byte) chunkWrapper.getDhBlockLight(relBlockX, y + 1, relBlockZ);
|
||||||
byte newSkyLight = (byte) chunkWrapper.getSkyLight(relBlockX, y + 1, relBlockZ);
|
byte newSkyLight = (byte) chunkWrapper.getDhSkyLight(relBlockX, y + 1, relBlockZ);
|
||||||
|
|
||||||
// save the biome/block change
|
// save the biome/block change
|
||||||
if (!newBiome.equals(biome) || !newBlockState.equals(blockState))
|
if (!newBiome.equals(biome) || !newBlockState.equals(blockState))
|
||||||
@@ -439,8 +437,6 @@ public class LodDataBuilder
|
|||||||
// helper methods //
|
// helper methods //
|
||||||
//================//
|
//================//
|
||||||
|
|
||||||
public static boolean canGenerateLodFromChunk(IChunkWrapper chunk) { return chunk != null && chunk.isLightCorrect(); }
|
|
||||||
|
|
||||||
public static int getXOrZSectionPosFromChunkPos(int chunkXOrZPos)
|
public static int getXOrZSectionPosFromChunkPos(int chunkXOrZPos)
|
||||||
{
|
{
|
||||||
// get the section position
|
// get the section position
|
||||||
|
|||||||
+9
-1
@@ -23,6 +23,7 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratio
|
|||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||||
|
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
||||||
import com.seibel.distanthorizons.core.generation.IFullDataSourceRetrievalQueue;
|
import com.seibel.distanthorizons.core.generation.IFullDataSourceRetrievalQueue;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker;
|
import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker;
|
||||||
import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult;
|
import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult;
|
||||||
@@ -394,7 +395,14 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void onDataSourceSave(FullDataSourceV2 fullDataSource)
|
private void onDataSourceSave(FullDataSourceV2 fullDataSource)
|
||||||
{ GeneratedFullDataSourceProvider.this.updateDataSourceAsync(fullDataSource); }
|
{
|
||||||
|
// block lights should have been populated at the chunkWrapper stage
|
||||||
|
// waiting to populate the data source's skylight at this stage prevents re-lighting and
|
||||||
|
// allows us to reduce cross-chunk lighting issues by lighting the whole 4x4 LOD at once
|
||||||
|
DhLightingEngine.INSTANCE.bakeDataSourceSkyLight(fullDataSource, LodUtil.MAX_MC_LIGHT);
|
||||||
|
|
||||||
|
GeneratedFullDataSourceProvider.this.updateDataSourceAsync(fullDataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
-5
@@ -184,11 +184,6 @@ public class SubDimensionLevelMatcher implements AutoCloseable
|
|||||||
DhLightingEngine.INSTANCE.lightChunk(newlyLoadedChunk, new ArrayList<>(), MC_CLIENT.getWrappedClientLevel().hasSkyLight() ? 15 : 0);
|
DhLightingEngine.INSTANCE.lightChunk(newlyLoadedChunk, new ArrayList<>(), MC_CLIENT.getWrappedClientLevel().hasSkyLight() ? 15 : 0);
|
||||||
|
|
||||||
// build the chunk LOD
|
// build the chunk LOD
|
||||||
if (!LodDataBuilder.canGenerateLodFromChunk(newlyLoadedChunk))
|
|
||||||
{
|
|
||||||
LOGGER.warn("unable to build lod for chunk:"+newlyLoadedChunk.getChunkPos());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk);
|
FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk);
|
||||||
// convert to a data source for easier comparing
|
// convert to a data source for easier comparing
|
||||||
FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos));
|
FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos));
|
||||||
|
|||||||
+313
-44
@@ -60,11 +60,23 @@ public class DhLightingEngine
|
|||||||
private static final ThreadLocal<DhBlockPosMutable> PRIMARY_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new DhBlockPosMutable());
|
private static final ThreadLocal<DhBlockPosMutable> PRIMARY_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new DhBlockPosMutable());
|
||||||
private static final ThreadLocal<DhBlockPosMutable> SECONDARY_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new DhBlockPosMutable());
|
private static final ThreadLocal<DhBlockPosMutable> SECONDARY_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new DhBlockPosMutable());
|
||||||
|
|
||||||
/** if enabled will render each block light value when the lighting engine is run */
|
/** if enabled will render each block light value when the chunk lighting engine is run */
|
||||||
private static final boolean RENDER_BLOCK_LIGHT_WIREFRAME = false;
|
private static final boolean RENDER_BLOCK_LIGHT_WIREFRAME = false;
|
||||||
/** if enabled will render each sky light value when the lighting engine is run */
|
/** if enabled will render each sky light value when the chunk lighting engine is run */
|
||||||
private static final boolean RENDER_SKY_LIGHT_WIREFRAME = false;
|
private static final boolean RENDER_SKY_LIGHT_WIREFRAME = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for dataSource lighting. <br>
|
||||||
|
* Packed as alternating x and z offsets.
|
||||||
|
*/
|
||||||
|
private static final byte[] ADJACENT_DIRECTION_OFFSETS = new byte[]
|
||||||
|
{
|
||||||
|
-1, 0,
|
||||||
|
+1, 0,
|
||||||
|
0, -1,
|
||||||
|
0, +1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
//=============//
|
||||||
@@ -75,9 +87,28 @@ public class DhLightingEngine
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=========//
|
//================//
|
||||||
// methods //
|
// chunk lighting //
|
||||||
//=========//
|
//================//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates both block and sky lighting.
|
||||||
|
* @see DhLightingEngine#lightChunk(IChunkWrapper, ArrayList, int, boolean, boolean)
|
||||||
|
*/
|
||||||
|
public void lightChunk(
|
||||||
|
@NotNull IChunkWrapper centerChunk, @NotNull ArrayList<IChunkWrapper> nearbyChunkList,
|
||||||
|
int maxSkyLight)
|
||||||
|
{ this.lightChunk(centerChunk, nearbyChunkList, maxSkyLight, true, true); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only populates block lights.
|
||||||
|
* @see DhLightingEngine#lightChunk(IChunkWrapper, ArrayList, int, boolean, boolean)
|
||||||
|
*/
|
||||||
|
public void bakeChunkBlockLighting(
|
||||||
|
@NotNull IChunkWrapper centerChunk, @NotNull ArrayList<IChunkWrapper> nearbyChunkList,
|
||||||
|
int maxSkyLight)
|
||||||
|
{ this.lightChunk(centerChunk, nearbyChunkList, maxSkyLight, true, false); }
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: depending on the implementation of {@link IChunkWrapper#setDhBlockLight(int, int, int, int)} and {@link IChunkWrapper#setDhSkyLight(int, int, int, int)}
|
* Note: depending on the implementation of {@link IChunkWrapper#setDhBlockLight(int, int, int, int)} and {@link IChunkWrapper#setDhSkyLight(int, int, int, int)}
|
||||||
@@ -88,13 +119,13 @@ public class DhLightingEngine
|
|||||||
* @param nearbyChunkList should also contain centerChunk
|
* @param nearbyChunkList should also contain centerChunk
|
||||||
* @param maxSkyLight should be a value between 0 and 15
|
* @param maxSkyLight should be a value between 0 and 15
|
||||||
*/
|
*/
|
||||||
public void lightChunk(@NotNull IChunkWrapper centerChunk, @NotNull ArrayList<IChunkWrapper> nearbyChunkList, int maxSkyLight)
|
private void lightChunk(
|
||||||
|
@NotNull IChunkWrapper centerChunk, @NotNull ArrayList<IChunkWrapper> nearbyChunkList,
|
||||||
|
int maxSkyLight, boolean updateBlockLight, boolean updateSkyLight)
|
||||||
{
|
{
|
||||||
DhChunkPos centerChunkPos = centerChunk.getChunkPos();
|
DhChunkPos centerChunkPos = centerChunk.getChunkPos();
|
||||||
AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk);
|
AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk);
|
||||||
|
|
||||||
long startTimeNs = System.nanoTime();
|
|
||||||
|
|
||||||
|
|
||||||
// try-finally to handle the stableArray resources
|
// try-finally to handle the stableArray resources
|
||||||
StableLightPosStack blockLightWorldPosQueue = null;
|
StableLightPosStack blockLightWorldPosQueue = null;
|
||||||
@@ -121,7 +152,6 @@ public class DhLightingEngine
|
|||||||
|
|
||||||
// find all adjacent chunks
|
// find all adjacent chunks
|
||||||
// and get any necessary info from them
|
// and get any necessary info from them
|
||||||
boolean warningLogged = false;
|
|
||||||
for (int chunkIndex = 0; chunkIndex < nearbyChunkList.size(); chunkIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
|
for (int chunkIndex = 0; chunkIndex < nearbyChunkList.size(); chunkIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
|
||||||
{
|
{
|
||||||
IChunkWrapper chunk = nearbyChunkList.get(chunkIndex);
|
IChunkWrapper chunk = nearbyChunkList.get(chunkIndex);
|
||||||
@@ -133,28 +163,31 @@ public class DhLightingEngine
|
|||||||
// add the adjacent chunk
|
// add the adjacent chunk
|
||||||
adjacentChunkHolder.add(chunk);
|
adjacentChunkHolder.add(chunk);
|
||||||
|
|
||||||
|
// get and set the adjacent chunk's initial block lights
|
||||||
|
final DhBlockPosMutable relLightBlockPos = PRIMARY_BLOCK_POS_REF.get();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==================//
|
//==================//
|
||||||
// set block lights //
|
// set block lights //
|
||||||
//==================//
|
//==================//
|
||||||
|
|
||||||
// get and set the adjacent chunk's initial block lights
|
if (updateBlockLight)
|
||||||
final DhBlockPosMutable relLightBlockPos = PRIMARY_BLOCK_POS_REF.get();
|
|
||||||
|
|
||||||
ArrayList<DhBlockPos> blockLightPosList = chunk.getWorldBlockLightPosList();
|
|
||||||
for (int blockLightIndex = 0; blockLightIndex < blockLightPosList.size(); blockLightIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
|
|
||||||
{
|
{
|
||||||
DhBlockPos blockLightPos = blockLightPosList.get(blockLightIndex);
|
ArrayList<DhBlockPos> blockLightPosList = chunk.getWorldBlockLightPosList();
|
||||||
blockLightPos.mutateToChunkRelativePos(relLightBlockPos);
|
for (int blockLightIndex = 0; blockLightIndex < blockLightPosList.size(); blockLightIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead
|
||||||
|
{
|
||||||
// get the light
|
DhBlockPos blockLightPos = blockLightPosList.get(blockLightIndex);
|
||||||
IBlockStateWrapper blockState = chunk.getBlockState(relLightBlockPos);
|
blockLightPos.mutateToChunkRelativePos(relLightBlockPos);
|
||||||
int lightValue = blockState.getLightEmission();
|
|
||||||
blockLightWorldPosQueue.push(blockLightPos.getX(), blockLightPos.getY(), blockLightPos.getZ(), lightValue);
|
// get the light
|
||||||
|
IBlockStateWrapper blockState = chunk.getBlockState(relLightBlockPos);
|
||||||
// set the light
|
int lightValue = blockState.getLightEmission();
|
||||||
chunk.setDhBlockLight(relLightBlockPos.getX(), relLightBlockPos.getY(), relLightBlockPos.getZ(), lightValue);
|
blockLightWorldPosQueue.push(blockLightPos.getX(), blockLightPos.getY(), blockLightPos.getZ(), lightValue);
|
||||||
|
|
||||||
|
// set the light
|
||||||
|
chunk.setDhBlockLight(relLightBlockPos.getX(), relLightBlockPos.getY(), relLightBlockPos.getZ(), lightValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -165,7 +198,7 @@ public class DhLightingEngine
|
|||||||
|
|
||||||
// get and set the adjacent chunk's initial skylights,
|
// get and set the adjacent chunk's initial skylights,
|
||||||
// if the dimension has skylights
|
// if the dimension has skylights
|
||||||
if (maxSkyLight > 0)
|
if (updateSkyLight && maxSkyLight > 0)
|
||||||
{
|
{
|
||||||
IMutableBlockPosWrapper mcBlockPos = chunk.getMutableBlockPosWrapper();
|
IMutableBlockPosWrapper mcBlockPos = chunk.getMutableBlockPosWrapper();
|
||||||
IBlockStateWrapper previousBlockState = null;
|
IBlockStateWrapper previousBlockState = null;
|
||||||
@@ -211,16 +244,22 @@ public class DhLightingEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
// block light
|
// block light
|
||||||
this.propagateLightPosList(blockLightWorldPosQueue, adjacentChunkHolder,
|
if (updateBlockLight)
|
||||||
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
{
|
||||||
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
this.propagateChunkLightPosList(blockLightWorldPosQueue, adjacentChunkHolder,
|
||||||
true);
|
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
||||||
|
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
// sky light
|
// sky light
|
||||||
this.propagateLightPosList(skyLightWorldPosQueue, adjacentChunkHolder,
|
if (updateSkyLight)
|
||||||
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
{
|
||||||
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
this.propagateChunkLightPosList(skyLightWorldPosQueue, adjacentChunkHolder,
|
||||||
false);
|
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
|
||||||
|
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
|
||||||
|
false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -233,17 +272,18 @@ public class DhLightingEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (updateBlockLight)
|
||||||
centerChunk.setIsDhLightCorrect(true);
|
{
|
||||||
centerChunk.setUseDhLighting(true);
|
centerChunk.setIsDhBlockLightCorrect(true);
|
||||||
|
}
|
||||||
long endTimeNs = System.nanoTime();
|
if (updateSkyLight)
|
||||||
float totalTimeMs = (endTimeNs - startTimeNs) / 1_000_000.0f;
|
{
|
||||||
//LOGGER.trace("Finished generating lighting for chunk: [" + centerChunkPos + "] in ["+totalTimeMs+"] milliseconds");
|
centerChunk.setIsDhSkyLightCorrect(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Applies each {@link LightPos} from the queue to the given set of {@link IChunkWrapper}'s. */
|
/** Applies each {@link LightPos} from the queue to the given set of {@link IChunkWrapper}'s. */
|
||||||
private void propagateLightPosList(
|
private void propagateChunkLightPosList(
|
||||||
StableLightPosStack lightPosQueue, AdjacentChunkHolder adjacentChunkHolder,
|
StableLightPosStack lightPosQueue, AdjacentChunkHolder adjacentChunkHolder,
|
||||||
IGetLightFunc getLightFunc, ISetLightFunc setLightFunc,
|
IGetLightFunc getLightFunc, ISetLightFunc setLightFunc,
|
||||||
boolean propagatingBlockLights)
|
boolean propagatingBlockLights)
|
||||||
@@ -339,10 +379,239 @@ public class DhLightingEngine
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//======================//
|
||||||
|
// data source lighting //
|
||||||
|
//======================//
|
||||||
|
|
||||||
|
/** @author BuilderB0y */
|
||||||
|
public void bakeDataSourceSkyLight(FullDataSourceV2 dataSource, int maxSkyLight)
|
||||||
|
{
|
||||||
|
// create a cache of all the IDs which are completely transparent.
|
||||||
|
// FullDataPointIdMap is thread-safe with locks, and is also a map lookup,
|
||||||
|
// and both of these things add a bit of overhead which is not necessary
|
||||||
|
// in this context.
|
||||||
|
// note: since IDs map to both biomes and blocks, there can be more than
|
||||||
|
// one ID which corresponds to air.
|
||||||
|
BitSet airIDs = new BitSet(dataSource.mapping.size());
|
||||||
|
for (int id = 0, size = dataSource.mapping.size(); id < size; id++)
|
||||||
|
{
|
||||||
|
if (dataSource.mapping.getBlockStateWrapper(id).getOpacity() == 0)
|
||||||
|
{
|
||||||
|
airIDs.set(id, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
|
||||||
|
{
|
||||||
|
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
|
||||||
|
{
|
||||||
|
LongArrayList dataPoints = dataSource.get(x, z);
|
||||||
|
if (dataPoints != null && !dataPoints.isEmpty())
|
||||||
|
{
|
||||||
|
// iterate through the data points in this column top-down
|
||||||
|
// until we reach light level 0 in some way. at this point,
|
||||||
|
// no more propagation needs to be performed for this column.
|
||||||
|
int size = dataPoints.size();
|
||||||
|
for (int index = 0; index < size; index++)
|
||||||
|
{
|
||||||
|
long point = dataPoints.getLong(index);
|
||||||
|
// if the data point in the column is transparent,
|
||||||
|
// then fill it with light and then propagate
|
||||||
|
// that light both horizontally and downwards.
|
||||||
|
if (airIDs.get(FullDataPointUtil.getId(point)))
|
||||||
|
{
|
||||||
|
int skylight;
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
// top-most data point in the column.
|
||||||
|
skylight = maxSkyLight;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// handle down propagation here. sort of.
|
||||||
|
// down propagation is also handled partially elsewhere.
|
||||||
|
// basically if the data point above is transparent,
|
||||||
|
// we copy its light level.
|
||||||
|
// otherwise, if the data point above is opaque,
|
||||||
|
// then no light can propagate downwards from it.
|
||||||
|
// therefore, this data point should be light level 0*
|
||||||
|
// and no more propagation needs to be performed for this column.
|
||||||
|
//
|
||||||
|
// *unless light propagates into it horizontally,
|
||||||
|
// but that is handled separately.
|
||||||
|
long above = dataPoints.getLong(index - 1);
|
||||||
|
if (airIDs.get(FullDataPointUtil.getId(above)))
|
||||||
|
{
|
||||||
|
skylight = FullDataPointUtil.getSkyLight(above);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the data point to contain the correct starting skylight level.
|
||||||
|
point = FullDataPointUtil.setSkyLight(point, skylight);
|
||||||
|
dataPoints.set(index, point);
|
||||||
|
// now for the propagation.
|
||||||
|
recursivelyLightAdjacentDataPoints(dataSource, airIDs, x, z, point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// at this point, all transparent data points have been lit,
|
||||||
|
// but opaque ones still have light level 0.
|
||||||
|
// in this loop we make opaque data points copy the light level
|
||||||
|
// above them if, and only if, the data point above is translucent.
|
||||||
|
// with one exception: if the data point above is only partially translucent,
|
||||||
|
// we use a slightly different way of computing how much light it absorbed.
|
||||||
|
// this is how we handle water and ocean floors.
|
||||||
|
// note that this alternate logic assumes the
|
||||||
|
// data point above is being lit from the top.
|
||||||
|
// this is a fine assumption for water and oceans.
|
||||||
|
for (LongArrayList list : dataSource.dataPoints)
|
||||||
|
{
|
||||||
|
if (list != null)
|
||||||
|
{
|
||||||
|
for (int index = 0, size = list.size(); index < size; index++)
|
||||||
|
{
|
||||||
|
long dataPoint = list.getLong(index);
|
||||||
|
if (index == 0)
|
||||||
|
{
|
||||||
|
// top data point, assume "above" has the max sky light.
|
||||||
|
dataPoint = FullDataPointUtil.setSkyLight(dataPoint, maxSkyLight);
|
||||||
|
list.set(index, dataPoint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// there is another data point above this one.
|
||||||
|
// check to see how opaque this data point is first.
|
||||||
|
// we will check the above one after that.
|
||||||
|
if (!airIDs.get(FullDataPointUtil.getId(dataPoint)))
|
||||||
|
{
|
||||||
|
// this data point is not transparent.
|
||||||
|
// it should be lit from above.
|
||||||
|
long above = list.getLong(index - 1);
|
||||||
|
int aboveLight = FullDataPointUtil.getSkyLight(above);
|
||||||
|
if (airIDs.get(FullDataPointUtil.getId(above)))
|
||||||
|
{
|
||||||
|
// the above data point is transparent,
|
||||||
|
// and does not absorb any light.
|
||||||
|
// its light level can be copied as-is.
|
||||||
|
dataPoint = FullDataPointUtil.setSkyLight(dataPoint, aboveLight);
|
||||||
|
list.set(index, dataPoint);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// determine how much light should be absorbed by this column
|
||||||
|
int absorption = dataSource.mapping.getBlockStateWrapper(FullDataPointUtil.getId(above)).getOpacity() * FullDataPointUtil.getHeight(above);
|
||||||
|
if (absorption < aboveLight)
|
||||||
|
{
|
||||||
|
// the above data point is partially translucent,
|
||||||
|
// and absorbs some light. however, it did not absorb
|
||||||
|
// enough light to bring the light level down to 0.
|
||||||
|
// so, the remaining light can still be copied.
|
||||||
|
dataPoint = FullDataPointUtil.setSkyLight(dataPoint, aboveLight - absorption);
|
||||||
|
list.set(index, dataPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @author BuilderB0y */
|
||||||
|
public void recursivelyLightAdjacentDataPoints(
|
||||||
|
FullDataSourceV2 chunk,
|
||||||
|
BitSet airIDs,
|
||||||
|
int relativeX,
|
||||||
|
int relativeZ,
|
||||||
|
long dataPoint
|
||||||
|
)
|
||||||
|
{
|
||||||
|
int lightLevel = FullDataPointUtil.getSkyLight(dataPoint);
|
||||||
|
// early exit condition:
|
||||||
|
// in this case, propagating light is guaranteed to be 0 at adjacent positions,
|
||||||
|
// and therefore we do not need to waste time propagating it.
|
||||||
|
if (lightLevel <= 1)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int minY = FullDataPointUtil.getBottomY(dataPoint);
|
||||||
|
int maxY = FullDataPointUtil.getHeight(dataPoint) + minY;
|
||||||
|
// try to propagate in all 4 directions.
|
||||||
|
for (int offsetIndex = 0; offsetIndex < ADJACENT_DIRECTION_OFFSETS.length; )
|
||||||
|
{
|
||||||
|
int adjacentX = relativeX + ADJACENT_DIRECTION_OFFSETS[offsetIndex++];
|
||||||
|
int adjacentZ = relativeZ + ADJACENT_DIRECTION_OFFSETS[offsetIndex++];
|
||||||
|
|
||||||
|
// check if the adjacent position is within the bounds of this data source...
|
||||||
|
if (adjacentX >= 0 && adjacentX < FullDataSourceV2.WIDTH && adjacentZ >= 0 && adjacentZ < FullDataSourceV2.WIDTH)
|
||||||
|
{
|
||||||
|
LongArrayList adjacentDataPoints = chunk.get(adjacentX, adjacentZ);
|
||||||
|
// ...and also check to make sure we have some data points
|
||||||
|
// (potentially transparent ones) to propagate through in the adjacent column.
|
||||||
|
if (adjacentDataPoints != null)
|
||||||
|
{
|
||||||
|
// try to find adjacent data points we can propagate into.
|
||||||
|
// we go top-down for this, which will be important for some
|
||||||
|
// later conditions.
|
||||||
|
int size = adjacentDataPoints.size();
|
||||||
|
for (int adjacentIndex = 0; adjacentIndex < size; adjacentIndex++)
|
||||||
|
{
|
||||||
|
long adjacentDataPoint = adjacentDataPoints.getLong(adjacentIndex);
|
||||||
|
int adjacentMinY = FullDataPointUtil.getBottomY(adjacentDataPoint);
|
||||||
|
int adjacentMaxY = FullDataPointUtil.getHeight(adjacentDataPoint) + adjacentMinY;
|
||||||
|
if (adjacentMinY >= maxY)
|
||||||
|
{
|
||||||
|
// if the adjacent data point is completely above this one,
|
||||||
|
// then there is no overlap between this one and the adjacent one,
|
||||||
|
// and therefore light cannot propagate here.
|
||||||
|
// try to propagate to the next data point down from the adjacent one.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (adjacentMaxY <= minY)
|
||||||
|
{
|
||||||
|
// if the adjacent data point is completely below this one,
|
||||||
|
// then it also has no overlap and can't propagate,
|
||||||
|
// but since we're going top-down, neither can any subsequent adjacent data points.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (!airIDs.get(FullDataPointUtil.getId(adjacentDataPoint)))
|
||||||
|
{
|
||||||
|
// assume for now that we cannot propagate into non-transparent data points.
|
||||||
|
continue; // TODO how does this work with water? Do we care?
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// now we can try to propagate.
|
||||||
|
int adjacentLightLevel = FullDataPointUtil.getSkyLight(adjacentDataPoint);
|
||||||
|
// if the resulting light level after propagation would INCREASE
|
||||||
|
// the light level of the adjacent data point, then propagate to it.
|
||||||
|
// otherwise, don't do that.
|
||||||
|
if (lightLevel - 1 > adjacentLightLevel)
|
||||||
|
{
|
||||||
|
adjacentDataPoint = FullDataPointUtil.setSkyLight(adjacentDataPoint, lightLevel - 1);
|
||||||
|
adjacentDataPoints.set(adjacentIndex, adjacentDataPoint);
|
||||||
|
// if propagation succeeded, recursively propagate again starting at the adjacent data point.
|
||||||
|
recursivelyLightAdjacentDataPoints(chunk, airIDs, adjacentX, adjacentZ, adjacentDataPoint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// test
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
//===========//
|
//===========//
|
||||||
|
|||||||
+1
-1
@@ -467,7 +467,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
IChunkWrapper chunk = WRAPPER_FACTORY.createChunkWrapper(generatedObjectArray);
|
IChunkWrapper chunk = WRAPPER_FACTORY.createChunkWrapper(generatedObjectArray);
|
||||||
FullDataSourceV2 dataSource = LodDataBuilder.createGeneratedDataSource(chunk);
|
FullDataSourceV2 dataSource = LodDataBuilder.createFromChunk(chunk);
|
||||||
LodUtil.assertTrue(dataSource != null);
|
LodUtil.assertTrue(dataSource != null);
|
||||||
chunkDataConsumer.accept(dataSource);
|
chunkDataConsumer.accept(dataSource);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.level;
|
|||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent;
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent;
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache;
|
import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache;
|
||||||
|
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||||
@@ -32,6 +33,7 @@ import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
|||||||
import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO;
|
import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO;
|
||||||
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
||||||
import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo;
|
import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo;
|
||||||
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
@@ -168,6 +170,12 @@ public abstract class AbstractDhLevel implements IDhLevel
|
|||||||
|
|
||||||
private void onDataSourceSave(FullDataSourceV2 fullDataSource)
|
private void onDataSourceSave(FullDataSourceV2 fullDataSource)
|
||||||
{
|
{
|
||||||
|
// block lights should have been populated at the chunkWrapper stage
|
||||||
|
// waiting to populate the data source's skylight at this stage prevents re-lighting and
|
||||||
|
// allows us to reduce cross-chunk lighting issues by lighting the whole 4x4 LOD at once
|
||||||
|
DhLightingEngine.INSTANCE.bakeDataSourceSkyLight(fullDataSource, this.hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT);
|
||||||
|
|
||||||
|
|
||||||
this.updateDataSourcesAsync(fullDataSource).thenRun(() ->
|
this.updateDataSourcesAsync(fullDataSource).thenRun(() ->
|
||||||
{
|
{
|
||||||
HashSet<DhChunkPos> updatedChunkPosSet = this.updatedChunkPosSetBySectionPos.remove(fullDataSource.getPos());
|
HashSet<DhChunkPos> updatedChunkPosSet = this.updatedChunkPosSetBySectionPos.remove(fullDataSource.getPos());
|
||||||
|
|||||||
@@ -157,6 +157,7 @@ public class FullDataPointUtil
|
|||||||
public static int getSkyLight(long data) { return (int) ((data >> SKY_LIGHT_OFFSET) & SKY_LIGHT_MASK); }
|
public static int getSkyLight(long data) { return (int) ((data >> SKY_LIGHT_OFFSET) & SKY_LIGHT_MASK); }
|
||||||
|
|
||||||
public static long setBlockLight(long data, byte blockLight) { return (data & ~((long) BLOCK_LIGHT_MASK << BLOCK_LIGHT_OFFSET) | (long) blockLight << BLOCK_LIGHT_OFFSET); }
|
public static long setBlockLight(long data, byte blockLight) { return (data & ~((long) BLOCK_LIGHT_MASK << BLOCK_LIGHT_OFFSET) | (long) blockLight << BLOCK_LIGHT_OFFSET); }
|
||||||
|
public static long setSkyLight(long data, int skyLight) { return (data & ~((long) SKY_LIGHT_MASK << SKY_LIGHT_OFFSET) | (long) skyLight << SKY_LIGHT_OFFSET); }
|
||||||
|
|
||||||
public static String toString(long data) { return "[ID:" + getId(data) + ",Y:" + getBottomY(data) + ",Height:" + getHeight(data) + ",BlockLight:" + getBlockLight(data) + ",SkyLight:" + getSkyLight(data) + "]"; }
|
public static String toString(long data) { return "[ID:" + getId(data) + ",Y:" + getBottomY(data) + ",Height:" + getHeight(data) + ",BlockLight:" + getBlockLight(data) + ",SkyLight:" + getSkyLight(data) + "]"; }
|
||||||
|
|
||||||
|
|||||||
+5
-85
@@ -75,9 +75,11 @@ public interface IChunkWrapper extends IBindable
|
|||||||
int getMinBlockX();
|
int getMinBlockX();
|
||||||
int getMinBlockZ();
|
int getMinBlockZ();
|
||||||
|
|
||||||
void setIsDhLightCorrect(boolean isDhLightCorrect);
|
void setIsDhBlockLightCorrect(boolean isDhLightCorrect);
|
||||||
void setUseDhLighting(boolean useDhLighting);
|
void setIsDhSkyLightCorrect(boolean isDhLightCorrect);
|
||||||
boolean isLightCorrect();
|
|
||||||
|
boolean isDhBlockLightingCorrect();
|
||||||
|
boolean isDhSkyLightCorrect();
|
||||||
|
|
||||||
|
|
||||||
int getDhSkyLight(int relX, int relY, int relZ);
|
int getDhSkyLight(int relX, int relY, int relZ);
|
||||||
@@ -88,9 +90,6 @@ public interface IChunkWrapper extends IBindable
|
|||||||
void setDhBlockLight(int relX, int relY, int relZ, int lightValue);
|
void setDhBlockLight(int relX, int relY, int relZ, int lightValue);
|
||||||
void clearDhBlockLighting();
|
void clearDhBlockLighting();
|
||||||
|
|
||||||
int getBlockLight(int relX, int relY, int relZ);
|
|
||||||
int getSkyLight(int relX, int relY, int relZ);
|
|
||||||
|
|
||||||
|
|
||||||
/** Note: don't modify this array, it will only be generated once and then shared between uses */
|
/** Note: don't modify this array, it will only be generated once and then shared between uses */
|
||||||
ArrayList<DhBlockPos> getWorldBlockLightPosList();
|
ArrayList<DhBlockPos> getWorldBlockLightPosList();
|
||||||
@@ -160,85 +159,6 @@ public interface IChunkWrapper extends IBindable
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Populates DH's saved lighting using MC's lighting engine.
|
|
||||||
* This is generally done in cases where MC's lighting is correct now, but may not be later (like when a chunk is unloading).
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException if the chunk's lighting isn't valid. This is done to prevent accidentally baking broken lighting.
|
|
||||||
* @return true if the chunk's lighting was successfully populated, false otherwise
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
default boolean bakeDhLightingUsingMcLightingEngine(ILevelWrapper levelWrapper) throws IllegalStateException
|
|
||||||
{
|
|
||||||
if (!this.isLightCorrect())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//=======================//
|
|
||||||
// get lighting for each //
|
|
||||||
// relative block pos //
|
|
||||||
//=======================//
|
|
||||||
|
|
||||||
boolean lightingFound = false;
|
|
||||||
// if the level doesn't have sky lights, then this check can be ignored
|
|
||||||
// since all sky light values will be 0 anyway
|
|
||||||
boolean skyLightingFound = !levelWrapper.hasSkyLight();
|
|
||||||
|
|
||||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
|
|
||||||
{
|
|
||||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
|
||||||
{
|
|
||||||
for (int y = this.getMinBuildHeight(); y < this.getMaxBuildHeight(); y++)
|
|
||||||
{
|
|
||||||
int skyLight = this.getSkyLight(relX, y, relZ);
|
|
||||||
this.setDhSkyLight(relX, y, relZ, skyLight);
|
|
||||||
int blockLight = this.getBlockLight(relX, y, relZ);
|
|
||||||
this.setDhBlockLight(relX, y, relZ, blockLight);
|
|
||||||
|
|
||||||
// MC defaults to max sky light and no block light, including underground blocks.
|
|
||||||
// If any position has something different then those default values, it's likely that the
|
|
||||||
// lighting was properly populated for at least part of the chunk
|
|
||||||
if (!lightingFound &&
|
|
||||||
(skyLight != LodUtil.MAX_MC_LIGHT || blockLight != LodUtil.MIN_MC_LIGHT))
|
|
||||||
{
|
|
||||||
lightingFound = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!skyLightingFound
|
|
||||||
&& skyLight != LodUtil.MIN_MC_LIGHT)
|
|
||||||
{
|
|
||||||
skyLightingFound = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// validate result //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
// if no lighting was found or the sky is always black, the lighting is likely broken
|
|
||||||
if (!lightingFound || !skyLightingFound
|
|
||||||
// if lighting is no longer correct or doesn't match the saved values
|
|
||||||
// its very likely it broke halfway through and will need regenerating
|
|
||||||
|| !this.isLightCorrect()
|
|
||||||
|| this.getSkyLight(0, 0, 0) != this.getDhSkyLight(0,0,0)
|
|
||||||
|| this.getBlockLight(0, 0, 0) != this.getDhBlockLight(0,0,0))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// lighting is valid
|
|
||||||
this.setIsDhLightCorrect(true);
|
|
||||||
this.setUseDhLighting(true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a 3D position into a 1D array index. <br><br>
|
* Converts a 3D position into a 1D array index. <br><br>
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2023 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package testItems.lightingEngine;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
|
||||||
import tests.LightingEngineTest;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see LightingEngineTest
|
|
||||||
* @see LightingTestChunkWrapper
|
|
||||||
*/
|
|
||||||
public class LightingTestBlockStateWrapper implements IBlockStateWrapper
|
|
||||||
{
|
|
||||||
private int opacity = -1;
|
|
||||||
private int lightEmission = -1;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public LightingTestBlockStateWrapper(int opacity, int lightEmission)
|
|
||||||
{
|
|
||||||
this.opacity = opacity;
|
|
||||||
this.lightEmission = lightEmission;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=================//
|
|
||||||
// wrapper methods //
|
|
||||||
//=================//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOpacity() { return this.opacity; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightEmission() { return this.lightEmission; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===============//
|
|
||||||
// unimplemented //
|
|
||||||
//===============//
|
|
||||||
|
|
||||||
//@Override
|
|
||||||
//public boolean equals(Object obj)
|
|
||||||
//{
|
|
||||||
// if (this == obj)
|
|
||||||
// {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (obj == null || this.getClass() != obj.getClass())
|
|
||||||
// {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// BlockStateTestWrapper that = (BlockStateTestWrapper) obj;
|
|
||||||
// // the serialized value is used so we can test the contents instead of the references
|
|
||||||
// return Objects.equals(this.getSerialString(), that.getSerialString());
|
|
||||||
//}
|
|
||||||
|
|
||||||
//@Override
|
|
||||||
//public int hashCode() { return this.hashCode; }
|
|
||||||
//@Override
|
|
||||||
//public String toString() { return this.getSerialString(); }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSerialString() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getWrappedMcObject() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAir() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSolid() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLiquid() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte getMaterialId() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBeaconBlock() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
@Override
|
|
||||||
public boolean isBeaconBaseBlock() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Color getMapColor() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
@Override
|
|
||||||
public boolean isBeaconTintBlock() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
@Override
|
|
||||||
public Color getBeaconTintColor() { throw new UnsupportedOperationException("Not Implemented"); }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,435 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2023 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package testItems.lightingEngine;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import tests.LightingEngineTest;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see LightingEngineTest
|
|
||||||
* @see LightingTestBlockStateWrapper
|
|
||||||
*/
|
|
||||||
public class LightingTestChunkWrapper implements IChunkWrapper
|
|
||||||
{
|
|
||||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
|
||||||
|
|
||||||
|
|
||||||
// chunk values //
|
|
||||||
|
|
||||||
private final DhChunkPos chunkPos;
|
|
||||||
private ChunkLightStorage blockLightStorage;
|
|
||||||
private ChunkLightStorage skyLightStorage;
|
|
||||||
|
|
||||||
private ArrayList<DhBlockPos> blockLightPosList = null;
|
|
||||||
|
|
||||||
private boolean useDhLighting;
|
|
||||||
|
|
||||||
private int minNonEmptyHeight = Integer.MIN_VALUE;
|
|
||||||
private int maxNonEmptyHeight = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
|
|
||||||
// test values //
|
|
||||||
|
|
||||||
private final Int2IntOpenHashMap blockOpacityStorage;
|
|
||||||
private final Int2IntOpenHashMap blockEmissionStorage;
|
|
||||||
private final int[][] solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
|
||||||
private final int[][] lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=============//
|
|
||||||
// constructor //
|
|
||||||
//=============//
|
|
||||||
|
|
||||||
public LightingTestChunkWrapper(IChunkWrapper chunkWrapper)
|
|
||||||
{
|
|
||||||
this.chunkPos = chunkWrapper.getChunkPos();
|
|
||||||
|
|
||||||
this.blockOpacityStorage = new Int2IntOpenHashMap();
|
|
||||||
this.blockEmissionStorage = new Int2IntOpenHashMap();
|
|
||||||
this.blockLightPosList = new ArrayList<>();
|
|
||||||
|
|
||||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
|
||||||
{
|
|
||||||
for (int y = this.getMinBuildHeight(); y < this.getMaxBuildHeight(); y++)
|
|
||||||
{
|
|
||||||
IBlockStateWrapper block = chunkWrapper.getBlockState(x,y,z);
|
|
||||||
|
|
||||||
int opacity = block.getOpacity();
|
|
||||||
if (opacity >= LodUtil.BLOCK_FULLY_OPAQUE)
|
|
||||||
{
|
|
||||||
opacity = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blockOpacityStorage.put(new DhBlockPos(x, y, z).hashCode(), opacity);
|
|
||||||
this.blockEmissionStorage.put(new DhBlockPos(x, y, z).hashCode(), block.getLightEmission());
|
|
||||||
|
|
||||||
if (block.getLightEmission() != 0)
|
|
||||||
{
|
|
||||||
this.blockLightPosList.add(new DhBlockPos(x,y,z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lightBlockingHeightMap[x][z] = chunkWrapper.getLightBlockingHeightMapValue(x, z);
|
|
||||||
this.solidHeightMap[x][z] = chunkWrapper.getSolidHeightMapValue(x, z);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===============//
|
|
||||||
// file handling //
|
|
||||||
//===============//
|
|
||||||
|
|
||||||
public LightingTestChunkWrapper(DhChunkPos pos, File saveFile) throws DataCorruptedException
|
|
||||||
{
|
|
||||||
this.chunkPos = pos;
|
|
||||||
|
|
||||||
this.blockOpacityStorage = new Int2IntOpenHashMap();
|
|
||||||
this.blockEmissionStorage = new Int2IntOpenHashMap();
|
|
||||||
this.blockLightPosList = new ArrayList<>();
|
|
||||||
|
|
||||||
try(FileInputStream inputStream = new FileInputStream(saveFile);
|
|
||||||
BufferedInputStream bufferedStream = new BufferedInputStream(inputStream);
|
|
||||||
DataInputStream stream = new DataInputStream(bufferedStream))
|
|
||||||
{
|
|
||||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
|
||||||
{
|
|
||||||
for (int y = this.getMinBuildHeight(); y < this.getMaxBuildHeight(); y++)
|
|
||||||
{
|
|
||||||
this.blockOpacityStorage.put(new DhBlockPos(x, y, z).hashCode(), stream.readInt());
|
|
||||||
|
|
||||||
int blockEmission = stream.readInt();
|
|
||||||
this.blockEmissionStorage.put(new DhBlockPos(x, y, z).hashCode(), blockEmission);
|
|
||||||
if (blockEmission != 0)
|
|
||||||
{
|
|
||||||
this.blockLightPosList.add(new DhBlockPos(x,y,z));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream.readChar() != ';')
|
|
||||||
{
|
|
||||||
throw new DataCorruptedException("bad height map");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lightBlockingHeightMap[x][z] = stream.readInt();
|
|
||||||
this.solidHeightMap[x][z] = stream.readInt();
|
|
||||||
|
|
||||||
if (stream.readChar() != '\n')
|
|
||||||
{
|
|
||||||
throw new DataCorruptedException(" bad col ending");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Unable to write to file: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void writeToFile(File file)
|
|
||||||
{
|
|
||||||
try(FileOutputStream fileStream = new FileOutputStream(file);
|
|
||||||
BufferedOutputStream bufferedStream = new BufferedOutputStream(fileStream);
|
|
||||||
DataOutputStream stream = new DataOutputStream(bufferedStream))
|
|
||||||
{
|
|
||||||
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
|
|
||||||
{
|
|
||||||
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
|
|
||||||
{
|
|
||||||
for (int y = this.getMinBuildHeight(); y < this.getMaxBuildHeight(); y++)
|
|
||||||
{
|
|
||||||
stream.writeInt(this.blockOpacityStorage.get(new DhBlockPos(x, y, z).hashCode()));
|
|
||||||
stream.writeInt(this.blockEmissionStorage.get(new DhBlockPos(x, y, z).hashCode()));
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeChar(';');
|
|
||||||
|
|
||||||
stream.writeInt(this.lightBlockingHeightMap[x][z]);
|
|
||||||
stream.writeInt(this.solidHeightMap[x][z]);
|
|
||||||
|
|
||||||
stream.writeChar('\n');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.flush();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
LOGGER.error("Unable to write to file: ["+e.getMessage()+"].", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be added into {@link com.seibel.distanthorizons.core.api.internal.SharedApi#applyChunkUpdate(IChunkWrapper, ILevelWrapper, boolean)}
|
|
||||||
* to save chunks to file for future testing.
|
|
||||||
*/
|
|
||||||
public void tryConvertingAndSavingChunkWrapper(IChunkWrapper chunkWrapper)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File chunkFile = new File(LightingEngineTest.TEST_DATA_PATH + "/" + chunkWrapper.getChunkPos().toString());
|
|
||||||
if (!chunkFile.exists())
|
|
||||||
{
|
|
||||||
LightingTestChunkWrapper testWrapper = new LightingTestChunkWrapper(chunkWrapper);
|
|
||||||
testWrapper.writeToFile(chunkFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.error(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//===============//
|
|
||||||
// chunk methods //
|
|
||||||
//===============//
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getHeight() { return 255; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinBuildHeight() { return -64; }
|
|
||||||
@Override
|
|
||||||
public int getMaxBuildHeight() { return 255; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinNonEmptyHeight()
|
|
||||||
{
|
|
||||||
if (this.minNonEmptyHeight != Integer.MIN_VALUE)
|
|
||||||
{
|
|
||||||
return this.minNonEmptyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// default if every section is empty or missing
|
|
||||||
this.minNonEmptyHeight = this.getMinBuildHeight();
|
|
||||||
|
|
||||||
// determine the lowest empty section (bottom up)
|
|
||||||
int maxYHeight = this.getMaxBuildHeight();
|
|
||||||
for (int y = this.getMinBuildHeight(); y < maxYHeight; y++)
|
|
||||||
{
|
|
||||||
if (this.blockOpacityStorage.get(new DhBlockPos(0, y, 0).hashCode()) != 0)
|
|
||||||
{
|
|
||||||
// -16 to simulate having to populate the full chunk section
|
|
||||||
this.minNonEmptyHeight = Math.min(y - 16, maxYHeight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.minNonEmptyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxNonEmptyHeight()
|
|
||||||
{
|
|
||||||
if (this.maxNonEmptyHeight != Integer.MAX_VALUE)
|
|
||||||
{
|
|
||||||
return this.maxNonEmptyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// default if every section is empty or missing
|
|
||||||
this.maxNonEmptyHeight = this.getMaxBuildHeight();
|
|
||||||
|
|
||||||
// determine the highest empty section (top down)
|
|
||||||
int minYHeight = this.getMinBuildHeight();
|
|
||||||
for (int y = this.getMaxBuildHeight(); y >= minYHeight; y--)
|
|
||||||
{
|
|
||||||
if (this.blockOpacityStorage.get(new DhBlockPos(0, y, 0).hashCode()) != 0)
|
|
||||||
{
|
|
||||||
// -16 to simulate having to populate the full chunk section
|
|
||||||
this.maxNonEmptyHeight = Math.max(y - 16, minYHeight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.maxNonEmptyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSolidHeightMapValue(int xRel, int zRel) { return this.solidHeightMap[xRel][zRel]; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.lightBlockingHeightMap[xRel][zRel]; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBiomeWrapper getBiome(int relX, int relY, int relZ) { throw new UnsupportedOperationException("Not implemented"); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DhChunkPos getChunkPos() { return this.chunkPos; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaxBlockX() { return 0; }
|
|
||||||
@Override
|
|
||||||
public int getMaxBlockZ() { return 0; }
|
|
||||||
@Override
|
|
||||||
public int getMinBlockX() { return LodUtil.CHUNK_WIDTH; }
|
|
||||||
@Override
|
|
||||||
public int getMinBlockZ() { return LodUtil.CHUNK_WIDTH; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIsDhLightCorrect(boolean isDhLightCorrect) { }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLightCorrect() { return false; }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDhBlockLight(int relX, int y, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
return this.getBlockLightStorage().get(relX, y, relZ);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void setDhBlockLight(int relX, int y, int relZ, int lightValue)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
this.getBlockLightStorage().set(relX, y, relZ, lightValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ChunkLightStorage getBlockLightStorage()
|
|
||||||
{
|
|
||||||
if (this.blockLightStorage == null)
|
|
||||||
{
|
|
||||||
this.blockLightStorage = new ChunkLightStorage(
|
|
||||||
// +/- 16 is to fix an issue with the test chunk where the storage isn't big enough,
|
|
||||||
// James probably just screwed up the min/max height slightly
|
|
||||||
this.getMinBuildHeight() - 16, this.getMaxBuildHeight() + 16,
|
|
||||||
// positions above and below the handled area should be unlit
|
|
||||||
LodUtil.MIN_MC_LIGHT, LodUtil.MIN_MC_LIGHT);
|
|
||||||
}
|
|
||||||
return this.blockLightStorage;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void clearDhBlockLighting() { throw new UnsupportedOperationException("Not implemented"); }
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDhSkyLight(int relX, int y, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
return this.getSkyLightStorage().get(relX, y, relZ);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void setDhSkyLight(int relX, int y, int relZ, int lightValue)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
this.getSkyLightStorage().set(relX, y, relZ, lightValue);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void clearDhSkyLighting() { throw new UnsupportedOperationException("Not implemented"); }
|
|
||||||
|
|
||||||
private ChunkLightStorage getSkyLightStorage()
|
|
||||||
{
|
|
||||||
if (this.skyLightStorage == null)
|
|
||||||
{
|
|
||||||
this.skyLightStorage = new ChunkLightStorage(
|
|
||||||
// +/- 16 is to fix an issue with the test chunk where the storage isn't big enough,
|
|
||||||
// James probably just screwed up the min/max height slightly
|
|
||||||
this.getMinBuildHeight() - 16, this.getMaxBuildHeight() + 16,
|
|
||||||
// positions above should be lit but positions below should be unlit
|
|
||||||
LodUtil.MAX_MC_LIGHT, LodUtil.MIN_MC_LIGHT);
|
|
||||||
}
|
|
||||||
return this.skyLightStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getBlockLight(int relX, int y, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
return this.getBlockLightStorage().get(relX, y, relZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSkyLight(int relX, int y, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
|
|
||||||
return this.getSkyLightStorage().get(relX, y, relZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ArrayList<DhBlockPos> getWorldBlockLightPosList() { return this.blockLightPosList; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean doNearbyChunksExist() { return false; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() { return this.chunkPos.toString(); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
|
|
||||||
{
|
|
||||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
|
||||||
|
|
||||||
int opacity = this.blockOpacityStorage.get(new DhBlockPos(relX, relY, relZ).hashCode());
|
|
||||||
int lightEmission = this.blockEmissionStorage.get(new DhBlockPos(relX, relY, relZ).hashCode());
|
|
||||||
return new LightingTestBlockStateWrapper(opacity, lightEmission);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
|
|
||||||
{ return this.getBlockState(relX, relY, relZ); }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IMutableBlockPosWrapper getMutableBlockPosWrapper()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isStillValid() { return true; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of the Distant Horizons mod
|
|
||||||
* licensed under the GNU LGPL v3 License.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020-2023 James Seibel
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
* the Free Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package tests;
|
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
|
||||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
|
||||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
|
||||||
import testItems.lightingEngine.LightingTestChunkWrapper;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Can be used to A/B Test lighting engine performance changes. <br><br>
|
|
||||||
*
|
|
||||||
* normal - chunks: [1595], total Time: [1490], avg time: [0.9341692789968652] <br>
|
|
||||||
* only surface light prop - chunks: [1595], total Time: [984], avg time: [0.6169278996865204] <br>
|
|
||||||
*
|
|
||||||
* @author James Seibel
|
|
||||||
* @version 2024-6-11
|
|
||||||
*/
|
|
||||||
public class LightingEngineTest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* There should be test data in the following core repo folder: <br>
|
|
||||||
* <code> Core\_Misc Files\test files\Lighting engine test chunk data.7z </code>
|
|
||||||
*/
|
|
||||||
public static final String TEST_DATA_PATH = "C:/Users/James_Seibel/Desktop/test chunk data";
|
|
||||||
|
|
||||||
|
|
||||||
//@Test
|
|
||||||
public void TestLightingEngine() throws DataCorruptedException
|
|
||||||
{
|
|
||||||
long totalNanoTime = 0;
|
|
||||||
int chunkCount = 0;
|
|
||||||
|
|
||||||
File testFolder = new File(TEST_DATA_PATH);
|
|
||||||
File[] chunkFiles = testFolder.listFiles();
|
|
||||||
for (int i = 0; i < chunkFiles.length; i++)
|
|
||||||
{
|
|
||||||
// chunk file parsing //
|
|
||||||
|
|
||||||
File chunkFile = chunkFiles[i];
|
|
||||||
|
|
||||||
String fileName = chunkFile.getName(); // C[0,-3]
|
|
||||||
fileName = fileName.replace("C", "").replace("[", "").replace("]", "");
|
|
||||||
int xPos = Integer.parseInt(fileName.split(",")[0]);
|
|
||||||
int zPos = Integer.parseInt(fileName.split(",")[1]);
|
|
||||||
DhChunkPos pos = new DhChunkPos(xPos, zPos);
|
|
||||||
|
|
||||||
if (i % 100 == 0)
|
|
||||||
{
|
|
||||||
System.out.println(i + "/" + chunkFiles.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
LightingTestChunkWrapper chunk = new LightingTestChunkWrapper(pos, chunkFile);
|
|
||||||
chunkCount++;
|
|
||||||
|
|
||||||
ArrayList<IChunkWrapper> nearbyChunkList = new ArrayList<>(1);
|
|
||||||
nearbyChunkList.add(chunk);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// lighting //
|
|
||||||
|
|
||||||
long startTime = System.nanoTime();
|
|
||||||
DhLightingEngine.INSTANCE.lightChunk(chunk, nearbyChunkList, LodUtil.MAX_MC_LIGHT);
|
|
||||||
long endTime = System.nanoTime();
|
|
||||||
totalNanoTime += endTime - startTime;
|
|
||||||
}
|
|
||||||
long timeMs = totalNanoTime / 1_000_000;
|
|
||||||
|
|
||||||
|
|
||||||
System.out.println("chunks: ["+chunkCount+"], total Time: ["+timeMs+"], avg time: ["+(timeMs/(double)chunkCount)+"]");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user