Clean up beacon render handling logic

This commit is contained in:
James Seibel
2024-07-08 07:32:29 -05:00
parent 9e13b27197
commit c45f9f442f
8 changed files with 288 additions and 187 deletions
@@ -32,6 +32,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler;
import com.seibel.distanthorizons.core.render.renderer.generic.CloudRenderHandler;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory;
@@ -71,14 +72,10 @@ public abstract class AbstractDhLevel implements IDhLevel
/** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */
protected final ConcurrentHashMap<Long, HashSet<DhChunkPos>> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>();
protected final IDhApiRenderableBoxGroup beaconBoxGroup;
protected final HashMap<DhBlockPos, AtomicInteger> beaconRefCountByBlockPos = new HashMap<>();
protected boolean beaconGroupBound = false;
/** Will be null if clouds shouldn't be rendered for this level. */
@Nullable
protected CloudRenderHandler cloudRenderHandler;
protected BeaconRenderHandler beaconRenderHandler;
@@ -89,14 +86,12 @@ public abstract class AbstractDhLevel implements IDhLevel
protected AbstractDhLevel()
{
this.chunkToLodBuilder = new ChunkToLodBuilder();
this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(new ArrayList<>(0));
this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded());
this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get()));
}
/**
* Creating the repos requires access to the level file, which isn't
* available at constructor time.
*/
protected void createAndSetSupportingRepos(File databaseFile)
{
// chunk hash
@@ -125,6 +120,28 @@ public abstract class AbstractDhLevel implements IDhLevel
this.beaconBeamRepo = newBeaconBeamRepo;
}
/** handles any setup that needs the repos to be created */
protected void runRepoReliantSetup()
{
GenericObjectRenderer genericRenderer = this.getGenericRenderer();
if (genericRenderer != null)
{
// only add clouds for certain dimension types
if (!this.getLevelWrapper().hasCeiling()
&& !this.getLevelWrapper().getDimensionType().isTheEnd())
{
this.cloudRenderHandler = new CloudRenderHandler(this, genericRenderer);
}
// shouldn't happen, but just in case
if (this.beaconBeamRepo != null)
{
this.beaconRenderHandler = new BeaconRenderHandler(this.beaconBeamRepo, genericRenderer);
}
}
}
//=================//
@@ -210,184 +227,32 @@ public abstract class AbstractDhLevel implements IDhLevel
// beacon handling //
//=================//
@Override
public List<BeaconBeamDTO> getAllBeamsForSectionPos(long pos)
{
if (this.beaconBeamRepo != null)
{
return this.beaconBeamRepo.getAllBeamsForPos(pos);
}
else
{
return new ArrayList<>(0);
}
}
@Override
public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List<BeaconBeamDTO> newBeamList)
{
// synchronized to prevent two threads from updating the same chunk at the same time
synchronized (this)
if (this.beaconRenderHandler != null)
{
GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer();
// should always be non-null, but just in case
if (this.beaconBeamRepo != null
&& genericObjectRenderer != null)
{
HashSet<DhBlockPos> allPosSet = new HashSet<>();
// sort new beams
HashMap<DhBlockPos, BeaconBeamDTO> newBeamByPos = new HashMap<>(newBeamList.size());
for (int i = 0; i < newBeamList.size(); i++)
{
BeaconBeamDTO beam = newBeamList.get(i);
newBeamByPos.put(beam.pos, beam);
allPosSet.add(beam.pos);
}
// get existing beams
List<BeaconBeamDTO> existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos);
HashMap<DhBlockPos, BeaconBeamDTO> existingBeamByPos = new HashMap<>(existingBeamList.size());
for (int i = 0; i < existingBeamList.size(); i++)
{
BeaconBeamDTO beam = existingBeamList.get(i);
existingBeamByPos.put(beam.pos, beam);
allPosSet.add(beam.pos);
}
for (DhBlockPos beaconPos : allPosSet)
{
if (!chunkPos.contains(beaconPos))
{
// don't update beacons outside the updated chunk
continue;
}
BeaconBeamDTO existingBeam = existingBeamByPos.get(beaconPos);
BeaconBeamDTO newBeam = newBeamByPos.get(beaconPos);
if (existingBeam != null && newBeam != null)
{
// beam still exists in chunk, do nothing
}
else if (existingBeam == null && newBeam != null)
{
// new beam found, add to DB
this.beaconBeamRepo.save(newBeam);
this.startRenderingBeacon(newBeam);
}
else if (existingBeam != null && newBeam == null)
{
// beam no longer exists at position, remove
this.beaconBeamRepo.deleteWithKey(beaconPos); // TODO broken when updating adjacent chunks
this.stopRenderingBeaconAtPos(beaconPos);
}
}
}
this.beaconRenderHandler.setBeaconBeamsForChunk(chunkPos, newBeamList);
}
}
@Override
public void loadBeaconBeamsInPos(long pos)
{
GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer();
// should always be non-null, but just in case
if (this.beaconBeamRepo != null
&& genericObjectRenderer != null)
if (this.beaconRenderHandler != null)
{
// generic setup is delayed to allow for the main renderer to start up
if (!this.beaconGroupBound)
{
this.beaconGroupBound = true;
genericObjectRenderer.add(this.beaconBoxGroup);
if (!this.getLevelWrapper().hasCeiling()
&& !this.getLevelWrapper().getDimensionType().isTheEnd())
{
this.cloudRenderHandler = new CloudRenderHandler(this, genericObjectRenderer);
}
}
// get beams in pos
List<BeaconBeamDTO> existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos);
for (int i = 0; i < existingBeamList.size(); i++)
{
BeaconBeamDTO newBeam = existingBeamList.get(i);
this.startRenderingBeacon(newBeam);
}
this.beaconRenderHandler.loadBeaconBeamsInPos(pos);
}
}
@Override
public void unloadBeaconBeamsInPos(long pos)
{
GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer();
// should always be non-null, but just in case
if (this.beaconBeamRepo != null
&& genericObjectRenderer != null)
if (this.beaconRenderHandler != null)
{
// get beams in pos
List<BeaconBeamDTO> existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos);
for (int i = 0; i < existingBeamList.size(); i++)
{
BeaconBeamDTO beam = existingBeamList.get(i);
this.stopRenderingBeaconAtPos(beam.pos);
}
this.beaconRenderHandler.unloadBeaconBeamsInPos(pos);
}
}
private void startRenderingBeacon(BeaconBeamDTO beacon)
{
this.beaconRefCountByBlockPos.compute(beacon.pos, (beamPos, beaconRefCount) ->
{
if (beaconRefCount == null) { beaconRefCount = new AtomicInteger(); }
if (beaconRefCount.getAndIncrement() == 0)
{
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
beacon.color
);
this.beaconBoxGroup.add(beaconBox);
this.beaconBoxGroup.triggerBoxChange();
}
return beaconRefCount;
});
}
private void stopRenderingBeaconAtPos(DhBlockPos beaconPos)
{
this.beaconRefCountByBlockPos.compute(beaconPos, (pos, beaconRefCount) ->
{
if (beaconRefCount != null
&& beaconRefCount.decrementAndGet() <= 0)
{
this.beaconBoxGroup.removeIf((box) ->
box.minPos.x == beaconPos.x
&& box.minPos.y == beaconPos.y+1 // plus 1 because the beam starts above the beacon
&& box.minPos.z == beaconPos.z
);
this.beaconBoxGroup.triggerBoxChange();
return null;
}
else
{
return beaconRefCount;
}
});
}
//================//
@@ -400,11 +265,6 @@ public abstract class AbstractDhLevel implements IDhLevel
this.chunkToLodBuilder.close();
GenericObjectRenderer genericObjectRenderer = this.getGenericRenderer();
if (genericObjectRenderer != null)
{
genericObjectRenderer.remove(this.beaconBoxGroup.getId());
this.beaconGroupBound = false;
}
}
}
@@ -66,6 +66,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
this.clientside = new ClientLevelModule(this);
this.createAndSetSupportingRepos(this.dataFileHandler.repo.databaseFile);
this.runRepoReliantSetup();
if (enableRendering)
{
@@ -72,6 +72,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
this.serverside = new ServerLevelModule(this, saveStructure);
this.clientside = new ClientLevelModule(this);
this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile);
this.runRepoReliantSetup();
LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure);
}
@@ -54,6 +54,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
this.serverLevelWrapper = serverLevelWrapper;
this.serverside = new ServerLevelModule(this, saveStructure);
this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile);
this.runRepoReliantSetup();
LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure);
}
@@ -49,7 +49,6 @@ public interface IDhLevel extends AutoCloseable
void updateChunkAsync(IChunkWrapper chunk);
void loadBeaconBeamsInPos(long pos);
List<BeaconBeamDTO> getAllBeamsForSectionPos(long pos);
void setBeaconBeamsForChunk(DhChunkPos chunkPos, List<BeaconBeamDTO> beamList);
void unloadBeaconBeamsInPos(long pos);
@@ -0,0 +1,228 @@
/*
* 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 com.seibel.distanthorizons.core.render.renderer.generic;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
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;
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.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class BeaconRenderHandler
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
/** if this is null then the other handler is probably null too, but just in case */
private final BeaconBeamRepo beaconBeamRepo;
private final IDhApiRenderableBoxGroup beaconBoxGroup;
private final HashMap<DhBlockPos, AtomicInteger> beaconRefCountByBlockPos = new HashMap<>();
//=============//
// constructor //
//=============//
public BeaconRenderHandler(@NotNull BeaconBeamRepo beaconBeamRepo, @NotNull GenericObjectRenderer renderer)
{
this.beaconBeamRepo = beaconBeamRepo;
this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(new ArrayList<>(0));
this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded());
this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get()));
renderer.add(this.beaconBoxGroup);
}
//
//
//
public List<BeaconBeamDTO> getAllBeamsForSectionPos(long pos)
{
if (this.beaconBeamRepo != null)
{
return this.beaconBeamRepo.getAllBeamsForPos(pos);
}
else
{
return new ArrayList<>(0);
}
}
public void setBeaconBeamsForChunk(DhChunkPos chunkPos, java.util.List<BeaconBeamDTO> newBeamList)
{
// TODO move beacon handling to its own BeaconRenderHandler class
// synchronized to prevent two threads from updating the same chunk at the same time
synchronized (this)
{
HashSet<DhBlockPos> allPosSet = new HashSet<>();
// sort new beams
HashMap<DhBlockPos, BeaconBeamDTO> newBeamByPos = new HashMap<>(newBeamList.size());
for (int i = 0; i < newBeamList.size(); i++)
{
BeaconBeamDTO beam = newBeamList.get(i);
newBeamByPos.put(beam.pos, beam);
allPosSet.add(beam.pos);
}
// get existing beams
java.util.List<BeaconBeamDTO> existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos);
HashMap<DhBlockPos, BeaconBeamDTO> existingBeamByPos = new HashMap<>(existingBeamList.size());
for (int i = 0; i < existingBeamList.size(); i++)
{
BeaconBeamDTO beam = existingBeamList.get(i);
existingBeamByPos.put(beam.pos, beam);
allPosSet.add(beam.pos);
}
for (DhBlockPos beaconPos : allPosSet)
{
if (!chunkPos.contains(beaconPos))
{
// don't update beacons outside the updated chunk
continue;
}
BeaconBeamDTO existingBeam = existingBeamByPos.get(beaconPos);
BeaconBeamDTO newBeam = newBeamByPos.get(beaconPos);
if (existingBeam != null && newBeam != null)
{
// beam still exists in chunk, do nothing
}
else if (existingBeam == null && newBeam != null)
{
// new beam found, add to DB
this.beaconBeamRepo.save(newBeam);
this.startRenderingBeacon(newBeam);
}
else if (existingBeam != null && newBeam == null)
{
// beam no longer exists at position, remove from DB
this.beaconBeamRepo.deleteWithKey(beaconPos); // TODO broken when updating adjacent chunks
this.stopRenderingBeaconAtPos(beaconPos);
}
}
}
}
public void loadBeaconBeamsInPos(long pos)
{
// get all beams in pos
List<BeaconBeamDTO> existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos);
for (int i = 0; i < existingBeamList.size(); i++)
{
BeaconBeamDTO newBeam = existingBeamList.get(i);
this.startRenderingBeacon(newBeam);
}
}
public void unloadBeaconBeamsInPos(long pos)
{
// get all beams in pos
List<BeaconBeamDTO> existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos);
for (int i = 0; i < existingBeamList.size(); i++)
{
BeaconBeamDTO beam = existingBeamList.get(i);
this.stopRenderingBeaconAtPos(beam.pos);
}
}
//
//
//
private void startRenderingBeacon(BeaconBeamDTO beacon)
{
this.beaconRefCountByBlockPos.compute(beacon.pos, (beamPos, beaconRefCount) ->
{
if (beaconRefCount == null) { beaconRefCount = new AtomicInteger(); }
if (beaconRefCount.getAndIncrement() == 0)
{
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
beacon.color
);
this.beaconBoxGroup.add(beaconBox);
this.beaconBoxGroup.triggerBoxChange();
}
return beaconRefCount;
});
}
private void stopRenderingBeaconAtPos(DhBlockPos beaconPos)
{
this.beaconRefCountByBlockPos.compute(beaconPos, (pos, beaconRefCount) ->
{
if (beaconRefCount != null
&& beaconRefCount.decrementAndGet() <= 0)
{
this.beaconBoxGroup.removeIf((box) ->
box.minPos.x == beaconPos.x
&& box.minPos.y == beaconPos.y+1 // plus 1 because the beam starts above the beacon
&& box.minPos.z == beaconPos.z
);
this.beaconBoxGroup.triggerBoxChange();
return null;
}
else
{
return beaconRefCount;
}
});
}
}
@@ -60,8 +60,10 @@ public class CloudRenderHandler
private final IDhApiRenderableBoxGroup[][] boxGroupByOffset = new IDhApiRenderableBoxGroup[3][3];
private final IDhLevel level;
private final GenericObjectRenderer renderer;
private float moveSpeedInBlocksPerSecond = 3.0f;
private boolean disabledWarningLogged = false;
@@ -72,12 +74,7 @@ public class CloudRenderHandler
public CloudRenderHandler(IDhLevel level, GenericObjectRenderer renderer)
{
this.level = level;
if (!renderer.getUseInstancedRendering())
{
LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled.");
}
this.renderer = renderer;
@@ -174,11 +171,7 @@ public class CloudRenderHandler
CloudParams params = new CloudParams(textureWidth, x, z);
boxGroup.setPreRenderFunc((renderParam) -> this.preRender(params));
// we only stop before adding to the renderer to prevent accidental issues with null pointers and such
if (renderer.getUseInstancedRendering())
{
renderer.add(boxGroup);
}
renderer.add(boxGroup);
this.boxGroupByOffset[x+1][z+1] = boxGroup;
}
}
@@ -195,6 +188,15 @@ public class CloudRenderHandler
return;
}
if (!this.renderer.getUseInstancedRendering())
{
if (!this.disabledWarningLogged)
{
this.disabledWarningLogged = true;
LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled.");
}
return;
}
//================//
@@ -629,7 +629,16 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister
// getters //
//=========//
public boolean getUseInstancedRendering() { return this.useInstancedRendering; }
/** @throws IllegalStateException if {@link #init()} function hasn't been called yet */
public boolean getUseInstancedRendering() throws IllegalStateException
{
if (!this.init)
{
throw new IllegalStateException("GL initialization hasn't been completed.");
}
return this.useInstancedRendering;
}