Add beacon nearby culling
This commit is contained in:
+157
-26
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.render.renderer.generic;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
|
||||
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
|
||||
import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
|
||||
@@ -32,6 +33,8 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
|
||||
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -41,6 +44,11 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class BeaconRenderHandler
|
||||
{
|
||||
@@ -49,13 +57,23 @@ public class BeaconRenderHandler
|
||||
|
||||
private static final int BEAM_TOP_Y = 6_000;
|
||||
|
||||
/** how often should we check if a beacon should be culled? */
|
||||
private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000;
|
||||
|
||||
|
||||
|
||||
/** if this is null then the other handler is probably null too, but just in case */
|
||||
private final BeaconBeamRepo beaconBeamRepo;
|
||||
|
||||
private final ReentrantLock updateLock = new ReentrantLock();
|
||||
|
||||
private final IDhApiRenderableBoxGroup beaconBoxGroup;
|
||||
private final ArrayList<DhApiRenderableBox> fullBeaconBoxList = new ArrayList<>();
|
||||
private final HashSet<DhBlockPos> beaconBlockPosSet = new HashSet<>();
|
||||
|
||||
private boolean cullingThreadRunning = false;
|
||||
private boolean updateRenderDataNextFrame = false;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
@@ -71,7 +89,7 @@ public class BeaconRenderHandler
|
||||
this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
|
||||
this.beaconBoxGroup.setSsaoEnabled(false);
|
||||
this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded());
|
||||
this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get()));
|
||||
this.beaconBoxGroup.setPreRenderFunc(this::beforeRender);
|
||||
|
||||
renderer.add(this.beaconBoxGroup);
|
||||
}
|
||||
@@ -178,49 +196,162 @@ public class BeaconRenderHandler
|
||||
|
||||
private void startRenderingBeacon(BeaconBeamDTO beacon)
|
||||
{
|
||||
if (this.beaconBlockPosSet.add(beacon.blockPos))
|
||||
try
|
||||
{
|
||||
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
|
||||
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() +1, beacon.blockPos.getZ()),
|
||||
new DhApiVec3d(beacon.blockPos.getX() +1, BEAM_TOP_Y, beacon.blockPos.getZ() +1),
|
||||
beacon.color,
|
||||
EDhApiBlockMaterial.ILLUMINATED
|
||||
);
|
||||
this.updateLock.lock();
|
||||
|
||||
this.beaconBoxGroup.add(beaconBox);
|
||||
this.beaconBoxGroup.triggerBoxChange();
|
||||
if (this.beaconBlockPosSet.add(beacon.blockPos))
|
||||
{
|
||||
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
|
||||
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
|
||||
new DhApiVec3d(beacon.blockPos.getX() + 1, BEAM_TOP_Y, beacon.blockPos.getZ() + 1),
|
||||
beacon.color,
|
||||
EDhApiBlockMaterial.ILLUMINATED
|
||||
);
|
||||
|
||||
this.beaconBoxGroup.add(beaconBox);
|
||||
this.fullBeaconBoxList.add(beaconBox);
|
||||
this.beaconBoxGroup.triggerBoxChange();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.updateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRenderingBeaconAtPos(DhBlockPos beaconPos)
|
||||
{
|
||||
if (this.beaconBlockPosSet.remove(beaconPos))
|
||||
try
|
||||
{
|
||||
this.beaconBoxGroup.removeIf((box) ->
|
||||
this.updateLock.lock();
|
||||
|
||||
if (this.beaconBlockPosSet.remove(beaconPos))
|
||||
{
|
||||
return box.minPos.x == beaconPos.getX()
|
||||
&& box.minPos.y == beaconPos.getY() +1 // plus 1 because the beam starts above the beacon
|
||||
&& box.minPos.z == beaconPos.getZ();
|
||||
});
|
||||
this.beaconBoxGroup.triggerBoxChange();
|
||||
Predicate<DhApiRenderableBox> removePredicate = (DhApiRenderableBox box) ->
|
||||
{
|
||||
return box.minPos.x == beaconPos.getX()
|
||||
&& box.minPos.y == beaconPos.getY() + 1 // plus 1 because the beam starts above the beacon
|
||||
&& box.minPos.z == beaconPos.getZ();
|
||||
};
|
||||
this.beaconBoxGroup.removeIf(removePredicate);
|
||||
this.fullBeaconBoxList.removeIf(removePredicate);
|
||||
|
||||
this.beaconBoxGroup.triggerBoxChange();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.updateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBeaconColor(BeaconBeamDTO newBeam)
|
||||
{
|
||||
DhBlockPos pos = newBeam.blockPos;
|
||||
for (int i = 0; i < this.beaconBoxGroup.size(); i++)
|
||||
try
|
||||
{
|
||||
DhApiRenderableBox box = this.beaconBoxGroup.get(i);
|
||||
if (box.minPos.x == pos.getX()
|
||||
&& box.minPos.y == pos.getY() +1 // plus 1 because the beam starts above the beacon
|
||||
&& box.minPos.z == pos.getZ())
|
||||
this.updateLock.lock();
|
||||
|
||||
DhBlockPos pos = newBeam.blockPos;
|
||||
for (int i = 0; i < this.fullBeaconBoxList.size(); i++)
|
||||
{
|
||||
box.color = newBeam.color;
|
||||
this.beaconBoxGroup.triggerBoxChange();
|
||||
break;
|
||||
DhApiRenderableBox box = this.fullBeaconBoxList.get(i);
|
||||
if (box.minPos.x == pos.getX()
|
||||
&& box.minPos.y == pos.getY() + 1 // plus 1 because the beam starts above the beacon
|
||||
&& box.minPos.z == pos.getZ())
|
||||
{
|
||||
box.color = newBeam.color;
|
||||
this.beaconBoxGroup.triggerBoxChange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.updateLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void beforeRender(DhApiRenderParam renderEventParam)
|
||||
{
|
||||
// this could be called only when the player moves, but it's an extremely cheap check,
|
||||
// so there isn't much of a reason to bother
|
||||
this.tryUpdateBeaconCullingAsync();
|
||||
|
||||
// this must be called on the render thread to prevent concurrency issues
|
||||
if (this.updateRenderDataNextFrame)
|
||||
{
|
||||
this.beaconBoxGroup.triggerBoxChange();
|
||||
this.updateRenderDataNextFrame = false;
|
||||
}
|
||||
this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get());
|
||||
}
|
||||
/** does nothing if the culling thread is already running */
|
||||
private void tryUpdateBeaconCullingAsync()
|
||||
{
|
||||
ThreadPoolExecutor executor = ThreadPoolUtil.getBeaconCullingExecutor();
|
||||
if (executor != null
|
||||
&& !this.cullingThreadRunning)
|
||||
{
|
||||
this.cullingThreadRunning = true;
|
||||
|
||||
try
|
||||
{
|
||||
executor.execute(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep(MAX_CULLING_FREQUENCY_IN_MS);
|
||||
}
|
||||
catch (InterruptedException ignore)
|
||||
{
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// lock to make sure we don't try adding beacons to the arrays while processing them
|
||||
this.updateLock.lock();
|
||||
|
||||
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
|
||||
double mcRenderDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
|
||||
|
||||
|
||||
// Clear the existing box group so we can re-populate it.
|
||||
// Since the box group is only used when we trigger an update, clearing it here
|
||||
// and repopulating it is fine.
|
||||
this.beaconBoxGroup.clear();
|
||||
|
||||
// While iterating over every beacon isn't a great way of doing this,
|
||||
// when 940 beacons were tested this only took ~0.9 Milliseconds, so as long as
|
||||
// we aren't freezing the render thread this method of culling works just fine.
|
||||
for (DhApiRenderableBox box : this.fullBeaconBoxList)
|
||||
{
|
||||
// if a beacon is outside the vanilla render distance render it
|
||||
double distance = Vec3d.getDistance(cameraPos, box.minPos);
|
||||
if (distance > mcRenderDistance)
|
||||
{
|
||||
this.beaconBoxGroup.add(box);
|
||||
}
|
||||
}
|
||||
|
||||
this.updateRenderDataNextFrame = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Unexpected issue while updating beacon culling. Error: " + e.getMessage(), e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.updateLock.unlock();
|
||||
this.cullingThreadRunning = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (RejectedExecutionException ignore)
|
||||
{ /* If this happens that means everything is already shut down and no culling is necessary */ }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.seibel.distanthorizons.core.util.math;
|
||||
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
|
||||
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
|
||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
|
||||
/**
|
||||
@@ -134,7 +135,6 @@ public class Vec3d extends DhApiVec3d
|
||||
|
||||
public Vec3d copy() { return new Vec3d(this.x, this.y, this.z); }
|
||||
|
||||
// Forge start
|
||||
public Vec3d(double[] values) { this.set(values); }
|
||||
|
||||
public void set(double[] values)
|
||||
@@ -144,4 +144,25 @@ public class Vec3d extends DhApiVec3d
|
||||
this.z = values[2];
|
||||
}
|
||||
|
||||
public static double getManhattanDistance(DhApiVec3d a, DhApiVec3d b)
|
||||
{
|
||||
return Math.abs(a.x - b.x)
|
||||
+ Math.abs(a.y - b.y)
|
||||
+ Math.abs(a.z - b.z);
|
||||
}
|
||||
|
||||
public static double getDistance(DhApiVec3d a, DhApiVec3d b)
|
||||
{
|
||||
return Math.sqrt(Math.pow(a.x - b.x, 2)
|
||||
+ Math.pow(a.y - b.y, 2)
|
||||
+ Math.pow(a.z - b.z, 2));
|
||||
}
|
||||
/** slightly faster version of {@link Vec3d#getDistance} */
|
||||
public static double getSquaredDistance(DhApiVec3d a, DhApiVec3d b)
|
||||
{
|
||||
return Math.pow(a.x - b.x, 2)
|
||||
+ Math.pow(a.y - b.y, 2)
|
||||
+ Math.pow(a.z - b.z, 2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,6 +66,11 @@ public class ThreadPoolUtil
|
||||
@Nullable
|
||||
public static ThreadPoolExecutor getCleanupExecutor() { return cleanupThreadPool; }
|
||||
|
||||
public static final String BEACON_CULLING_THREAD_NAME = "Beacon Culling";
|
||||
private static ThreadPoolExecutor beaconCullingThreadPool;
|
||||
@Nullable
|
||||
public static ThreadPoolExecutor getBeaconCullingExecutor() { return beaconCullingThreadPool; }
|
||||
|
||||
|
||||
|
||||
//======================//
|
||||
@@ -109,6 +114,7 @@ public class ThreadPoolUtil
|
||||
worldGenThreadPool = new ConfigThreadPool(WORLD_GEN_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads, null);
|
||||
bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool(BUFFER_UPLOADER_THREAD_NAME);
|
||||
cleanupThreadPool = ThreadUtil.makeSingleThreadPool(CLEANUP_THREAD_NAME);
|
||||
beaconCullingThreadPool = ThreadUtil.makeSingleThreadPool(BEACON_CULLING_THREAD_NAME);
|
||||
|
||||
|
||||
|
||||
@@ -149,6 +155,7 @@ public class ThreadPoolUtil
|
||||
worldGenThreadPool.shutdownExecutorService();
|
||||
bufferUploaderThreadPool.shutdown();
|
||||
cleanupThreadPool.shutdown();
|
||||
beaconCullingThreadPool.shutdown();
|
||||
|
||||
|
||||
// worker threads
|
||||
|
||||
Reference in New Issue
Block a user