diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java index ddcefaf41..0e0243040 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java @@ -25,7 +25,6 @@ import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; @@ -47,6 +46,8 @@ public class BeaconRenderHandler private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + private static final int BEAM_TOP_Y = 6_000; + /** if this is null then the other handler is probably null too, but just in case */ private final BeaconBeamRepo beaconBeamRepo; @@ -79,7 +80,7 @@ public class BeaconRenderHandler // level loading/unloading // //=========================// - public void setBeaconBeamsForChunk(DhChunkPos chunkPos, java.util.List newBeamList) + public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList) { // synchronized to prevent two threads from updating the same chunk at the same time synchronized (this) @@ -96,7 +97,7 @@ public class BeaconRenderHandler } // get existing beams - java.util.List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos); + List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos); HashMap existingBeamByPos = new HashMap<>(existingBeamList.size()); for (int i = 0; i < existingBeamList.size(); i++) { @@ -120,7 +121,13 @@ public class BeaconRenderHandler if (existingBeam != null && newBeam != null) { - // beam still exists in chunk, do nothing + // beam still exists in chunk + if (!existingBeam.color.equals(newBeam.color)) + { + // beam colors were changed + this.beaconBeamRepo.save(newBeam); + this.updateBeaconColor(newBeam); + } } else if (existingBeam == null && newBeam != null) { @@ -176,8 +183,7 @@ public class BeaconRenderHandler { DhApiRenderableBox beaconBox = new DhApiRenderableBox( new DhApiVec3f(beacon.pos.x, beacon.pos.y+1, beacon.pos.z), - new DhApiVec3f(beacon.pos.x+1, 6_000, beacon.pos.z+1), - // TODO calculate color + new DhApiVec3f(beacon.pos.x+1, BEAM_TOP_Y, beacon.pos.z+1), beacon.color ); @@ -210,4 +216,21 @@ public class BeaconRenderHandler }); } + private void updateBeaconColor(BeaconBeamDTO newBeam) + { + DhBlockPos pos = newBeam.pos; + for (int i = 0; i < this.beaconBoxGroup.size(); i++) + { + DhApiRenderableBox box = this.beaconBoxGroup.get(i); + if (box.minPos.x == pos.x + && box.minPos.y == pos.y+1 // plus 1 because the beam starts above the beacon + && box.minPos.z == pos.z) + { + box.color = newBeam.color; + this.beaconBoxGroup.triggerBoxChange(); + break; + } + } + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java index 222fcbb9b..6e4b3acc9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.util; +import java.awt.*; + /** * Handles the bit-wise math used when * dealing with colors stored as integers. @@ -212,4 +214,7 @@ public class ColorUtil ",B:" + getBlue(color); } + public static Color toColorObjRGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color)); } + public static Color toColorObjRGBA(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index 68fe9a194..8ddef3213 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.block; import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; +import java.awt.*; import java.util.Arrays; import java.util.List; @@ -92,4 +93,7 @@ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper boolean isBeaconBlock(); boolean isBeaconBaseBlock(); + Color getMapColor(); + boolean isGlassBlock(); + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java index ea774036b..f7062d774 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java @@ -29,11 +29,10 @@ import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; +import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; import java.util.List; public interface IChunkWrapper extends IBindable @@ -251,7 +250,7 @@ public interface IChunkWrapper extends IBindable default List getAllActiveBeacons(ArrayList neighbourChunkList) { - ArrayList beaconPosList = new ArrayList<>(); + ArrayList beaconBeamList = new ArrayList<>(); AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(this, neighbourChunkList); @@ -266,21 +265,30 @@ public interface IChunkWrapper extends IBindable IBlockStateWrapper block = this.getBlockState(relPos); if (block.isBeaconBlock()) { - if (isBeaconActive(pos, adjacentChunkHolder)) + Color beaconColor = getBeaconColor(pos, adjacentChunkHolder); + if (beaconColor != null) { - BeaconBeamDTO beam = new BeaconBeamDTO(blockPosList.get(i), Color.WHITE); - beaconPosList.add(beam); + BeaconBeamDTO beam = new BeaconBeamDTO(blockPosList.get(i), beaconColor); + beaconBeamList.add(beam); } } } - return beaconPosList; + return beaconBeamList; } - static boolean isBeaconActive(DhBlockPos beaconPos, AdjacentChunkHolder chunkHolder) + /** @return Null if the position isn't valid for a beacon beam. */ + @Nullable + static Color getBeaconColor(DhBlockPos beaconPos, AdjacentChunkHolder chunkHolder) { DhBlockPos beaconRelPos = beaconPos.convertToChunkRelativePos(); DhBlockPos baseRelPos = new DhBlockPos(0, beaconRelPos.y-1, 0); + + + //===========================// + // check for the base blocks // + //===========================// + for (int x = -1; x<= 1; x++) { for (int z = -1; z <= 1; z++) @@ -295,15 +303,53 @@ public interface IChunkWrapper extends IBindable IBlockStateWrapper block = chunk.getBlockState(baseRelPos.x, baseRelPos.y, baseRelPos.z); if (!block.isBeaconBaseBlock()) { - return false; + return null; } } } } - // TODO check if the sky is visible - return true; + + //=========================// + // get the beacon color // + // and check for occlusion // + //=========================// + + int red = 0; + int green = 0; + int blue = 0; + boolean glassBlockFound = false; + + IChunkWrapper centerChunk = chunkHolder.getByBlockPos(beaconPos.x, beaconPos.z); + int maxY = centerChunk.getMaxNonEmptyHeight(); + for (int y = beaconRelPos.y+1; y <= maxY; y++) + { + IBlockStateWrapper block = centerChunk.getBlockState(beaconRelPos.x, y, beaconRelPos.z); + if (!block.isAir() && block.getOpacity() == IBlockStateWrapper.FULLY_OPAQUE) + { + return null; + } + + if (block.isGlassBlock() + // ignore invisible blocks (which have pure black as their map color, luckily black stained-glass is actually extremely dark gray) + && !block.getMapColor().equals(Color.BLACK)) + { + red += block.getMapColor().getRed(); + green += block.getMapColor().getGreen(); + blue += block.getMapColor().getBlue(); + + if (glassBlockFound) + { + red /= 2; + green /= 2; + blue /= 2; + } + glassBlockFound = true; + } + } + + return glassBlockFound ? new Color(red, green, blue) : Color.WHITE; } diff --git a/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java b/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java index 58eb1bdb0..269498454 100644 --- a/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java +++ b/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java @@ -22,6 +22,8 @@ package testItems.lightingEngine; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import tests.LightingEngineTest; +import java.awt.*; + /** * @see LightingEngineTest * @see LightingTestChunkWrapper @@ -109,6 +111,11 @@ public class LightingTestBlockStateWrapper implements IBlockStateWrapper @Override public boolean isBeaconBaseBlock() { throw new UnsupportedOperationException("Not Implemented"); } + @Override + public Color getMapColor() { throw new UnsupportedOperationException("Not Implemented"); } + @Override + public boolean isGlassBlock() { throw new UnsupportedOperationException("Not Implemented"); } + }