diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java index 699e3b7ff..104e90bb6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java @@ -47,8 +47,7 @@ public class DhLightingEngine DhChunkPos centerChunkPos = centerChunk.getChunkPos(); - - HashMap chunksByChunkPos = new HashMap<>(9); + AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk); // try-finally to handle the stableArray resources @@ -85,7 +84,7 @@ public class DhLightingEngine requestedAdjacentPositions.remove(chunk.getChunkPos()); // add the adjacent chunk - chunksByChunkPos.put(chunk.getChunkPos(), chunk); + adjacentChunkHolder.add(chunk); @@ -147,7 +146,7 @@ public class DhLightingEngine } // validate that at least 1 chunk was found - if (chunksByChunkPos.size() == 0) + if (adjacentChunkHolder.size() == 0) { LOGGER.warn("Attempted to generate lighting for position [" + centerChunkPos + "], but neither that chunk nor any adjacent chunks were found. No chunk lighting was performed."); return; @@ -156,12 +155,12 @@ public class DhLightingEngine // block light - this.propagateLightPosList(blockLightPosQueue, chunksByChunkPos, + this.propagateLightPosList(blockLightPosQueue, adjacentChunkHolder, (neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.x, relBlockPos.y, relBlockPos.z), (neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.x, relBlockPos.y, relBlockPos.z, newLightValue)); // sky light - this.propagateLightPosList(skyLightPosQueue, chunksByChunkPos, + this.propagateLightPosList(skyLightPosQueue, adjacentChunkHolder, (neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.x, relBlockPos.y, relBlockPos.z), (neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.x, relBlockPos.y, relBlockPos.z, newLightValue)); } @@ -183,9 +182,15 @@ public class DhLightingEngine /** Applies each {@link LightPos} from the queue to the given set of {@link IChunkWrapper}'s. */ private void propagateLightPosList( - StableLightPosStack lightPosQueue, HashMap chunksByChunkPos, + StableLightPosStack lightPosQueue, AdjacentChunkHolder adjacentChunkHolder, IGetLightFunc getLightFunc, ISetLightFunc setLightFunc) { + // these objects are saved so they can be mutated throughout the method, + // this reduces the number of allocations necessary, reducing GC pressure + final DhBlockPos neighbourBlockPos = new DhBlockPos(); + final DhBlockPos relNeighbourBlockPos = new DhBlockPos(); + + // update each light position while (!lightPosQueue.isEmpty()) { @@ -200,14 +205,13 @@ public class DhLightingEngine // propagate the lighting in each cardinal direction, IE: -x, +x, -y, +y, -z, +z for (EDhDirection direction : EDhDirection.CARDINAL_DIRECTIONS) { - DhBlockPos neighbourBlockPos = pos.offset(direction); - DhChunkPos neighbourChunkPos = new DhChunkPos(neighbourBlockPos); + pos.offset(direction, neighbourBlockPos); // mutates neighbourBlockPos // converting the block pos into a relative position is necessary for accessing the light values in the chunk - DhBlockPos relNeighbourBlockPos = neighbourBlockPos.convertToChunkRelativePos(); + neighbourBlockPos.convertToChunkRelativePos(relNeighbourBlockPos); // mutates relNeighbourBlockPos // only continue if the light position is inside one of our chunks - IChunkWrapper neighbourChunk = chunksByChunkPos.get(neighbourChunkPos); + IChunkWrapper neighbourChunk = adjacentChunkHolder.getByBlockPos(neighbourBlockPos.x, neighbourBlockPos.z); if (neighbourChunk == null) { // the light pos is outside our generator's range, ignore it @@ -274,6 +278,41 @@ public class DhLightingEngine } + /** holds the adjacent chunks without having to create new Pos objects */ + private static class AdjacentChunkHolder + { + ArrayList chunkArray = new ArrayList<>(9); + + + public AdjacentChunkHolder(IChunkWrapper centerWrapper) + { + this.chunkArray.add(centerWrapper); + } + + + public int size() { return this.chunkArray.size(); } + + public void add(IChunkWrapper centerWrapper) { this.chunkArray.add(centerWrapper); } + + public IChunkWrapper getByBlockPos(int blockX, int blockZ) + { + // >> 4 is equivalent to dividing by 16 + int chunkX = blockX >> 4; + int chunkZ = blockZ >> 4; + + // since there will only ever be 9 items in the array, this sequential search should be fast enough + for (IChunkWrapper chunk : this.chunkArray) + { + if (chunk != null + && chunk.getChunkPos().x == chunkX && chunk.getChunkPos().z == chunkZ) + { + return chunk; + } + } + return null; + } + } + /** * Holds all potential {@link LightPos} objects a lighting task may need. * This is done so existing {@link LightPos} objects can be repurposed instead of destroyed, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos.java index d7f9d650d..d673aa5be 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.pos; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.util.LodUtil; +import javax.annotation.Nullable; import java.util.Objects; public class DhBlockPos @@ -128,11 +129,39 @@ public class DhBlockPos return asLong(x, y, z); } - public DhBlockPos offset(EDhDirection direction) { return this.offset(direction.getNormal().x, direction.getNormal().y, direction.getNormal().z); } - public DhBlockPos offset(int x, int y, int z) { return new DhBlockPos(this.x + x, this.y + y, this.z + z); } + /** creates a new {@link DhBlockPos} with the given offset from the current pos. */ + public DhBlockPos offset(EDhDirection direction) { return this.offset(direction, null); } + /** if not null, mutates "mutablePos" so it matches the current pos after being offset. Otherwise creates a new {@link DhBlockPos}. */ + public DhBlockPos offset(EDhDirection direction, @Nullable DhBlockPos mutablePos) { return this.offset(direction.getNormal().x, direction.getNormal().y, direction.getNormal().z, mutablePos); } - /** Limits the block position to a value between 0 and 15 (inclusive) */ - public DhBlockPos convertToChunkRelativePos() + public DhBlockPos offset(int x, int y, int z) { return this.offset(x,y,z, null); } + public DhBlockPos offset(int x, int y, int z, @Nullable DhBlockPos mutablePos) + { + int newX = this.x + x; + int newY = this.y + y; + int newZ = this.z + z; + + if (mutablePos != null) + { + mutablePos.x = newX; + mutablePos.y = newY; + mutablePos.z = newZ; + + return mutablePos; + } + else + { + return new DhBlockPos(this.x + x, this.y + y, this.z + z); + } + } + + /** Returns a new {@link DhBlockPos} limits to a value between 0 and 15 (inclusive) */ + public DhBlockPos convertToChunkRelativePos() { return this.convertToChunkRelativePos(null); } + /** + * Limits the block position to a value between 0 and 15 (inclusive) + * If not null, mutates "mutableBlockPos" + */ + public DhBlockPos convertToChunkRelativePos(@Nullable DhBlockPos mutableBlockPos) { // move the position into the range -15 and +15 int relX = (this.x % LodUtil.CHUNK_WIDTH); @@ -143,7 +172,19 @@ public class DhBlockPos relZ = (relZ < 0) ? (relZ + LodUtil.CHUNK_WIDTH) : relZ; // the y value shouldn't need to be changed - return new DhBlockPos(relX, this.y, relZ); + + + if (mutableBlockPos != null) + { + mutableBlockPos.x = relX; + mutableBlockPos.z = relX; + + return mutableBlockPos; + } + else + { + return new DhBlockPos(relX, this.y, relZ); + } } /**