diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java
index 80ae01702..21995fced 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java
@@ -23,7 +23,9 @@ import com.seibel.distanthorizons.api.interfaces.events.IDhApiEventInjector;
import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory;
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGeneratorOverrideRegister;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory;
import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy;
+import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.override.DhApiWorldGeneratorOverrideRegister;
import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -127,6 +129,12 @@ public class DhApi
*/
public static IDhApiWrapperFactory wrapperFactory = null;
+ /**
+ * Used to create custom renderable objects.
+ * These objects can be added to the renderer in {@link IDhApiLevelWrapper}
+ * @since API 3.0.0
+ */
+ public static IDhApiCustomRenderObjectFactory customRenderObjectFactory = null;
}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java
new file mode 100644
index 000000000..dfedf3278
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java
@@ -0,0 +1,67 @@
+package com.seibel.distanthorizons.api.enums.rendering;
+
+/**
+ * contains the indices used by shaders to determine
+ * how different block types should be rendered.
+ *
+ * UNKNOWN,
+ * LEAVES,
+ * STONE,
+ * WOOD,
+ * METAL,
+ * DIRT,
+ * LAVA,
+ * DEEPSLATE,
+ * SNOW,
+ * SAND,
+ * TERRACOTTA,
+ * NETHER_STONE,
+ * WATER,
+ * GRASS,
+ * AIR,
+ * ILLUMINATED,
+ *
+ * @author James Seibel
+ * @since API 3.0.0
+ * @version 2024-7-11
+ */
+public enum EDhApiBlockMaterial
+{
+ UNKNOWN(0),
+ LEAVES(1),
+ STONE(2),
+ WOOD(3),
+ METAL(4),
+ DIRT(5),
+ LAVA(6),
+ DEEPSLATE(7),
+ SNOW(8),
+ SAND(9),
+ TERRACOTTA(10),
+ NETHER_STONE(11),
+ WATER(12),
+ GRASS(13),
+ /** shouldn't normally be needed, but just in case */
+ AIR(14),
+ ILLUMINATED(15); // Max value
+
+
+
+ public final byte index;
+
+ EDhApiBlockMaterial(int index) { this.index = (byte)index;}
+
+ public static EDhApiBlockMaterial getFromIndex(int index)
+ {
+ for(EDhApiBlockMaterial material : EDhApiBlockMaterial.values())
+ {
+ if (material.index == index)
+ {
+ return material;
+ }
+ }
+
+ return EDhApiBlockMaterial.UNKNOWN;
+ }
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java
index 682b04f06..8a71a090f 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java
@@ -30,12 +30,21 @@ import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
*/
public interface IDhApiBlockStateWrapper extends IDhApiUnsafeWrapper
{
+ /** @since API 1.0.0 */
boolean isAir();
+ /** @since API 1.0.0 */
boolean isSolid();
+ /** @since API 1.0.0 */
boolean isLiquid();
- // TODO:
- // boolean hasNoCollision();
- // boolean noFaceIsFullFace();
+ /**
+ * Returns the full serialized form of the given block
+ * as defined by DH's serialization methods.
+ * @since API 3.0.0
+ */
+ String getSerialString();
+ /** @since API 3.0.0 */
+ byte getMaterialId();
+
}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java
new file mode 100644
index 000000000..3209b07ba
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java
@@ -0,0 +1,46 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.api.interfaces.config.client;
+
+import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup;
+import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
+
+/**
+ * Distant Horizons' generic rendering configuration.
+ *
+ * @author James Seibel
+ * @version 2024-7-11
+ * @since API 3.0.0
+ */
+public interface IDhApiGenericRenderingConfig extends IDhApiConfigGroup
+{
+ /**
+ * If enabled DH will render generic objects into its terrain pass.
+ * This includes: clouds, beacons, and API added objects.
+ */
+ IDhApiConfigValue renderingEnabled();
+
+ /** If enabled DH will render beacon beams. */
+ IDhApiConfigValue beaconRenderingEnabled();
+
+ /** If enabled DH will render clouds. */
+ IDhApiConfigValue cloudRenderingEnabled();
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java
index d7c2792f4..f69869aeb 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java
@@ -41,6 +41,7 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup
IDhApiFogConfig fog();
IDhApiAmbientOcclusionConfig ambientOcclusion();
IDhApiNoiseTextureConfig noiseTexture();
+ IDhApiGenericRenderingConfig genericRendering();
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java
new file mode 100644
index 000000000..a57a8a56e
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java
@@ -0,0 +1,21 @@
+package com.seibel.distanthorizons.api.interfaces.data;
+
+/**
+ * Can be used to drastically speed up repeat read operations in {@link IDhApiTerrainDataRepo}.
+ *
+ * @see IDhApiTerrainDataRepo
+ *
+ * @author James Seibel
+ * @version 2024-7-14
+ * @since API 3.0.0
+ */
+public interface IDhApiTerrainDataCache
+{
+ /**
+ * Removes any data that's currently stored in this cache.
+ * This cane be done to free up memory or invalidate
+ * the cache so fresh data can be pulled in.
+ */
+ void clear();
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java
index 2a4f81e24..45f8c1d72 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java
@@ -29,6 +29,8 @@ import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
/**
* Used to interface with Distant Horizons' terrain data.
*
+ * @see IDhApiTerrainDataCache
+ *
* @author James Seibel
* @version 2023-6-22
* @since API 1.0.0
@@ -40,29 +42,50 @@ public interface IDhApiTerrainDataRepo
// getters //
//=========//
- /** Returns the terrain datapoint at the given block position, at/or containing the given Y position. */
- DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ);
- /** Returns every datapoint in the column located at the given block X and Z position top to bottom. */
- DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ);
+ /** @see IDhApiTerrainDataRepo#getSingleDataPointAtBlockPos(IDhApiLevelWrapper, int, int, int, IDhApiTerrainDataCache) */
+ default DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ) { return this.getSingleDataPointAtBlockPos(levelWrapper, blockPosX, blockPosY, blockPosZ, null); }
+ /**
+ * Returns the terrain datapoint at the given block position, at/or containing the given Y position.
+ * @since API 3.0.0
+ */
+ DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache);
+ /** @see IDhApiTerrainDataRepo#getColumnDataAtBlockPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
+ default DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ) { return this.getColumnDataAtBlockPos(levelWrapper, blockPosX, blockPosZ, null); }
+ /**
+ * Returns every datapoint in the column located at the given block X and Z position top to bottom.
+ * @since API 3.0.0
+ */
+ DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, IDhApiTerrainDataCache dataCache);
+
+ /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtChunkPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
+ default DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ) { return this.getAllTerrainDataAtChunkPos(levelWrapper, chunkPosX, chunkPosZ, null); }
/**
* Returns every datapoint in the given chunk's X and Z position.
*
* The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex]
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the chunk's position.
* The column data is ordered from top to bottom. Note: each column may have a different number of values.
+ *
+ * @since API 3.0.0
*/
- DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ);
+ DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, IDhApiTerrainDataCache dataCache);
+ /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtRegionPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */
+ default DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ) { return this.getAllTerrainDataAtRegionPos(levelWrapper, regionPosX, regionPosZ, null); }
/**
* Returns every datapoint in the given region's X and Z position.
*
* The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex]
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the region's position.
* The column data is ordered from top to bottom. Note: each column may have a different number of values.
+ *
+ * @since API 3.0.0
*/
- DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ);
+ DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, IDhApiTerrainDataCache dataCache);
+ /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper, byte, int, int, IDhApiTerrainDataCache) */
+ default DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ) { return this.getAllTerrainDataAtDetailLevelAndPos(levelWrapper, detailLevel, posX, posZ, null); }
/**
* Returns every datapoint in the column located at the given detail level and X/Z position.
* This can be used to return terrain data for non-standard sizes (IE 2x2 blocks or 2x2 chunks).
@@ -71,20 +94,42 @@ public interface IDhApiTerrainDataRepo
* Every increase doubles the width of the returned area.
* Example values: 0 = block, 1 = 2x2 blocks, 2 = 4x4 blocks, ... 4 = chunk (16x16 blocks), ... 9 = region (512x512 blocks)
* See {@link EDhApiDetailLevel} for more information.
+ *
+ * @since API 3.0.0
*/
- DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ);
+ DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache);
+
+
+
+ /** @see IDhApiTerrainDataRepo#raycast(IDhApiLevelWrapper, double, double, double, float, float, float, int, IDhApiTerrainDataCache) */
+ default DhApiResult raycast(
+ IDhApiLevelWrapper levelWrapper,
+ double rayOriginX, double rayOriginY, double rayOriginZ,
+ float rayDirectionX, float rayDirectionY, float rayDirectionZ,
+ int maxRayBlockLength)
+ {
+ return this.raycast(
+ levelWrapper,
+ rayOriginX, rayOriginY, rayOriginZ,
+ rayDirectionX, rayDirectionY, rayDirectionZ,
+ maxRayBlockLength,
+ null);
+ }
/**
* Returns the datapoint and position of the LOD
* at the end of the given ray.
*
* Will return "success" with a null datapoint if the ray reaches the max length without finding any data.
+ *
+ * @since API 3.0.0
*/
DhApiResult raycast(
IDhApiLevelWrapper levelWrapper,
double rayOriginX, double rayOriginY, double rayOriginZ,
float rayDirectionX, float rayDirectionY, float rayDirectionZ,
- int maxRayBlockLength);
+ int maxRayBlockLength,
+ IDhApiTerrainDataCache dataCache);
@@ -98,15 +143,27 @@ public interface IDhApiTerrainDataRepo
* Notes:
* - Only works if the given {@link IDhApiLevelWrapper} points to a loaded level.
* - If the player travels to this chunk, or the chunk is updated in some other way; your data will be replaced
- * by whatever the current chunk is.
- * - This method may not update the LOD data immediately. Any other chunks have
- * been queued to update, they will be handled first.
+ * by whatever the current chunk is.
+ * - This method may not update the LOD data immediately. Any other chunks that have
+ * been queued to update will be handled first.
*
* @param levelWrapper the level wrapper that the chunk should be saved to.
* @param chunkObjectArray see {@link IDhApiWorldGenerator#generateChunks} for what objects are expected.
* @throws ClassCastException if chunkObjectArray doesn't contain the right objects.
* The exception will contain the expected object(s).
*/
- public DhApiResult overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException;
+ DhApiResult overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException;
+
+
+
+ //=========//
+ // helpers //
+ //=========//
+
+ /**
+ * @return a {@link IDhApiTerrainDataCache} backed by {@link java.lang.ref.SoftReference}'s.
+ * @since API 3.0.0
+ */
+ IDhApiTerrainDataCache getSoftCache();
}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java
index 524742f04..ce3e70662 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java
@@ -79,11 +79,34 @@ public interface IDhApiWrapperFactory
- ///**
- // * Specifically designed to be used with the API.
- // *
- // * @throws ClassCastException with instructions on expected objects if the object couldn't be cast
- // */
- //IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException;
+ /**
+ * Constructs a {@link IDhApiBiomeWrapper} for use by other DhApi methods.
+ *
+ * @param resourceLocationString example: "minecraft:plains"
+ *
+ * @param levelWrapper Expects a {@link IDhApiLevelWrapper} returned by one of DH's {@link DhApi.Delayed#worldProxy} methods.
+ * A custom implementation of {@link IDhApiLevelWrapper} will not be accepted.
+ *
+ * @throws IOException if the resourceLocationString wasn't able to be parsed or converted into a valid {@link IDhApiBiomeWrapper}
+ * @throws ClassCastException if the wrong levelWrapper type was given
+ *
+ * @since API 3.0.0
+ */
+ IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException;
+
+ /**
+ * Constructs a {@link IDhApiBlockStateWrapper} for use by other DhApi methods.
+ * This returns the default blockstate for the given resource location.
+ *
+ * @param resourceLocationString examples: "minecraft:bedrock", "minecraft:stone", "minecraft:grass_block"
+ * @param levelWrapper Expects a {@link IDhApiBlockStateWrapper} returned by one of DH's {@link DhApi.Delayed#worldProxy} methods.
+ * A custom implementation of {@link IDhApiBlockStateWrapper} will not be accepted.
+ *
+ * @throws IOException if the resourceLocationString wasn't able to be parsed or converted into a valid {@link IDhApiBlockStateWrapper}
+ * @throws ClassCastException if the wrong levelWrapper type was given
+ *
+ * @since API 3.0.0
+ */
+ IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException;
}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java
index 44106b82a..33cebacd2 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java
@@ -21,7 +21,7 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering;
import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel;
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
+import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
/**
* Used to determine if a LOD should be rendered or is outside the
@@ -41,7 +41,7 @@ public interface IDhApiCullingFrustum extends IDhApiOverrideable
* @param worldMaxBlockY the highest block position this level allows.
* @param worldViewProjection the projection matrix used in this render pass.
*/
- void update(int worldMinBlockY, int worldMaxBlockY, Mat4f worldViewProjection);
+ void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f worldViewProjection);
/**
* returns true if the LOD bounds intersect this frustum
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java
new file mode 100644
index 000000000..ce1d6fcde
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java
@@ -0,0 +1,80 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.api.interfaces.override.rendering;
+
+import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
+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;
+
+/**
+ * @see IDhApiShaderProgram
+ *
+ * @author James Seibel
+ * @version 2024-7-11
+ * @since API 3.0.0
+ */
+public interface IDhApiGenericObjectShaderProgram extends IDhApiOverrideable
+{
+
+ /**
+ * If this method is called that means this program has the highest priority as defined by {@link IDhApiOverrideable#getPriority()}
+ * and gets to decide if it wants to be used to render this frame or not.
+ *
+ * If this method returns true then this program will be used for this frame.
+ * If this returns false then the default DH {@link IDhApiGenericObjectShaderProgram} will be used instead.
+ */
+ boolean overrideThisFrame();
+
+ /** @return the OpenGL ID for this shader program */
+ int getId();
+
+ /** Free any OpenGL objects owned by this program. */
+ void free();
+
+ /** Runs any necessary binding this program needs so rendering can be done. */
+ void bind(DhApiRenderParam renderEventParam);
+ /** Runs any necessary unbinding this program needs so rendering can be done by another program. */
+ void unbind();
+
+ /** Binds the given Vertex Buffer Object to this shader program for rendering. */
+ void bindVertexBuffer(int vbo);
+
+
+ /** sets up the necessary uniforms for rendering */
+ void fillIndirectUniformData(
+ DhApiRenderParam renderParameters,
+ DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
+ DhApiVec3d camPos);
+
+ /** sets up the necessary uniforms for rendering */
+ void fillSharedDirectUniformData(
+ DhApiRenderParam renderParameters,
+ DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
+ DhApiVec3d camPos);
+ void fillDirectUniformData(
+ DhApiRenderParam renderParameters,
+ IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box,
+ DhApiVec3d camPos);
+
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java
index 6f329121d..6844715cf 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java
@@ -21,10 +21,11 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering;
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
/**
+ * @see IDhApiGenericObjectShaderProgram
+ *
* @author James Seibel
* @version 2024-1-24
* @since API 2.0.0
@@ -57,7 +58,7 @@ public interface IDhApiShaderProgram extends IDhApiOverrideable
void fillUniformData(DhApiRenderParam renderParameters);
/** sets the vec3 that all DH verticies should be offset by when rendering */
- void setModelOffsetPos(Vec3f modelPos);
+ void setModelOffsetPos(DhApiVec3f modelPos);
/** Binds the given Vertex Buffer Object to this shader program for rendering. */
void bindVertexBuffer(int vbo);
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java
index 5748d06d9..564c27838 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java
@@ -19,10 +19,6 @@
package com.seibel.distanthorizons.api.interfaces.override.rendering;
-import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel;
-import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
-
/**
* The culling frustum used during Distant Horizons' shadow pass
* if another mod has enabled Distant Horizons' shadow
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java
index e34fe2ecb..09e71d706 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java
@@ -22,6 +22,8 @@ package com.seibel.distanthorizons.api.interfaces.override.worldGenerator;
import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable;
+import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
+import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import java.io.Closeable;
@@ -77,13 +79,41 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
}, worldGeneratorThreadPool);
}
+ @Override
+ public final CompletableFuture generateApiChunks(
+ int chunkPosMinX,
+ int chunkPosMinZ,
+ byte granularity,
+ byte targetDataDetail,
+ EDhApiDistantGeneratorMode generatorMode,
+ ExecutorService worldGeneratorThreadPool,
+ Consumer resultConsumer
+ )
+ {
+ return CompletableFuture.runAsync(() ->
+ {
+ // TODO what does this mean?
+ int genChunkWidth = BitShiftUtil.powerOfTwo(granularity - 4);
+
+ for (int chunkX = chunkPosMinX; chunkX < chunkPosMinX + genChunkWidth; chunkX++)
+ {
+ for (int chunkZ = chunkPosMinZ; chunkZ < chunkPosMinZ + genChunkWidth; chunkZ++)
+ {
+ DhApiChunk apiChunk = this.generateApiChunk(chunkX, chunkZ, generatorMode);
+ resultConsumer.accept(apiChunk);
+ }
+ }
+ }, worldGeneratorThreadPool);
+ }
+
+
/**
* This method is called to generate terrain over a given area
* from a thread defined by Distant Horizons.
*
* @param chunkPosX the chunk X position in the level (not to be confused with the chunk's BlockPos in the level)
* @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level)
- * @param generatorMode how far into the world gen pipeline this method run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
+ * @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
*
* @return See {@link IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator.generateChunks}
* for the list of Object's this method should return along with additional documentation.
@@ -92,4 +122,21 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh
*/
public abstract Object[] generateChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode);
+ /**
+ * This method is called to generate terrain over a given area
+ * from a thread defined by Distant Horizons.
+ *
+ * @param chunkPosX the chunk X position in the level (not to be confused with the chunk's BlockPos in the level)
+ * @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level)
+ * @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation.
+ *
+ * @return A {@link DhApiChunk} with the generated {@link DhApiTerrainDataPoint} including air blocks.
+ * Note: if air blocks aren't included with the proper lighting, lower detail levels will appear as black/unlit.
+ *
+ * @see IDhApiWorldGenerator#generateApiChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer)
+ *
+ * @since API 3.0.0
+ */
+ public abstract DhApiChunk generateApiChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode);
+
}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java
index eb1831973..7861040cc 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java
@@ -154,6 +154,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable
*
* After the {@link DhApiChunk} has been generated, it should be passed into the
* resultConsumer's {@link Consumer#accept(Object)} method.
+ * Note: if air blocks aren't included in the with the {@link DhApiChunk} with proper lighting, lower detail levels will appear as black/unlit.
*
* @implNote the default implementation of this method throws an {@link UnsupportedOperationException},
* and must be overridden when {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}.
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java
new file mode 100644
index 000000000..334c3edd1
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java
@@ -0,0 +1,67 @@
+package com.seibel.distanthorizons.api.interfaces.render;
+
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
+import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
+
+import java.util.List;
+
+/**
+ * Handles creating
+ * {@link IDhApiRenderableBoxGroup} objects,
+ * which can be added via a {@link IDhApiCustomRenderRegister}.
+ *
+ * @see IDhApiCustomRenderRegister
+ * @see IDhApiRenderableBoxGroup
+ *
+ * @author James Seibel
+ * @version 2024-7-3
+ * @since API 3.0.0
+ */
+public interface IDhApiCustomRenderObjectFactory
+{
+ /**
+ * Creates a {@link IDhApiRenderableBoxGroup} from for the given {@link DhApiRenderableBox}
+ * where the box is positioned relative to the level's origin.
+ *
+ * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds"
+ *
+ * @see DhApiRenderableBox
+ * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace()
+ * @see IDhApiRenderableBoxGroup#getResourceLocationPath()
+ *
+ * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons.
+ */
+ IDhApiRenderableBoxGroup createForSingleBox(String resourceLocation, DhApiRenderableBox cube) throws IllegalArgumentException;
+
+ /**
+ * Creates a {@link IDhApiRenderableBoxGroup} from the given list of {@link DhApiRenderableBox} where each
+ * one is positioned relative to given originBlockPos, which in turn is relative to the level's origin.
+ *
+ * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds"
+ * @param originBlockPos The starting position for this {@link IDhApiRenderableBoxGroup}, can be changed during runtime.
+ *
+ *
+ * @see DhApiRenderableBox
+ * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace()
+ * @see IDhApiRenderableBoxGroup#getResourceLocationPath()
+ *
+ * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons.
+ */
+ IDhApiRenderableBoxGroup createRelativePositionedGroup(String resourceLocation, DhApiVec3d originBlockPos, List cubeList);
+
+ /**
+ * Creates a {@link IDhApiRenderableBoxGroup} from the given list of {@link DhApiRenderableBox} where each
+ * one is positioned relative to the level's origin.
+ *
+ * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds"
+ *
+ * @see DhApiRenderableBox
+ * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace()
+ * @see IDhApiRenderableBoxGroup#getResourceLocationPath()
+ *
+ * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons.
+ */
+ IDhApiRenderableBoxGroup createAbsolutePositionedGroup(String resourceLocation, List cubeList);
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java
new file mode 100644
index 000000000..c156c0c8c
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java
@@ -0,0 +1,30 @@
+package com.seibel.distanthorizons.api.interfaces.render;
+
+import com.seibel.distanthorizons.api.DhApi;
+import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
+import com.seibel.distanthorizons.api.interfaces.world.IDhApiWorldProxy;
+
+/**
+ * Handles adding and removing
+ * {@link IDhApiRenderableBoxGroup} objects,
+ * from DH's renderer.
+ *
+ * Can be accessed in
+ * {@link DhApi.Delayed#worldProxy} -> {@link IDhApiLevelWrapper}.
+ *
+ * @see IDhApiCustomRenderObjectFactory
+ * @see IDhApiRenderableBoxGroup
+ * @see IDhApiWorldProxy
+ * @see IDhApiLevelWrapper
+ *
+ * @author James Seibel
+ * @version 2024-7-3
+ * @since API 3.0.0
+ */
+public interface IDhApiCustomRenderRegister
+{
+ void add(IDhApiRenderableBoxGroup cubeGroup) throws IllegalArgumentException;
+
+ IDhApiRenderableBoxGroup remove(long id);
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java
new file mode 100644
index 000000000..399ba1571
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java
@@ -0,0 +1,98 @@
+package com.seibel.distanthorizons.api.interfaces.render;
+
+import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
+import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
+import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
+
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * A list of {@link DhApiRenderableBox}'s that
+ * can be rendered to DH's terrain pass.
+ *
+ * @see DhApiRenderableBox
+ *
+ * @author James Seibel
+ * @version 2024-6-30
+ * @since API 3.0.0
+ */
+public interface IDhApiRenderableBoxGroup extends List
+{
+ /**
+ * A unique numerical ID used by DH during rendering.
+ * This can also be used to bind/unbind specific {@link IDhApiRenderableBoxGroup}'s from the renderer.
+ * @return the ID for this specific group
+ */
+ long getId();
+
+ /**
+ * Used to determine which mods have added what to the DH renderer.
+ * This can be used both by the F3 pie chart so you as a mod developer can profile your code
+ * or by shader developers who want to render your objects differently.
+ *
+ * Should be used the same as a vanilla Minecraft ResourceLocation.
+ * For example if your mod named "Heavy Thunder" adds additional clouds named "Storm Front",
+ * your Resource Location would be something like "HeavyThunder:StormFront"
+ * and this method would return "HeavyThunder".
+ */
+ String getResourceLocationNamespace();
+ /**
+ * Used to determine what type of object mods have added what to the DH renderer.
+ * This can be used both by the F3 pie chart so you as a mod developer can profile your code
+ * or by shader developers who want to render your objects differently.
+ *
+ * Should be used the same as a vanilla Minecraft ResourceLocation.
+ * For example if your mod named "Heavy Thunder" adds additional clouds named "Storm Front",
+ * your Resource Location would be something like "HeavyThunder:StormFront"
+ * and this method would return "StormFront".
+ */
+ String getResourceLocationPath();
+
+ /** Sets whether this group should render or not. */
+ void setActive(boolean active);
+ /** @return if active this group will render. */
+ boolean isActive();
+
+ /** Sets whether this group should render with Screen Space Ambient Occlusioning. */
+ void setSsaoEnabled(boolean ssaoEnabled);
+ /** @return if active this group will render with Screen Space Ambient Occlusioning. */
+ boolean isSsaoEnabled();
+
+ /** Sets where this group will render in the level. */
+ void setOriginBlockPos(DhApiVec3d pos);
+ /** @return the block position in the level that all {@see DhApiRenderableBox} will render relative to. */
+ DhApiVec3d getOriginBlockPos();
+
+ /**
+ * Called right before this group is rendered.
+ * This is a good place to change the origin or notify of any box changes.
+ */
+ void setPreRenderFunc(Consumer renderEventParam);
+ void setPostRenderFunc(Consumer renderEventParam); // TODO name?
+
+ /**
+ * If a cube's color, position, or other property is changed this method
+ * must be called for those changes to render.
+ *
+ * Note: changing the group's position via {@link #setOriginBlockPos} doesn't
+ * require calling this method.
+ */
+ void triggerBoxChange();
+
+ /** Only accepts values between 0 and 15 */
+ void setSkyLight(int skyLight);
+ int getSkyLight();
+
+ /** Only accepts values between 0 and 15 */
+ void setBlockLight(int blockLight);
+ int getBlockLight();
+
+ void setShading(DhApiRenderableBoxGroupShading shading);
+ DhApiRenderableBoxGroupShading getShading();
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java
new file mode 100644
index 000000000..4a86357ef
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java
@@ -0,0 +1,18 @@
+package com.seibel.distanthorizons.api.interfaces.util;
+
+/**
+ * Used for objects that need deep clones.
+ * Replacement for {@link Cloneable}.
+ *
+ * @see Cloneable
+ *
+ * @author James Seibel
+ * @version 2024-7-12
+ * @since API 3.0.0
+ */
+public interface IDhApiCopyable
+{
+ /** Returns a deep clone of all parameters whenever possible. */
+ IDhApiCopyable copy();
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java
index b846f1880..1c159e746 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.api.interfaces.world;
import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
/**
* Can be either a Server or Client level.
@@ -43,7 +44,7 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
boolean hasSkyLight();
/** Returns the max block height of the level(?) */
- int getHeight();
+ int getMaxHeight();
/**
* Returns the lowest possible block position for the level.
@@ -51,4 +52,10 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper
*/
default int getMinHeight() { return 0; }
+ /**
+ * Will return null if called on the server,
+ * or if called before the renderer has been set up.
+ */
+ IDhApiCustomRenderRegister getRenderRegister();
+
}
\ No newline at end of file
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java
index 05da17b96..7bc6dab51 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java
@@ -26,17 +26,20 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
/**
* Fired after Distant Horizons finishes rendering a frame.
* At this point DH will have also finished cleaning up any modifications it
- * did to the OpenGL state, so the state should be back to Minecraft's defaults.
+ * did to the OpenGL state, so the state should be back to Minecraft's defaults.
*
+ * Note: as of API v 3.0.0 no {@link DhApiRenderParam} is included in this event
+ * because the specific parameters may change
+ * depending on whether deferred rendering is enabled or not.
+ *
* @author James Seibel
- * @version 2024-1-31
- * @see DhApiRenderParam
+ * @version 2024-7-14
* @since API 1.0.0
*/
-public abstract class DhApiAfterRenderEvent implements IDhApiEvent
+public abstract class DhApiAfterRenderEvent implements IDhApiEvent
{
/** Fired after Distant Horizons finishes rendering fake chunks. */
- public abstract void afterRender(DhApiEventParam event);
+ public abstract void afterRender(DhApiEventParam event);
//=========================//
@@ -44,6 +47,6 @@ public abstract class DhApiAfterRenderEvent implements IDhApiEvent event) { this.afterRender(event); }
+ public final void fireEvent(DhApiEventParam event) { this.afterRender(event); }
}
\ No newline at end of file
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java
index 7716fda0c..01b8fcaa1 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java
@@ -20,9 +20,10 @@
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
/**
* Called before Distant Horizons starts rendering a buffer.
@@ -52,20 +53,29 @@ public abstract class DhApiBeforeBufferRenderEvent implements IDhApiEvent.
+ */
+
+package com.seibel.distanthorizons.api.methods.events.abstractEvents;
+
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
+import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent;
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
+
+/**
+ * Called before Distant Horizons starts rendering a generic object.
+ * Canceling this event will prevent the triggering {@link IDhApiRenderableBoxGroup} from rendering this frame.
+ *
+ * @author James Seibel
+ * @version 2024-7-11
+ * @since API 3.0.0
+ */
+public abstract class DhApiBeforeGenericObjectRenderEvent implements IDhApiCancelableEvent
+{
+ /** Fired before Distant Horizons renders a generic object. */
+ public abstract void beforeRender(DhApiCancelableEventParam event);
+
+
+ //=========================//
+ // internal DH API methods //
+ //=========================//
+
+ @Override
+ public final void fireEvent(DhApiCancelableEventParam input) { this.beforeRender(input); }
+
+
+ //==================//
+ // parameter object //
+ //==================//
+
+ public static class EventParam extends DhApiRenderParam implements IDhApiEventParam
+ {
+ public final long boxGroupId;
+ public final String resourceLocationNamespace;
+ public final String resourceLocationPath;
+
+
+ public EventParam(
+ DhApiRenderParam renderParam,
+ IDhApiRenderableBoxGroup boxGroup
+ )
+ {
+ super(renderParam);
+
+ this.boxGroupId = boxGroup.getId();
+ this.resourceLocationNamespace = boxGroup.getResourceLocationNamespace();
+ this.resourceLocationPath = boxGroup.getResourceLocationPath();
+ }
+ public EventParam(
+ DhApiRenderParam renderParam,
+ long boxGroupId, String resourceLocationNamespace, String resourceLocationPath
+ )
+ {
+ super(renderParam);
+
+ this.boxGroupId = boxGroupId;
+ this.resourceLocationNamespace = resourceLocationNamespace;
+ this.resourceLocationPath = resourceLocationPath;
+ }
+
+
+
+ @Override
+ public EventParam copy()
+ {
+ return new EventParam(
+ this,
+ this.boxGroupId, this.resourceLocationNamespace, this.resourceLocationPath
+ );
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java
new file mode 100644
index 000000000..e9c6bfbd4
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java
@@ -0,0 +1,47 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.api.methods.events.abstractEvents;
+
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
+
+/**
+ * Called before Distant Horizons starts the cleanup process done after rendering generic objects.
+ * This is called after all generic objects have finished rendering.
+ *
+ * @author James Seibel
+ * @version 2024-7-13
+ * @since API 3.0.0
+ */
+public abstract class DhApiBeforeGenericRenderCleanupEvent implements IDhApiEvent
+{
+ /** Fired before Distant Horizons starts the cleanup process once rendering has finished. */
+ public abstract void beforeCleanup(DhApiEventParam event);
+
+
+ //=========================//
+ // internal DH API methods //
+ //=========================//
+
+ @Override
+ public final void fireEvent(DhApiEventParam event) { this.beforeCleanup(event); }
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java
new file mode 100644
index 000000000..2dea2942c
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java
@@ -0,0 +1,50 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.api.methods.events.abstractEvents;
+
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
+import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
+
+/**
+ * Called before Distant Horizons has started setting up OpenGL objects for rendering generic objects.
+ * If you want to modify already bound DH OpenGL objects try using {@link DhApiBeforeGenericObjectRenderEvent}.
+ *
+ * @author James Seibel
+ * @version 2024-7-12
+ * @since API 3.0.0
+ *
+ * @see DhApiBeforeGenericObjectRenderEvent
+ */
+public abstract class DhApiBeforeGenericRenderSetupEvent implements IDhApiEvent
+{
+ /** Fired before Distant Horizons has started setting up OpenGL objects for rendering generic objects. */
+ public abstract void beforeSetup(DhApiEventParam input);
+
+
+ //=========================//
+ // internal DH API methods //
+ //=========================//
+
+ @Override
+ public final void fireEvent(DhApiEventParam input) { this.beforeSetup(input); }
+
+
+}
\ No newline at end of file
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java
index b8eb08568..85533fa35 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java
@@ -33,7 +33,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp
*/
public abstract class DhApiBeforeRenderCleanupEvent implements IDhApiEvent
{
- /** Fired before Distant Horizons renders LODs. */
+ /** Fired before Distant Horizons starts the cleanup process once rendering has finished. */
public abstract void beforeCleanup(DhApiEventParam event);
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java
index 6afe312cf..abc7bd0b7 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents;
import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataRepo;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
/**
@@ -53,7 +54,7 @@ public abstract class DhApiChunkModifiedEvent implements IDhApiEvent> dataPoints;
@@ -52,12 +52,12 @@ public class DhApiChunk
// constructors //
//==============//
- public DhApiChunk(int chunkPosX, int chunkPosZ, int topYBlockPos, int bottomYBlockPos)
+ public DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos)
{
this.chunkPosX = chunkPosX;
this.chunkPosZ = chunkPosZ;
- this.topYBlockPos = topYBlockPos;
this.bottomYBlockPos = bottomYBlockPos;
+ this.topYBlockPos = topYBlockPos;
// populate the array to prevent null pointers
this.dataPoints = new ArrayList<>(16 * 16); // 256
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java
index c54866bf0..ad3f182d2 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.api.objects.data;
import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3i;
/**
* Holds a single datapoint of terrain data
@@ -47,7 +46,7 @@ public class DhApiRaycastResult
- public DhApiRaycastResult(DhApiTerrainDataPoint dataPoint, Vec3i blockPos)
+ public DhApiRaycastResult(DhApiTerrainDataPoint dataPoint, DhApiVec3i blockPos)
{
this.dataPoint = dataPoint;
this.pos = blockPos;
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java
index 0c70dfbfb..ab2cb9f01 100644
--- a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java
@@ -26,7 +26,7 @@ import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
* Holds a single datapoint of terrain data.
*
* @author James Seibel
- * @version 2022-11-13
+ * @version 2024-7-20
* @since API 1.0.0
*/
public class DhApiTerrainDataPoint
@@ -42,22 +42,22 @@ public class DhApiTerrainDataPoint
public final int blockLightLevel;
public final int skyLightLevel;
- public final int topYBlockPos;
public final int bottomYBlockPos;
+ public final int topYBlockPos;
public final IDhApiBlockStateWrapper blockStateWrapper;
public final IDhApiBiomeWrapper biomeWrapper;
- public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int topYBlockPos, int bottomYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
+ public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int bottomYBlockPos, int topYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper)
{
this.detailLevel = detailLevel;
this.blockLightLevel = blockLightLevel;
this.skyLightLevel = skyLightLevel;
- this.topYBlockPos = topYBlockPos;
this.bottomYBlockPos = bottomYBlockPos;
+ this.topYBlockPos = topYBlockPos;
this.blockStateWrapper = blockStateWrapper;
this.biomeWrapper = biomeWrapper;
diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java
similarity index 57%
rename from api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java
rename to api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java
index 9d24e9f41..2c05ae708 100644
--- a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java
+++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java
@@ -17,46 +17,55 @@
* along with this program. If not, see .
*/
-package com.seibel.distanthorizons.coreapi.util.math;
+package com.seibel.distanthorizons.api.objects.math;
-import org.joml.Matrix4f;
-import org.joml.Matrix4fc;
-
-import java.nio.FloatBuffer;
+import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable;
/**
* An (almost) exact copy of Minecraft's 1.16.5
- * implementation of a 4x4 float matrix.
+ * implementation of a 4x4 float matrix.
*
+ *
+ * m00, m10, m20, m30,
+ * m01, m11, m21, m31,
+ * m02, m12, m22, m32,
+ * m03, m13, m23, m33
+ *
+ *
* @author James Seibel
- * @version 11-11-2021
+ * @version 2024-6-30
*/
-public class Mat4f
+public class DhApiMat4f implements IDhApiCopyable
{
- private float m00;
- private float m01;
- private float m02;
- private float m03;
- private float m10;
- private float m11;
- private float m12;
- private float m13;
- private float m20;
- private float m21;
- private float m22;
- private float m23;
- private float m30;
- private float m31;
- private float m32;
- private float m33;
+ public float m00;
+ public float m01;
+ public float m02;
+ public float m03;
+
+ public float m10;
+ public float m11;
+ public float m12;
+ public float m13;
+
+ public float m20;
+ public float m21;
+ public float m22;
+ public float m23;
+
+ public float m30;
+ public float m31;
+ public float m32;
+ public float m33;
- public Mat4f()
- {
-
- }
- public Mat4f(Mat4f sourceMatrix)
+ //==============//
+ // constructors //
+ //==============//
+
+ public DhApiMat4f() { /* all values are 0 */ }
+
+ public DhApiMat4f(DhApiMat4f sourceMatrix)
{
this.m00 = sourceMatrix.m00;
this.m01 = sourceMatrix.m01;
@@ -76,163 +85,36 @@ public class Mat4f
this.m33 = sourceMatrix.m33;
}
- public Mat4f(Matrix4fc sourceMatrix) { this(convertJomlMatrixToArray(sourceMatrix)); }
- private static float[] convertJomlMatrixToArray(Matrix4fc sourceMatrix)
+ /** Expects the values of the input array to be in row major order (AKA rows then columns) */
+ public DhApiMat4f(float[] values)
{
- FloatBuffer buffer = FloatBuffer.allocate(16);
+ m00 = values[0];
+ m01 = values[1];
+ m02 = values[2];
+ m03 = values[3];
- buffer.put(bufferIndex(0, 0), sourceMatrix.m00());
- buffer.put(bufferIndex(0, 1), sourceMatrix.m01());
- buffer.put(bufferIndex(0, 2), sourceMatrix.m02());
- buffer.put(bufferIndex(0, 3), sourceMatrix.m03());
- buffer.put(bufferIndex(1, 0), sourceMatrix.m10());
- buffer.put(bufferIndex(1, 1), sourceMatrix.m11());
- buffer.put(bufferIndex(1, 2), sourceMatrix.m12());
- buffer.put(bufferIndex(1, 3), sourceMatrix.m13());
- buffer.put(bufferIndex(2, 0), sourceMatrix.m20());
- buffer.put(bufferIndex(2, 1), sourceMatrix.m21());
- buffer.put(bufferIndex(2, 2), sourceMatrix.m22());
- buffer.put(bufferIndex(2, 3), sourceMatrix.m23());
- buffer.put(bufferIndex(3, 0), sourceMatrix.m30());
- buffer.put(bufferIndex(3, 1), sourceMatrix.m31());
- buffer.put(bufferIndex(3, 2), sourceMatrix.m32());
- buffer.put(bufferIndex(3, 3), sourceMatrix.m33());
+ m10 = values[4];
+ m11 = values[5];
+ m12 = values[6];
+ m13 = values[7];
- return buffer.array();
- }
-
- /* Quaternions are not currently needed/implemented
- public Matrix4float(Quaternion p_i48104_1_)
- {
- float f = p_i48104_1_.i();
- float f1 = p_i48104_1_.j();
- float f2 = p_i48104_1_.k();
- float f3 = p_i48104_1_.r();
- float f4 = 2.0F * f * f;
- float f5 = 2.0F * f1 * f1;
- float f6 = 2.0F * f2 * f2;
- this.m00 = 1.0F - f5 - f6;
- this.m11 = 1.0F - f6 - f4;
- this.m22 = 1.0F - f4 - f5;
- this.m33 = 1.0F;
- float f7 = f * f1;
- float f8 = f1 * f2;
- float f9 = f2 * f;
- float f10 = f * f3;
- float f11 = f1 * f3;
- float f12 = f2 * f3;
- this.m10 = 2.0F * (f7 + f12);
- this.m01 = 2.0F * (f7 - f12);
- this.m20 = 2.0F * (f9 - f11);
- this.m02 = 2.0F * (f9 + f11);
- this.m21 = 2.0F * (f8 + f10);
- this.m12 = 2.0F * (f8 - f10);
- }
- */
-
-
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- {
- return true;
- }
- else if (obj != null && this.getClass() == obj.getClass())
- {
- Mat4f otherMatrix = (Mat4f) obj;
- return Float.compare(otherMatrix.m00, this.m00) == 0
- && Float.compare(otherMatrix.m01, this.m01) == 0
- && Float.compare(otherMatrix.m02, this.m02) == 0
- && Float.compare(otherMatrix.m03, this.m03) == 0
- && Float.compare(otherMatrix.m10, this.m10) == 0
- && Float.compare(otherMatrix.m11, this.m11) == 0
- && Float.compare(otherMatrix.m12, this.m12) == 0
- && Float.compare(otherMatrix.m13, this.m13) == 0
- && Float.compare(otherMatrix.m20, this.m20) == 0
- && Float.compare(otherMatrix.m21, this.m21) == 0
- && Float.compare(otherMatrix.m22, this.m22) == 0
- && Float.compare(otherMatrix.m23, this.m23) == 0
- && Float.compare(otherMatrix.m30, this.m30) == 0
- && Float.compare(otherMatrix.m31, this.m31) == 0
- && Float.compare(otherMatrix.m32, this.m32) == 0
- && Float.compare(otherMatrix.m33, this.m33) == 0;
- }
- else
- {
- return false;
- }
- }
-
- @Override
- public int hashCode()
- {
- int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0;
- i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0);
- i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0);
- i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0);
- i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0);
- i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0);
- i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0);
- i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0);
- i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0);
- i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0);
- i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0);
- i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0);
- i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0);
- i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0);
- i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0);
- return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0);
+ m20 = values[8];
+ m21 = values[9];
+ m22 = values[10];
+ m23 = values[11];
+
+ m30 = values[12];
+ m31 = values[13];
+ m32 = values[14];
+ m33 = values[15];
}
- @Override
- public String toString()
- {
- return "Matrix4f:\n" +
- this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" +
- this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" +
- this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" +
- this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n";
- }
- public void store(FloatBuffer floatBuffer)
- {
- floatBuffer.put(bufferIndex(0, 0), this.m00);
- floatBuffer.put(bufferIndex(0, 1), this.m01);
- floatBuffer.put(bufferIndex(0, 2), this.m02);
- floatBuffer.put(bufferIndex(0, 3), this.m03);
- floatBuffer.put(bufferIndex(1, 0), this.m10);
- floatBuffer.put(bufferIndex(1, 1), this.m11);
- floatBuffer.put(bufferIndex(1, 2), this.m12);
- floatBuffer.put(bufferIndex(1, 3), this.m13);
- floatBuffer.put(bufferIndex(2, 0), this.m20);
- floatBuffer.put(bufferIndex(2, 1), this.m21);
- floatBuffer.put(bufferIndex(2, 2), this.m22);
- floatBuffer.put(bufferIndex(2, 3), this.m23);
- floatBuffer.put(bufferIndex(3, 0), this.m30);
- floatBuffer.put(bufferIndex(3, 1), this.m31);
- floatBuffer.put(bufferIndex(3, 2), this.m32);
- floatBuffer.put(bufferIndex(3, 3), this.m33);
- }
-
- public Matrix4f createJomlMatrix()
- {
- return new Matrix4f(
- this.m00, this.m10, this.m20, this.m30,
- this.m01, this.m11, this.m21, this.m31,
- this.m02, this.m12, this.m22, this.m32,
- this.m03, this.m13, this.m23, this.m33
- );
- }
-
-
- private static int bufferIndex(int xIndex, int zIndex)
- {
- return (zIndex * 4) + xIndex;
- }
-
+ //=========//
+ // methods //
+ //=========//
public void setIdentity()
{
@@ -341,7 +223,7 @@ public class Mat4f
}
}
- public void multiply(Mat4f multMatrix)
+ public void multiply(DhApiMat4f multMatrix)
{
float f = this.m00 * multMatrix.m00 + this.m01 * multMatrix.m10 + this.m02 * multMatrix.m20 + this.m03 * multMatrix.m30;
float f1 = this.m00 * multMatrix.m01 + this.m01 * multMatrix.m11 + this.m02 * multMatrix.m21 + this.m03 * multMatrix.m31;
@@ -377,13 +259,6 @@ public class Mat4f
this.m33 = f15;
}
- /* Quaternions aren't currently needed/implemented
- public void multiply(Quaternion p_226596_1_)
- {
- this.multiply(new Matrix4f(p_226596_1_));
- }
- */
-
public void multiply(float scalar)
{
this.m00 *= scalar;
@@ -404,81 +279,6 @@ public class Mat4f
this.m33 *= scalar;
}
- public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane)
- {
- float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D));
- Mat4f matrix = new Mat4f();
- matrix.m00 = f / widthHeightRatio;
- matrix.m11 = f;
- matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane);
- matrix.m32 = -1.0F;
- matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane);
- return matrix;
- }
-
-
- /* not currently needed/implemented
- * Also the parameter names should be double checked as they may be incorrect
- public static Matrix4Float orthographic(float left, float right, float top, float bottom)
- {
- Matrix4Float matrix4f = new Matrix4Float();
- matrix4f.m00 = 2.0F / left;
- matrix4f.m11 = 2.0F / right;
- float f = bottom - top;
- matrix4f.m22 = -2.0F / f;
- matrix4f.m33 = 1.0F;
- matrix4f.m03 = -1.0F;
- matrix4f.m13 = -1.0F;
- matrix4f.m23 = -(bottom + top) / f;
- return matrix4f;
- }
- */
-
- /**
- * TODO: what kind of translation is this?
- * and how is this different from "multiplyTranslationMatrix"?
- * Answer: This is faster and direct (but only if this is pure translation matrix without rotate)
- */
- public void translate(Vec3f vec)
- {
- this.m03 += vec.x;
- this.m13 += vec.y;
- this.m23 += vec.z;
- }
-
- /** originally "translate" from Minecraft's MatrixStack */
- public void multiplyTranslationMatrix(double x, double y, double z)
- {
- multiply(createTranslateMatrix((float) x, (float) y, (float) z));
- }
-
- public Mat4f copy()
- {
- return new Mat4f(this);
- }
-
- public static Mat4f createScaleMatrix(float x, float y, float z)
- {
- Mat4f matrix = new Mat4f();
- matrix.m00 = x;
- matrix.m11 = y;
- matrix.m22 = z;
- matrix.m33 = 1.0F;
- return matrix;
- }
-
- public static Mat4f createTranslateMatrix(float x, float y, float z)
- {
- Mat4f matrix = new Mat4f();
- matrix.m00 = 1.0F;
- matrix.m11 = 1.0F;
- matrix.m22 = 1.0F;
- matrix.m33 = 1.0F;
- matrix.m03 = x;
- matrix.m13 = y;
- matrix.m23 = z;
- return matrix;
- }
@@ -487,6 +287,8 @@ public class Mat4f
// methods //
//==================//
+ private static int getArrayIndex(int xIndex, int zIndex) { return (zIndex * 4) + xIndex; }
+
/** Returns the values of this matrix in row major order (AKA rows then columns) */
public float[] getValuesAsArray()
{
@@ -513,113 +315,77 @@ public class Mat4f
};
}
- public Vec3f asNonNormalizedLookForwardVector()
+
+
+ //================//
+ // base overrides //
+ //================//
+
+ @Override
+ public boolean equals(Object obj)
{
- return new Vec3f(this.m02, this.m12, this.m22);
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj != null && this.getClass() == obj.getClass())
+ {
+ DhApiMat4f otherMatrix = (DhApiMat4f) obj;
+ return Float.compare(otherMatrix.m00, this.m00) == 0
+ && Float.compare(otherMatrix.m01, this.m01) == 0
+ && Float.compare(otherMatrix.m02, this.m02) == 0
+ && Float.compare(otherMatrix.m03, this.m03) == 0
+ && Float.compare(otherMatrix.m10, this.m10) == 0
+ && Float.compare(otherMatrix.m11, this.m11) == 0
+ && Float.compare(otherMatrix.m12, this.m12) == 0
+ && Float.compare(otherMatrix.m13, this.m13) == 0
+ && Float.compare(otherMatrix.m20, this.m20) == 0
+ && Float.compare(otherMatrix.m21, this.m21) == 0
+ && Float.compare(otherMatrix.m22, this.m22) == 0
+ && Float.compare(otherMatrix.m23, this.m23) == 0
+ && Float.compare(otherMatrix.m30, this.m30) == 0
+ && Float.compare(otherMatrix.m31, this.m31) == 0
+ && Float.compare(otherMatrix.m32, this.m32) == 0
+ && Float.compare(otherMatrix.m33, this.m33) == 0;
+ }
+ else
+ {
+ return false;
+ }
}
- //===============//
- // Forge methods //
- //===============//
-
- public Mat4f(float[] values)
+ @Override
+ public int hashCode()
{
- m00 = values[0];
- m01 = values[1];
- m02 = values[2];
- m03 = values[3];
- m10 = values[4];
- m11 = values[5];
- m12 = values[6];
- m13 = values[7];
- m20 = values[8];
- m21 = values[9];
- m22 = values[10];
- m23 = values[11];
- m30 = values[12];
- m31 = values[13];
- m32 = values[14];
- m33 = values[15];
+ int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0;
+ i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0);
+ i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0);
+ i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0);
+ i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0);
+ i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0);
+ i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0);
+ i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0);
+ i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0);
+ i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0);
+ i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0);
+ i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0);
+ i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0);
+ i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0);
+ i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0);
+ return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0);
}
- public Mat4f(FloatBuffer buffer)
+ @Override
+ public String toString()
{
- this(buffer.array());
+ return "Matrix4f:\n" +
+ this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" +
+ this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" +
+ this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" +
+ this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n";
}
- public void set(Mat4f mat)
- {
- this.m00 = mat.m00;
- this.m01 = mat.m01;
- this.m02 = mat.m02;
- this.m03 = mat.m03;
- this.m10 = mat.m10;
- this.m11 = mat.m11;
- this.m12 = mat.m12;
- this.m13 = mat.m13;
- this.m20 = mat.m20;
- this.m21 = mat.m21;
- this.m22 = mat.m22;
- this.m23 = mat.m23;
- this.m30 = mat.m30;
- this.m31 = mat.m31;
- this.m32 = mat.m32;
- this.m33 = mat.m33;
- }
-
- public void add(Mat4f other)
- {
- m00 += other.m00;
- m01 += other.m01;
- m02 += other.m02;
- m03 += other.m03;
- m10 += other.m10;
- m11 += other.m11;
- m12 += other.m12;
- m13 += other.m13;
- m20 += other.m20;
- m21 += other.m21;
- m22 += other.m22;
- m23 += other.m23;
- m30 += other.m30;
- m31 += other.m31;
- m32 += other.m32;
- m33 += other.m33;
- }
-
- public void multiplyBackward(Mat4f other)
- {
- Mat4f copy = other.copy();
- copy.multiply(this);
- this.set(copy);
- }
-
- public void setTranslation(float x, float y, float z)
- {
- this.m00 = 1.0F;
- this.m11 = 1.0F;
- this.m22 = 1.0F;
- this.m33 = 1.0F;
- this.m03 = x;
- this.m13 = y;
- this.m23 = z;
- }
-
- /**
- * Changes the values that store the clipping planes.
- * Formula for calculating matrix values is the same that OpenGL uses when making matrices.
- *
- * @param nearClip New near clipping plane value.
- * @param farClip New far clipping plane value.
- */
- public void setClipPlanes(float nearClip, float farClip)
- {
- //convert to matrix values, formula copied from a textbook / openGL specification.
- float matNearClip = -((farClip + nearClip) / (farClip - nearClip));
- float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip));
- //set new values for the clip planes.
- this.m22 = matNearClip;
- this.m23 = matFarClip;
- }
+ @Override
+ public DhApiMat4f copy() { return new DhApiMat4f(this); }
}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java
new file mode 100644
index 000000000..9e1344262
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java
@@ -0,0 +1,94 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.api.objects.math;
+
+/**
+ * Often used to store block positions or any other
+ * position in 3D space.
+ *
+ * @author James Seibel
+ * @version 2024-7-9
+ * @since API 3.0.0
+ */
+public class DhApiVec3d
+{
+ public double x;
+ public double y;
+ public double z;
+
+
+
+ /** creates a Vec3 at (0,0,0) */
+ public DhApiVec3d()
+ {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+
+ public DhApiVec3d(double x, double y, double z)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj != null && this.getClass() == obj.getClass())
+ {
+ DhApiVec3d Vec3d = (DhApiVec3d) obj;
+ if (Double.compare(Vec3d.x, this.x) != 0)
+ {
+ return false;
+ }
+ else if (Double.compare(Vec3d.y, this.y) != 0)
+ {
+ return false;
+ }
+ else
+ {
+ return Double.compare(Vec3d.z, this.z) == 0;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ long i = Double.doubleToLongBits(this.x);
+ i = 31 * i + Double.doubleToLongBits(this.y);
+ i = 31 * i + Double.doubleToLongBits(this.z);
+ return Long.hashCode(i);
+ }
+
+ @Override
+ public String toString() { return "[" + this.x + ", " + this.y + ", " + this.z + "]"; }
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java
new file mode 100644
index 000000000..8120b149a
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java
@@ -0,0 +1,98 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.api.objects.math;
+
+import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable;
+
+/**
+ * Often used to store block positions or any other
+ * position in 3D space.
+ *
+ * @author James Seibel
+ * @version 2024-6-3
+ * @since API 2.2.0
+ */
+public class DhApiVec3f implements IDhApiCopyable
+{
+ public float x;
+ public float y;
+ public float z;
+
+
+
+ /** creates a Vec3 at (0,0,0) */
+ public DhApiVec3f()
+ {
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+ }
+
+ public DhApiVec3f(float x, float y, float z)
+ {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ else if (obj != null && this.getClass() == obj.getClass())
+ {
+ DhApiVec3f Vec3f = (DhApiVec3f) obj;
+ if (Float.compare(Vec3f.x, this.x) != 0)
+ {
+ return false;
+ }
+ else if (Float.compare(Vec3f.y, this.y) != 0)
+ {
+ return false;
+ }
+ else
+ {
+ return Float.compare(Vec3f.z, this.z) == 0;
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int i = Float.floatToIntBits(this.x);
+ i = 31 * i + Float.floatToIntBits(this.y);
+ return 31 * i + Float.floatToIntBits(this.z);
+ }
+
+ @Override
+ public String toString() { return "[" + this.x + ", " + this.y + ", " + this.z + "]"; }
+
+ @Override
+ public DhApiVec3f copy() { return new DhApiVec3f(this.x, this.y, this.z); }
+
+}
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java
new file mode 100644
index 000000000..d3411dc15
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java
@@ -0,0 +1,52 @@
+package com.seibel.distanthorizons.api.objects.render;
+
+
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
+
+import java.awt.*;
+
+/**
+ * @see IDhApiRenderableBoxGroup
+ *
+ * @author James Seibel
+ * @version 2024-6-30
+ * @since API 3.0.0
+ */
+public class DhApiRenderableBox
+{
+ /** the position closest to (-inf,-inf) */
+ public DhApiVec3d minPos;
+ /** the position closest to (+inf,+inf) */
+ public DhApiVec3d maxPos;
+
+ public Color color;
+ public byte material;
+
+
+
+ //==============//
+ // constructors //
+ //==============//
+
+ public DhApiRenderableBox(DhApiVec3d minPos, float width, Color color, EDhApiBlockMaterial material)
+ {
+ this(minPos, new DhApiVec3d(
+ minPos.x + width,
+ minPos.y + width,
+ minPos.z + width
+ ), color, material);
+ }
+
+ public DhApiRenderableBox(DhApiVec3d minPos, DhApiVec3d maxPos, Color color, EDhApiBlockMaterial material)
+ {
+ this.minPos = minPos;
+ this.maxPos = maxPos;
+ this.color = color;
+ this.material = material.index;
+ }
+
+}
+
diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java
new file mode 100644
index 000000000..396f73bc5
--- /dev/null
+++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java
@@ -0,0 +1,93 @@
+package com.seibel.distanthorizons.api.objects.render;
+
+
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
+
+import java.awt.*;
+
+/**
+ * @see IDhApiRenderableBoxGroup
+ *
+ * Shading values are multiplied against the color for each direction,
+ * for example:
+ * A shading value of 1.0 indicates the color is unchanged.
+ * A shading value of 0.0 changes the color to black.
+ *
+ * @author James Seibel
+ * @version 2024-7-7
+ * @since API 3.0.0
+ */
+public class DhApiRenderableBoxGroupShading
+{
+ /** negative X */
+ public float north = 1.0f;
+ /** positive X */
+ public float south = 1.0f;
+
+ /** positive X */
+ public float east = 1.0f;
+ /** negative X */
+ public float west = 1.0f;
+
+ /** positive Y */
+ public float top = 1.0f;
+ /** negative Y */
+ public float bottom = 1.0f;
+
+
+
+ //==============//
+ // constructors //
+ //==============//
+
+ public static DhApiRenderableBoxGroupShading getDefaultShaded()
+ {
+ DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading();
+ shading.setDefaultShaded();
+ return shading;
+ }
+
+ public static DhApiRenderableBoxGroupShading getUnshaded()
+ {
+ DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading();
+ shading.setUnshaded();
+ return shading;
+ }
+
+
+
+ //=========//
+ // methods //
+ //=========//
+
+ /**
+ * Directions will have different brightness similar to Minecraft blocks.
+ * This is a good default for un-lit objects.
+ */
+ public void setDefaultShaded()
+ {
+ this.north = 0.8f;
+ this.south = 0.8f;
+ this.east = 0.6f;
+ this.west = 0.6f;
+ this.top = 1.0f;
+ this.bottom = 0.5f;
+ }
+
+ /**
+ * All directions render with the same brightness.
+ * This is best used for glowing objects like beacons.
+ */
+ public void setUnshaded()
+ {
+ this.north = 1.0f;
+ this.south = 1.0f;
+ this.east = 1.0f;
+ this.west = 1.0f;
+ this.top = 1.0f;
+ this.bottom = 1.0f;
+ }
+
+}
+
diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java
index e6d49f611..386ab2aef 100644
--- a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java
+++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.coreapi.DependencyInjection;
import com.seibel.distanthorizons.api.interfaces.events.IDhApiEventInjector;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
+import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiOneTimeEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
@@ -140,7 +141,26 @@ public class ApiEventInjector extends DependencyInjector implements
// fire each event and record if any of them
// request to cancel the event.
- DhApiEventParam eventParam = createEventParamWrapper(event, eventInput);
+
+ // attempt to clone the event input if possible
+ // this is done to reduce the likely hood that one event listener
+ // will make change the event parameter for other listeners
+ T input = eventInput;
+ if (eventInput instanceof IDhApiEventParam)
+ {
+ try
+ {
+ //noinspection unchecked
+ input = (T) ((IDhApiEventParam) eventInput).copy();
+ }
+ catch (Exception e)
+ {
+ LOGGER.error("Unable to clone event parameter ["+eventInput.getClass().getSimpleName()+"], error: ["+e.getMessage()+"].", e);
+ }
+ }
+
+
+ DhApiEventParam eventParam = createEventParamWrapper(event, input);
event.fireEvent(eventParam);
if (eventParam instanceof DhApiCancelableEventParam)
diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java
index a382dca0d..526c5e10e 100644
--- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java
+++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java
@@ -47,9 +47,9 @@ public final class ModInfo
public static boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
/** This version should only be updated when breaking changes are introduced to the DH API */
- public static final int API_MAJOR_VERSION = 2;
+ public static final int API_MAJOR_VERSION = 3;
/** This version should be updated whenever new methods are added to the DH API */
- public static final int API_MINOR_VERSION = 1;
+ public static final int API_MINOR_VERSION = 0;
/** This version should be updated whenever non-breaking fixes are added to the DH API */
public static final int API_PATH_VERSION = 0;
diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java
index a31745a8e..3297a1a26 100644
--- a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java
+++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java
@@ -67,10 +67,11 @@ public interface IConfigEntry
* Checks if the option is valid
*
* 0 == valid
+ * 2 == invalid
* 1 == number too high
* -1 == number too low
*/
- byte isValid();
+ byte isValid(); // TODO replace with an enum
/** Checks if a value is valid */
byte isValid(T value);
diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java
deleted file mode 100644
index b45ba98be..000000000
--- a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java
+++ /dev/null
@@ -1,263 +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 .
- */
-
-package com.seibel.distanthorizons.coreapi.util.math;
-
-import com.seibel.distanthorizons.coreapi.util.MathUtil;
-
-/**
- * A (almost) exact copy of Minecraft's 1.16.5
- * implementation of a 3 element float vector.
- *
- * @author James Seibel
- * @version 11-11-2021
- */
-public class Vec3f
-{
- public static Vec3f XNeg = new Vec3f(-1.0F, 0.0F, 0.0F);
- public static Vec3f XPos = new Vec3f(1.0F, 0.0F, 0.0F);
- public static Vec3f YNeg = new Vec3f(0.0F, -1.0F, 0.0F);
- public static Vec3f YPos = new Vec3f(0.0F, 1.0F, 0.0F);
- public static Vec3f ZNeg = new Vec3f(0.0F, 0.0F, -1.0F);
- public static Vec3f ZPos = new Vec3f(0.0F, 0.0F, 1.0F);
-
-
- public float x;
- public float y;
- public float z;
-
-
-
- public Vec3f()
- {
-
- }
-
- public Vec3f(float x, float y, float z)
- {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- @Override
- public boolean equals(Object obj)
- {
- if (this == obj)
- {
- return true;
- }
- else if (obj != null && this.getClass() == obj.getClass())
- {
- Vec3f Vec3f = (Vec3f) obj;
- if (Float.compare(Vec3f.x, this.x) != 0)
- {
- return false;
- }
- else if (Float.compare(Vec3f.y, this.y) != 0)
- {
- return false;
- }
- else
- {
- return Float.compare(Vec3f.z, this.z) == 0;
- }
- }
- else
- {
- return false;
- }
- }
-
- @Override
- public int hashCode()
- {
- int i = Float.floatToIntBits(this.x);
- i = 31 * i + Float.floatToIntBits(this.y);
- return 31 * i + Float.floatToIntBits(this.z);
- }
-
- public void mul(float scalar)
- {
- this.x *= scalar;
- this.y *= scalar;
- this.z *= scalar;
- }
-
- public void mul(float x, float y, float z)
- {
- this.x *= x;
- this.y *= y;
- this.z *= z;
- }
-
- public void clamp(float min, float max)
- {
- this.x = MathUtil.clamp(min, this.x, max);
- this.y = MathUtil.clamp(min, this.y, max);
- this.z = MathUtil.clamp(min, this.z, max);
- }
-
- public void set(float x, float y, float z)
- {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
- public void add(float x, float y, float z)
- {
- this.x += x;
- this.y += y;
- this.z += z;
- }
-
- public void add(Vec3f vector)
- {
- this.x += vector.x;
- this.y += vector.y;
- this.z += vector.z;
- }
-
- public void subtract(Vec3f vector)
- {
- this.x -= vector.x;
- this.y -= vector.y;
- this.z -= vector.z;
- }
-
- public float dotProduct(Vec3f vector)
- {
- return this.x * vector.x + this.y * vector.y + this.z * vector.z;
- }
-
- /** Returns true if normalization had to be done */
- public boolean normalize()
- {
- float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z;
- if (squaredSum < 1.0E-5D)
- {
- return false;
- }
- else
- {
- float f1 = MathUtil.fastInvSqrt(squaredSum);
- this.x *= f1;
- this.y *= f1;
- this.z *= f1;
- return true;
- }
- }
-
- public void crossProduct(Vec3f vector)
- {
- float f = this.x;
- float f1 = this.y;
- float f2 = this.z;
- float f3 = vector.x;
- float f4 = vector.y;
- float f5 = vector.z;
- this.x = f1 * f5 - f2 * f4;
- this.y = f2 * f3 - f * f5;
- this.z = f * f4 - f1 * f3;
- }
-
- /* Matrix3f is not currently needed/implemented
- public void transform(Matrix3f p_229188_1_)
- {
- float f = this.x;
- float f1 = this.y;
- float f2 = this.z;
- this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2;
- this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2;
- this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2;
- }
- */
-
- /* Quaternions are not currently needed/implemented
- public void transform(Quaternion p_214905_1_)
- {
- Quaternion quaternion = new Quaternion(p_214905_1_);
- quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F));
- Quaternion quaternion1 = new Quaternion(p_214905_1_);
- quaternion1.conj();
- quaternion.mul(quaternion1);
- this.set(quaternion.i(), quaternion.j(), quaternion.k());
- }
- */
-
- /* not currently needed
- * percent may actually be partial ticks (which is available when rendering)
- public void linearInterp(Vec3f resultingVector, float percent)
- {
- float f = 1.0F - percent;
- this.x = this.x * f + resultingVector.x * percent;
- this.y = this.y * f + resultingVector.y * percent;
- this.z = this.z * f + resultingVector.z * percent;
- }
- */
-
- /* Quaternions are not currently needed/implemented
- public Quaternion rotation(float p_229193_1_)
- {
- return new Quaternion(this, p_229193_1_, false);
- }
-
-
- @OnlyIn(Dist.CLIENT)
- public Quaternion rotationDegrees(float p_229187_1_)
- {
- return new Quaternion(this, p_229187_1_, true);
- }
- */
-
- public Vec3f copy()
- {
- return new Vec3f(this.x, this.y, this.z);
- }
-
- /* not currently needed/implemented
- public void map(Float2FloatFunction p_229191_1_)
- {
- this.x = p_229191_1_.get(this.x);
- this.y = p_229191_1_.get(this.y);
- this.z = p_229191_1_.get(this.z);
- }
- */
-
- @Override
- public String toString()
- {
- return "[" + this.x + ", " + this.y + ", " + this.z + "]";
- }
-
- // Forge start
- public Vec3f(float[] values)
- {
- set(values);
- }
-
- public void set(float[] values)
- {
- this.x = values[0];
- this.y = values[1];
- this.z = values[2];
- }
-
-}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java
index 3ef5e3325..3bed23eed 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory;
import com.seibel.distanthorizons.core.sql.DatabaseUpdater;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -96,6 +97,7 @@ public class Initializer
DhApi.Delayed.terrainRepo = DhApiTerrainDataRepo.INSTANCE;
DhApi.Delayed.worldProxy = DhApiWorldProxy.INSTANCE;
DhApi.Delayed.renderProxy = DhApiRenderProxy.INSTANCE;
+ DhApi.Delayed.customRenderObjectFactory = GenericRenderObjectFactory.INSTANCE;
DhApi.Delayed.wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
if (DhApi.Delayed.wrapperFactory == null)
{
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java
new file mode 100644
index 000000000..f7fded3e5
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java
@@ -0,0 +1,46 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.core.api.external.methods.config.client;
+
+import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
+import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGenericRenderingConfig;
+import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig;
+import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
+import com.seibel.distanthorizons.core.config.Config;
+
+public class DhApiGenericRenderingConfig implements IDhApiGenericRenderingConfig
+{
+ public static DhApiGenericRenderingConfig INSTANCE = new DhApiGenericRenderingConfig();
+
+ private DhApiGenericRenderingConfig() { }
+
+
+
+ @Override
+ public IDhApiConfigValue renderingEnabled()
+ { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableRendering); }
+ @Override
+ public IDhApiConfigValue beaconRenderingEnabled()
+ { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering); }
+ @Override
+ public IDhApiConfigValue cloudRenderingEnabled()
+ { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering); }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java
index 27c1d48eb..ef4f66c74 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java
@@ -22,10 +22,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client;
import com.seibel.distanthorizons.api.enums.config.*;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency;
import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue;
-import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiAmbientOcclusionConfig;
-import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig;
-import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGraphicsConfig;
-import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig;
+import com.seibel.distanthorizons.api.interfaces.config.client.*;
import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.core.config.Config;
@@ -42,9 +39,14 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig
// inner layers //
//==============//
+ @Override
public IDhApiFogConfig fog() { return DhApiFogConfig.INSTANCE; }
+ @Override
public IDhApiAmbientOcclusionConfig ambientOcclusion() { return DhApiAmbientOcclusionConfig.INSTANCE; }
+ @Override
public IDhApiNoiseTextureConfig noiseTexture() { return DhApiNoiseTextureConfig.INSTANCE; }
+ @Override
+ public IDhApiGenericRenderingConfig genericRendering() { return DhApiGenericRenderingConfig.INSTANCE; }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java
new file mode 100644
index 000000000..37cabfb97
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java
@@ -0,0 +1,87 @@
+package com.seibel.distanthorizons.core.api.external.methods.data;
+
+import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache;
+import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
+import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap;
+import it.unimi.dsi.fastutil.longs.LongSet;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.ref.SoftReference;
+
+public class DhApiTerrainDataCache implements IDhApiTerrainDataCache
+{
+ private final Object modificationLock = new Object();
+ private Long2ReferenceOpenHashMap> posToFullDataRef = new Long2ReferenceOpenHashMap<>();
+
+ private static final Logger LOGGER = LogManager.getLogger(DhApiTerrainDataCache.class.getSimpleName());
+
+
+
+ //==================//
+ // internal methods //
+ //==================//
+
+ public void add(long pos, FullDataSourceV2 dataSource)
+ {
+ synchronized (this.modificationLock)
+ {
+ this.posToFullDataRef.put(pos, new SoftReference<>(dataSource));
+ }
+ }
+
+ @Nullable
+ public FullDataSourceV2 get(long pos)
+ {
+ synchronized (this.modificationLock)
+ {
+ SoftReference ref = this.posToFullDataRef.get(pos);
+ if (ref != null)
+ {
+ return ref.get();
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+
+
+ //=============//
+ // API methods //
+ //=============//
+
+ @Override
+ public void clear()
+ {
+ synchronized (this.modificationLock)
+ {
+ LongSet keySet = this.posToFullDataRef.keySet();
+ for (long pos : keySet)
+ {
+ SoftReference dataRef = this.posToFullDataRef.remove(pos);
+ if (dataRef != null)
+ {
+ FullDataSourceV2 dataSource = dataRef.get();
+ if (dataSource != null)
+ {
+ try
+ {
+ dataSource.close();
+ }
+ catch (Exception e)
+ {
+ LOGGER.warn("Unable to close data source, error: [" + e.getMessage() + "].", e);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java
index d08989ad8..cf7d337c0 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.api.external.methods.data;
+import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.objects.DhApiResult;
import com.seibel.distanthorizons.api.objects.data.DhApiRaycastResult;
@@ -35,6 +36,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RayCastUtil;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
@@ -43,12 +45,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3i;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.math.Vec3i;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
@@ -56,9 +58,6 @@ import java.util.concurrent.ExecutionException;
/**
* Allows interfacing with the terrain data Distant Horizons has stored.
- *
- * @author James Seibel
- * @version 2022-11-19
*/
public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
{
@@ -68,12 +67,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
// debugging values
private static volatile boolean debugThreadRunning = false;
- private static String currentDebugBiomeName = "";
- private static int currentDebugBlockColorInt = -1;
+ private static DhApiTerrainDataCache debugDataCache = new DhApiTerrainDataCache();
private static DhApiVec3i currentDebugVec3i = new Vec3i();
+ //=============//
+ // constructor //
+ //=============//
+
private DhApiTerrainDataRepo()
{
@@ -86,41 +88,32 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
//================//
@Override
- public DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ)
- {
- return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY);
- }
+ public DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache)
+ { return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, dataCache); }
@Override
- public DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ)
- {
- return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null);
- }
+ public DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache)
+ { return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, dataCache); }
@Override
- public DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ)
- {
- return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ));
- }
+ public DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, @Nullable IDhApiTerrainDataCache dataCache)
+ { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ), dataCache); }
@Override
- public DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ)
- {
- return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ));
- }
+ public DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, @Nullable IDhApiTerrainDataCache dataCache)
+ { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ), dataCache); }
@Override
- public DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ)
- {
- return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ));
- }
+ public DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, @Nullable IDhApiTerrainDataCache dataCache)
+ { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ), dataCache); }
+
// private getters //
/** Returns a single API terrain datapoint that contains the given Y block position */
- private static DhApiResult getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos)
+ private static DhApiResult getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos, @Nullable IDhApiTerrainDataCache dataCache)
{
- DhApiResult result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos);
+ DhApiResult result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos, dataCache);
if (result.success && result.payload.length > 0)
{
return DhApiResult.createSuccess(result.message, result.payload[0]);
@@ -140,7 +133,9 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
*
* will stop and return the in progress data if any errors are encountered.
*/
- private static DhApiResult getTerrainDataOverAreaForPositionDetailLevel(IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos)
+ private static DhApiResult getTerrainDataOverAreaForPositionDetailLevel(
+ IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos,
+ @Nullable IDhApiTerrainDataCache dataCache)
{
DhLodPos startingBlockPos = requestedAreaPos.getCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL);
int widthOfAreaInBlocks = BitShiftUtil.powerOfTwo(requestedAreaPos.detailLevel);
@@ -154,7 +149,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
for (int z = 0; z < widthOfAreaInBlocks; z++)
{
DhLodPos blockColumnPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, startingBlockPos.x + x, startingBlockPos.z + z);
- DhApiResult result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null);
+ DhApiResult result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null, dataCache);
if (result.success)
{
returnArray[x][z] = result.payload;
@@ -177,8 +172,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
* If the ApiResult is successful there will be an array of data.
* The returned array will be empty if no data could be retrieved.
*/
- private static DhApiResult getTerrainDataColumnArray(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer nullableBlockYPos)
+ private static DhApiResult getTerrainDataColumnArray(
+ IDhApiLevelWrapper levelWrapper,
+ DhLodPos requestedColumnPos, Integer nullableBlockYPos,
+ @Nullable IDhApiTerrainDataCache apiDataCache)
{
+ //============//
+ // validation //
+ //============//
+
AbstractDhWorld currentWorld = SharedApi.getAbstractDhWorld();
if (currentWorld == null)
{
@@ -194,6 +196,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
+ if (!(apiDataCache instanceof DhApiTerrainDataCache))
+ {
+ // custom level wrappers aren't supported,
+ // the API user must get a level wrapper from our code somewhere
+ return DhApiResult.createFail("Unsupported [" + IDhApiTerrainDataCache.class.getSimpleName() + "] implementation, only the core class [" + DhApiTerrainDataCache.class.getSimpleName() + "] is a valid parameter.");
+ }
+ DhApiTerrainDataCache dataCache = (DhApiTerrainDataCache) apiDataCache;
+
+
IDhLevel level = currentWorld.getLevel(coreLevelWrapper);
if (level == null)
{
@@ -209,70 +220,96 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel();
+
+ //=====================//
+ // get the data source //
+ //=====================//
+
try
{
- // attempt to get/generate the data source for this section
- FullDataSourceV2 dataSource = level.getFullDataProvider().getAsync(sectionPos).get();
+ FullDataSourceV2 dataSource = null;
+
+ // try using the cached data if possible
+ if (dataCache != null)
+ {
+ dataSource = dataCache.get(sectionPos);
+ }
+
if (dataSource == null)
{
- return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "].");
- }
- else
- {
- // attempt to get the LOD data from the data source
- FullDataPointIdMap mapping = dataSource.mapping;
- LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z);
- if (dataColumn != null)
+ // attempt to get/generate the data source for this section
+ dataSource = level.getFullDataProvider().getAsync(sectionPos).get();
+ if (dataSource == null)
{
- int dataColumnIndexCount = dataColumn.size();
- DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount];
- long dataPoint;
+ return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "].");
+ }
+ dataCache.add(sectionPos, dataSource);
+ }
+
+
+
+ //===============================//
+ // get LOD data from data source //
+ //===============================//
+
+ FullDataPointIdMap mapping = dataSource.mapping;
+ LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z);
+ if (dataColumn != null)
+ {
+ int dataColumnIndexCount = dataColumn.size();
+ DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount];
+ long dataPoint;
+
+ boolean getSpecificYCoordinate = nullableBlockYPos != null;
+ int levelMinimumHeight = levelWrapper.getMinHeight();
+
+
+ // search for a datapoint that contains the block y position
+ for (int i = 0; i < dataColumnIndexCount; i++)
+ {
+ dataPoint = dataColumn.getLong(i);
- boolean getSpecificYCoordinate = nullableBlockYPos != null;
- int levelMinimumHeight = levelWrapper.getMinHeight();
-
-
- // search for a datapoint that contains the block y position
- for (int i = 0; i < dataColumnIndexCount; i++)
+ if (!getSpecificYCoordinate)
{
- dataPoint = dataColumn.getLong(i);
-
- if (!getSpecificYCoordinate)
+ // if we aren't look for a specific datapoint, add each datapoint to the return array
+ returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
+ }
+ else
+ {
+ // we are looking for a specific datapoint,
+ // don't look at null ones
+ if (dataPoint != 0)
{
- // if we aren't look for a specific datapoint, add each datapoint to the return array
- returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
- }
- else
- {
- // we are looking for a specific datapoint,
- // don't look at null ones
- if (dataPoint != 0)
+ int requestedY = nullableBlockYPos;
+ int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight;
+ int height = FullDataPointUtil.getHeight(dataPoint);
+ int topY = bottomY + height;
+
+ // does this datapoint contain the requested Y position?
+ if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY
{
- int requestedY = nullableBlockYPos;
- int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight;
- int height = FullDataPointUtil.getHeight(dataPoint);
- int topY = bottomY + height;
-
- // does this datapoint contain the requested Y position?
- if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY
- {
- // this datapoint contains the requested block position, return it
- DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
- return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData});
- }
+ // this datapoint contains the requested block position, return it
+ DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint);
+ return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData});
}
}
}
-
- // return all collected data
- return DhApiResult.createSuccess(returnArray);
}
- // the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated)
- return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]);
+ // return all collected data
+ return DhApiResult.createSuccess(returnArray);
}
+
+ // the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated)
+ return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]);
}
catch (InterruptedException | ExecutionException e)
+ {
+ // shouldn't normally happen, but just in case
+ LOGGER.error("getTerrainDataColumnArray operation canceled. Error: [" + e.getMessage() + "]", e);
+ return DhApiResult.createFail("Operation cancled before it could complete: [" + e.getMessage() + "].");
+ }
+ catch (Exception e)
{
// shouldn't normally happen, but just in case
LOGGER.error("Unexpected exception in getTerrainDataColumnArray. Error: [" + e.getMessage() + "]", e);
@@ -306,9 +343,11 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
IDhApiLevelWrapper levelWrapper,
double rayOriginX, double rayOriginY, double rayOriginZ,
float rayDirectionX, float rayDirectionY, float rayDirectionZ,
- int maxRayBlockLength)
+ int maxRayBlockLength,
+ @Nullable
+ IDhApiTerrainDataCache dataCache)
{
- return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength);
+ return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength, dataCache);
}
/**
@@ -317,12 +356,17 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
* Works by walking through the world and attempting to get the LOD
* data present at each step.
*/
- private DhApiResult raycastLodData(IDhApiLevelWrapper levelWrapper, Vec3d rayOrigin, Vec3f rayDirection, int maxRayBlockLength)
+ private DhApiResult raycastLodData(
+ IDhApiLevelWrapper levelWrapper,
+ Vec3d rayOrigin, Vec3f rayDirection,
+ int maxRayBlockLength,
+ @Nullable
+ IDhApiTerrainDataCache dataCache)
{
rayDirection.normalize();
int minBlockHeight = levelWrapper.getMinHeight();
- int maxBlockHeight = levelWrapper.getHeight();
+ int maxBlockHeight = levelWrapper.getMaxHeight();
@@ -346,7 +390,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
for (Vec3i columnPos : columnPositions)
{
// check each column
- DhApiResult result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z);
+ DhApiResult result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z, dataCache);
if (!result.success)
{
// if there was an error, stop and return it
@@ -414,7 +458,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
*/
private static ArrayList getIntersectingColumnsAtPosition(Vec3i rayEndingPos, Vec3f rayDirection)
{
- ArrayList returnList = new ArrayList(9);
+ ArrayList returnList = new ArrayList<>(9);
for (int x = -1; x <= 1; x++)
{
@@ -469,6 +513,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
+ //=============//
+ // API helpers //
+ //=============//
+
+ @Override
+ public IDhApiTerrainDataCache getSoftCache() { return new DhApiTerrainDataCache(); }
+
+
+
//===============//
// debug methods //
//===============//
@@ -485,15 +538,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
Thread thread = new Thread(() -> {
try
{
- DhApiResult single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY);
- DhApiResult column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null);
+ DhApiResult single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, debugDataCache);
+ DhApiResult column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, debugDataCache);
DhLodPos chunkPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ).convertToDetailLevel(LodUtil.CHUNK_DETAIL_LEVEL);
- DhApiResult area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos);
+ DhApiResult area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos, debugDataCache);
IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
- DhApiResult rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000);
+ DhApiResult rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000, debugDataCache);
if (rayCast.payload != null && !rayCast.payload.pos.equals(currentDebugVec3i))
{
currentDebugVec3i = rayCast.payload.pos;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java
index 2e438978a..ec4df1ad7 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java
@@ -23,6 +23,12 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
+import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
+import com.seibel.distanthorizons.core.pos.DhChunkPos;
+import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
+import com.seibel.distanthorizons.core.util.objects.Pair;
+import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
+import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.network.messages.NetworkMessage;
import com.seibel.distanthorizons.core.network.session.Session;
@@ -30,17 +36,14 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
-import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
import com.seibel.distanthorizons.core.logging.SpamReducedLogger;
-import com.seibel.distanthorizons.core.pos.DhChunkPos;
-import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.renderer.TestRenderer;
import com.seibel.distanthorizons.core.util.RenderUtil;
-import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.world.DhClientServerWorld;
import com.seibel.distanthorizons.core.world.DhClientWorld;
@@ -49,13 +52,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
-import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
+import java.io.File;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Queue;
@@ -124,9 +126,28 @@ public class ClientApi
public synchronized void onClientOnlyConnected()
{
// only continue if the client is connected to a different server
- if (MC.clientConnectedToDedicatedServer())
+ boolean connectedToServer = MC.clientConnectedToDedicatedServer();
+ boolean connectedToReplay = MC.connectedToReplay();
+ if (connectedToServer || connectedToReplay)
{
- LOGGER.info("Client on ClientOnly mode connecting.");
+ if (connectedToServer)
+ {
+ LOGGER.info("Client on ClientOnly mode connecting.");
+ }
+ else
+ {
+ LOGGER.info("Replay on ClientServer mode connecting.");
+
+ if (Config.Client.Advanced.Logging.showReplayWarningOnStartup.get())
+ {
+ MC.sendChatMessage("\u00A76" + "Distant Horizons: Replay detected." + "\u00A7r"); // gold color
+ MC.sendChatMessage("DH may behave strangely or have missing functionality.");
+ MC.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:");
+ MC.sendChatMessage("\u00A77"+".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+"\u00A7r"); // light gray color
+ MC.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging.");
+ MC.sendChatMessage("");
+ }
+ }
// firing after clientLevelLoadEvent
// TODO if level has prepped to load it should fire level load event
@@ -447,7 +468,7 @@ public class ClientApi
if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering())
{
- ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, renderEventParam);
+ ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
}
}
else if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEBUG)
@@ -468,7 +489,7 @@ public class ClientApi
if (DhApi.Delayed.renderProxy.getDeferTransparentRendering())
{
- ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, renderEventParam);
+ ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null);
}
}
}
@@ -479,6 +500,7 @@ public class ClientApi
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons renderer has encountered an exception!");
MC.sendChatMessage("\u00A74Renderer is now disabled to prevent further issues.");
+ MC.sendChatMessage("\u00A74Please restart your game to re-enable Distant Horizons' LOD rendering.");
MC.sendChatMessage("\u00A74Exception detail: " + e);
}
finally
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java
index 7f16944bf..0c811b0a0 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java
@@ -138,7 +138,7 @@ public class ServerApi
//=======================//
public void serverChunkLoadEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); }
- public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); }
+ public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, true); }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java
index 91ed0745b..26b1afa9b 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java
@@ -29,7 +29,8 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
-import com.seibel.distanthorizons.core.util.LodUtil;
+import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
+import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
@@ -56,7 +57,7 @@ public class SharedApi
private static final Set UPDATING_CHUNK_POS_SET = ConcurrentHashMap.newKeySet();
/** how many chunks can be queued for updating per thread, used to prevent updates from infinitely pilling up if the user flys around extremely fast */
private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 500;
- private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 5_000;
+ private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 30_000;
private static final Timer CHUNK_UPDATE_TIMER = TimerUtil.CreateTimer("ChunkUpdateTimer");
@@ -101,7 +102,9 @@ public class SharedApi
{
DebugRenderer.clearRenderables();
MC_RENDER.clearTargetFrameBuffer();
- // needs to be closed on world shutdown to clear out un-processed chunks
+ // shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed
+ AbstractDhRepo.closeAllConnections();
+ // needs to be closed on world shutdown to clear out un-processed chunks
UPDATING_CHUNK_POS_SET.clear();
}
@@ -138,7 +141,8 @@ public class SharedApi
* Used to prevent getting a full chunk from MC if it isn't necessary.
* This is important since asking MC for a chunk is slow and may block the render thread.
*/
- public static boolean isChunkAtBlockPosAlreadyUpdating(int blockPosX, int blockPosZ) { return UPDATING_CHUNK_POS_SET.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ))); }
+ public static boolean isChunkAtBlockPosAlreadyUpdating(int blockPosX, int blockPosZ)
+ { return UPDATING_CHUNK_POS_SET.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ))); }
/** handles both block place and break events */
@@ -215,7 +219,13 @@ public class SharedApi
if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE)
{
lastOverloadedLogMessageMsTime = System.currentTimeMillis();
- LOGGER.warn("Too many chunks queued for updating, max queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread). This may result in holes in your LODs. Please move through the world slower, decrease your vanilla render distance, slow down your world pre-generator, or increase the CPU load config.");
+
+ String message = "Distant Horizons overloaded, too many chunks queued for updating. " +
+ "\nThis may result in holes in your LODs. " +
+ "\nPlease move through the world slower, decrease your vanilla render distance, slow down your world pre-generator, or increase the Distant Horizons' CPU load config. " +
+ "\nMax queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread).";
+ ClientApi.INSTANCE.showChatMessageNextFrame(message);
+ LOGGER.warn(message);
}
return;
@@ -310,6 +320,18 @@ public class SharedApi
}
+ // having a list of the nearby chunks is needed for lighting and beacon generation
+ ArrayList nearbyChunkList;
+ if (neighbourChunkList != null)
+ {
+ nearbyChunkList = neighbourChunkList;
+ }
+ else
+ {
+ nearbyChunkList = new ArrayList<>(1);
+ nearbyChunkList.add(chunkWrapper);
+ }
+
// 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
@@ -319,7 +341,7 @@ public class SharedApi
try
{
// If MC's lighting engine isn't thread safe this may cause the server thread to lag
- chunkWrapper.bakeDhLightingUsingMcLightingEngine(); // TODO handle unlit chunks, would pulling in the chunk from disk be a good idea? Look at ChunkLoader in the world gen code for an example
+ chunkWrapper.bakeDhLightingUsingMcLightingEngine();
}
catch (IllegalStateException e)
{
@@ -329,21 +351,16 @@ public class SharedApi
else
{
// generate the chunk's lighting, using neighboring chunks if present
-
- ArrayList nearbyChunkList;
- if (neighbourChunkList != null)
- {
- nearbyChunkList = neighbourChunkList;
- }
- else
- {
- nearbyChunkList = new ArrayList<>(1);
- nearbyChunkList.add(chunkWrapper);
- }
-
DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0);
}
+
+
+ // get this chunk's active beacons
+ List beaconBeamList = chunkWrapper.getAllActiveBeacons(nearbyChunkList);
+ dhLevel.setBeaconBeamsForChunk(chunkWrapper.getChunkPos(), beaconBeamList);
+
+
dhLevel.updateChunkAsync(chunkWrapper);
dhLevel.setChunkHash(chunkWrapper.getChunkPos(), newChunkHash);
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java
index 9233e59af..f865f9ec7 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java
@@ -20,17 +20,20 @@
package com.seibel.distanthorizons.core.config;
+import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.*;
import com.seibel.distanthorizons.api.enums.config.quickOptions.*;
import com.seibel.distanthorizons.api.enums.rendering.*;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.core.config.eventHandlers.*;
import com.seibel.distanthorizons.core.config.eventHandlers.presets.*;
+import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.config.types.*;
import com.seibel.distanthorizons.core.config.types.enums.*;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
+import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
@@ -136,6 +139,7 @@ public class Config
public static ConfigCategory fog = new ConfigCategory.Builder().set(Fog.class).build();
public static ConfigCategory ssao = new ConfigCategory.Builder().set(Ssao.class).build();
public static ConfigCategory noiseTextureSettings = new ConfigCategory.Builder().set(NoiseTextureSettings.class).build();
+ public static ConfigCategory genericRendering = new ConfigCategory.Builder().set(GenericRendering.class).build();
public static ConfigCategory advancedGraphics = new ConfigCategory.Builder().set(AdvancedGraphics.class).build();
@@ -288,7 +292,7 @@ public class Config
private static final Double FOG_RANGE_MAX = Math.sqrt(2.0);
public static ConfigEntry farFogStart = new ConfigEntry.Builder()
- .setMinDefaultMax(FOG_RANGE_MIN, 0.0, FOG_RANGE_MAX)
+ .setMinDefaultMax(FOG_RANGE_MIN, 0.4, FOG_RANGE_MAX)
.comment(""
+ "At what distance should the far fog start? \n"
+ "\n"
@@ -519,7 +523,8 @@ public class Config
public static ConfigEntry noiseSteps = new ConfigEntry.Builder()
.setMinDefaultMax(1, 4, null)
.comment(""
- + "How many steps of noise should be applied to LODs?")
+ + "How many steps of noise should be applied to LODs?"
+ + "")
.build();
public static ConfigEntry noiseIntensity = new ConfigEntry.Builder() // TODO: Make this a float (the ClassicConfigGUI doesn't support floats)
@@ -532,7 +537,34 @@ public class Config
.setMinDefaultMax(0, 1024, null)
.comment(""
+ "Defines how far should the noise texture render before it fades away. (in blocks) \n"
- + "Set to 0 to disable noise from fading away")
+ + "Set to 0 to disable noise from fading away \n"
+ + "")
+ .build();
+
+ }
+
+ public static class GenericRendering
+ {
+ public static ConfigEntry enableRendering = new ConfigEntry.Builder()
+ .set(true)
+ .comment(""
+ + "If true non terrain objects will be rendered in DH's terrain. \n"
+ + "This includes beacon beams and clouds. \n"
+ + "")
+ .build();
+
+ public static ConfigEntry enableBeaconRendering = new ConfigEntry.Builder()
+ .set(true)
+ .comment(""
+ + "If true LOD beacon beams will be rendered. \n"
+ + "")
+ .build();
+
+ public static ConfigEntry enableCloudRendering = new ConfigEntry.Builder()
+ .set(true)
+ .comment(""
+ + "If true LOD clouds will be rendered. \n"
+ + "")
.build();
}
@@ -589,8 +621,10 @@ public class Config
+ " does not have a ceiling.")
.build();
+ @Deprecated
public static ConfigEntry caveCullingHeight = new ConfigEntry.Builder()
.setMinDefaultMax(-4096, 40, 4096)
+ .setAppearance(EConfigEntryAppearance.ONLY_IN_API)
.comment(""
+ "At what Y value should cave culling start?")
.build();
@@ -761,6 +795,20 @@ public class Config
+ "")
.build();
+ public static ConfigEntry pullLightingForPregeneratedChunks = new ConfigEntry.Builder()
+ .set(false)
+ .comment(""
+ + "If true LOD generation for pre-existing chunks will attempt to pull the lighting data \n"
+ + "saved in Minecraft's Region files. \n"
+ + "If false DH will pull in chunks without lighting and re-light them. \n"
+ + " \n"
+ + "Setting this to true will result in faster LOD generation \n"
+ + "for already generated worlds, but is broken by most lighting mods. \n"
+ + " \n"
+ + "Set this to false if LODs are black. \n"
+ + "")
+ .build();
+
public static ConfigEntry dataCompression = new ConfigEntry.Builder()
.set(EDhApiDataCompressionMode.LZMA2)
.comment(""
@@ -809,13 +857,47 @@ public class Config
+ "")
.build();
- //public static ConfigEntry showMigrationChatWarning = new ConfigEntry.Builder()
- // .set(true)
- // .comment(""
- // + "Determines if a message should be displayed in the chat when LOD migration starts. \n"
- // + "")
- // .build();
+ public static ConfigEntry ignoredRenderBlockCsv = new ConfigEntry.Builder()
+ .set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire")
+ .comment(""
+ + "A comma separated list of block resource locations that won't be rendered by DH. \n"
+ + "Note: air is always included in this list. \n"
+ + "")
+ .build();
+ public static ConfigEntry ignoredRenderCaveBlockCsv = new ConfigEntry.Builder()
+ .set("minecraft:glow_lichen,minecraft:rail,minecraft:water,minecraft:lava,minecraft:bubble_column")
+ .comment(""
+ + "A comma separated list of block resource locations that shouldn't be rendered \n"
+ + "if they are in a 0 sky light underground area. \n"
+ + "Note: air is always included in this list. \n"
+ + "")
+ .build();
+
+ static
+ {
+ ignoredRenderBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv,
+ (blockCsv) ->
+ {
+ IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
+ if (wrapperFactory != null)
+ {
+ wrapperFactory.resetRendererIgnoredBlocksSet();
+ DhApi.Delayed.renderProxy.clearRenderDataCache();
+ }
+ }));
+
+ ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv,
+ (blockCsv) ->
+ {
+ IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
+ if (wrapperFactory != null)
+ {
+ wrapperFactory.resetRendererIgnoredCaveBlocks();
+ DhApi.Delayed.renderProxy.clearRenderDataCache();
+ }
+ }));
+ }
}
public static class Multiplayer
@@ -1192,7 +1274,7 @@ public class Config
// TODO default to error chat and info file
public static ConfigEntry logWorldGenEvent = new ConfigEntry.Builder()
.setServersideShortName("logWorldGenEvent")
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about the world generation process. \n"
+ "This can be useful for debugging.")
@@ -1200,7 +1282,7 @@ public class Config
public static ConfigEntry logWorldGenPerformance = new ConfigEntry.Builder()
.setServersideShortName("logWorldGenPerformance")
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log performance about the world generation process. \n"
+ "This can be useful for debugging.")
@@ -1208,7 +1290,7 @@ public class Config
public static ConfigEntry logWorldGenLoadEvent = new ConfigEntry.Builder()
.setServersideShortName("logWorldGenPerformance")
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about the world generation process. \n"
+ "This can be useful for debugging.")
@@ -1216,21 +1298,21 @@ public class Config
public static ConfigEntry logLodBuilderEvent = new ConfigEntry.Builder()
.setServersideShortName("logLodBuilderEvent")
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about the LOD generation process. \n"
+ "This can be useful for debugging.")
.build();
public static ConfigEntry logRendererBufferEvent = new ConfigEntry.Builder()
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about the renderer buffer process. \n"
+ "This can be useful for debugging.")
.build();
public static ConfigEntry logRendererGLEvent = new ConfigEntry.Builder()
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about the renderer OpenGL process. \n"
+ "This can be useful for debugging.")
@@ -1238,7 +1320,7 @@ public class Config
public static ConfigEntry logFileReadWriteEvent = new ConfigEntry.Builder()
.setServersideShortName("logFileReadWriteEvent")
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about file read/write operations. \n"
+ "This can be useful for debugging.")
@@ -1246,7 +1328,7 @@ public class Config
public static ConfigEntry logFileSubDimEvent = new ConfigEntry.Builder()
.setServersideShortName("logFileSubDimEvent")
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about file sub-dimension operations. \n"
+ "This can be useful for debugging.")
@@ -1254,7 +1336,7 @@ public class Config
public static ConfigEntry logNetworkEvent = new ConfigEntry.Builder()
.setServersideShortName("logNetworkEvent")
- .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE)
+ .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT)
.comment(""
+ "If enabled, the mod will log information about network operations. \n"
+ "This can be useful for debugging.")
@@ -1268,6 +1350,13 @@ public class Config
+ "memory allocated to run DH well.")
.build();
+ public static ConfigEntry showReplayWarningOnStartup = new ConfigEntry.Builder()
+ .set(true)
+ .comment(""
+ + "If enabled, a chat message will be displayed when a replay is started \n"
+ + "giving some basic information about how DH will function.")
+ .build();
+
}
public static class Debugging
@@ -1335,6 +1424,15 @@ public class Config
+ "")
.build();
+ public static ConfigEntry logBufferGarbageCollection = new ConfigEntry.Builder()
+ .set(false)
+ .comment(""
+ + "If true OpenGL Buffer garbage collection will be logged \n"
+ + "this also includes the number of live buffers. \n"
+ + "")
+ .build();
+
+
// Note: This will reset on game restart, and should have a warning on the tooltip
public static ConfigEntry allowUnsafeValues = new ConfigEntry.Builder()
.set(false)
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java
index 5a877f9f7..b982c621d 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java
@@ -260,20 +260,38 @@ public class ConfigEntry extends AbstractConfigType> implem
public byte isValid(T value, T min, T max)
{
if (this.configBase.disableMinMax)
+ {
return 0;
-
- if (value == null || this.value == null || value.getClass() != this.value.getClass()) // If the 2 variables aren't the same type then it will be invalid
+ }
+ else if (min == null && max == null)
+ {
+ // no validation is needed for this field
+ return 0;
+ }
+ else if (value == null || this.value == null
+ || value.getClass() != this.value.getClass())
+ {
+ // If the 2 variables aren't the same type then it will be invalid
return 2;
- if (Number.class.isAssignableFrom(value.getClass()))
- { // Only check min max if it is a number
+ }
+ else if (Number.class.isAssignableFrom(value.getClass()))
+ {
+ // Only check min max if it is a number
if (max != null && NumberUtil.greaterThan((Number) value, (Number) max))
+ {
return 1;
+ }
if (min != null && NumberUtil.lessThan((Number) value, (Number) min))
+ {
return -1;
+ }
return 0;
}
- return 0;
+ else
+ {
+ return 0;
+ }
}
/** This should normally not be called since set() automatically calls this */
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java
index bac72896c..5483f7769 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java
@@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance
*
* @author coolGi
*/
+@Deprecated // FIXME doesn't work with localization
public class ConfigLinkedEntry extends AbstractConfigType, ConfigLinkedEntry>
{
public ConfigLinkedEntry(AbstractConfigType, ?> value)
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java
index d10f2361e..87519bc3f 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java
@@ -62,6 +62,8 @@ public class FullDataSourceV2 implements IDataSource
/** measured in data columns */
public static final int WIDTH = 64;
+ /** how many chunks wide this datasource is. */
+ public static final int NUMB_OF_CHUNKS_WIDE = WIDTH / LodUtil.CHUNK_WIDTH;
public static final byte DATA_FORMAT_VERSION = 1;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java
index 9d3407c46..a00fb750d 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java
@@ -182,7 +182,7 @@ public class ColumnRenderSource implements IDataSource
EDhApiWorldGenerationStep worldGenStep = inputFullDataSource.getWorldGenStepAtRelativePos(x, z);
if (dataColumn != null && worldGenStep != EDhApiWorldGenerationStep.EMPTY)
{
- FullDataToRenderDataTransformer.convertColumnData(
+ FullDataToRenderDataTransformer.updateRenderDataViewWithFullDataColumn(
level, inputFullDataSource.mapping,
minBlockPos.x + x,
minBlockPos.z + z,
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java
index edf2a4c67..0cc51e422 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.util.ColorUtil;
@@ -475,10 +476,10 @@ public class ColumnBox
throw new RuntimeException("Loop error");
if (previousAdjDepth > adjYMax)
{
- if (irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS)
+ if (irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index)
{
// this LOD is underneath another, grass will never show here
- irisBlockMaterialId = IBlockStateWrapper.IrisBlockMaterial.DIRT;
+ irisBlockMaterialId = EDhApiBlockMaterial.DIRT.index;
}
builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId,
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java
index 8762bec32..8a95bc2c7 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java
@@ -50,8 +50,11 @@ public class ColumnRenderBuffer implements AutoCloseable
private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000;
- public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; // TODO what does the 4 represent
- public static final int MAX_QUADS_PER_BUFFER = (1024 * 1024 * 1) / QUADS_BYTE_SIZE; // TODO what do these multiples represent?
+ /** number of bytes a single quad takes */
+ public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4;
+ /** how big a single VBO can be in bytes */
+ public static final int MAX_VBO_BYTE_SIZE = 10 * 1024 * 1024; // 10 MB
+ public static final int MAX_QUADS_PER_BUFFER = MAX_VBO_BYTE_SIZE / QUADS_BYTE_SIZE;
public static final int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
index 02c064408..64c1abd02 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java
@@ -54,11 +54,6 @@ public class ColumnRenderBufferBuilder
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
- public static final int MAX_NUMBER_OF_CONCURRENT_CALLS_PER_THREAD = 3;
- public static int maxNumberOfConcurrentCalls = MAX_NUMBER_OF_CONCURRENT_CALLS_PER_THREAD;
-
-
-
//==============//
// vbo building //
@@ -87,33 +82,14 @@ public class ColumnRenderBufferBuilder
{
boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled;
- //EVENT_LOGGER.trace("RenderRegion start QuadBuild @ " + renderSource.sectionPos);
- boolean enableSkyLightCulling =
- Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get()
- && (
- // dimensions with a ceiling will be all caves so we don't want cave culling
- !clientLevel.getLevelWrapper().hasCeiling()
- // the end has a lot of overhangs with 0 lighting above the void, which look broken with
- // the current cave culling logic (this could probably be improved, but just skipping it works best for now)
- && !clientLevel.getLevelWrapper().getDimensionType().isTheEnd()
- // FIXME temporary fix
- // Cave culling is currently broken for any detail level above 0
- && DhSectionPos.getDetailLevel(renderSource.pos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL
- );
-
- int skyLightCullingBelow = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get();
- // FIXME: Clamp also to the max world height.
- skyLightCullingBelow = Math.max(skyLightCullingBelow, clientLevel.getMinY());
-
-
long builderStartTime = System.currentTimeMillis();
- LodQuadBuilder builder = new LodQuadBuilder(enableSkyLightCulling, (short) (skyLightCullingBelow - clientLevel.getMinY()), enableTransparency, clientLevel.getClientLevelWrapper());
+ LodQuadBuilder builder = new LodQuadBuilder(enableTransparency, clientLevel.getClientLevelWrapper());
makeLodRenderData(builder, renderSource, adjData);
long builderEndTime = System.currentTimeMillis();
long buildMs = builderEndTime - builderStartTime;
- LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs);
+ //LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs);
return builder;
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java
index 87b2e7720..a45fb2ec4 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
import com.seibel.distanthorizons.core.pos.DhLodPos;
@@ -95,53 +96,54 @@ public class CubicLodTemplate
}
case SHOW_BLOCK_MATERIAL:
{
- switch (blockMaterialId)
+
+ switch (EDhApiBlockMaterial.getFromIndex(blockMaterialId))
{
- case IBlockStateWrapper.IrisBlockMaterial.UNKOWN:
- case IBlockStateWrapper.IrisBlockMaterial.AIR: // shouldn't normally be rendered, but just in case
+ case UNKNOWN:
+ case AIR: // shouldn't normally be rendered, but just in case
color = ColorUtil.HOT_PINK;
break;
- case IBlockStateWrapper.IrisBlockMaterial.LEAVES:
+ case LEAVES:
color = ColorUtil.DARK_GREEN;
break;
- case IBlockStateWrapper.IrisBlockMaterial.STONE:
+ case STONE:
color = ColorUtil.GRAY;
break;
- case IBlockStateWrapper.IrisBlockMaterial.WOOD:
+ case WOOD:
color = ColorUtil.BROWN;
break;
- case IBlockStateWrapper.IrisBlockMaterial.METAL:
+ case METAL:
color = ColorUtil.DARK_GRAY;
break;
- case IBlockStateWrapper.IrisBlockMaterial.DIRT:
+ case DIRT:
color = ColorUtil.LIGHT_BROWN;
break;
- case IBlockStateWrapper.IrisBlockMaterial.LAVA:
+ case LAVA:
color = ColorUtil.ORANGE;
break;
- case IBlockStateWrapper.IrisBlockMaterial.DEEPSLATE:
+ case DEEPSLATE:
color = ColorUtil.BLACK;
break;
- case IBlockStateWrapper.IrisBlockMaterial.SNOW:
+ case SNOW:
color = ColorUtil.WHITE;
break;
- case IBlockStateWrapper.IrisBlockMaterial.SAND:
+ case SAND:
color = ColorUtil.TAN;
break;
- case IBlockStateWrapper.IrisBlockMaterial.TERRACOTTA:
+ case TERRACOTTA:
color = ColorUtil.DARK_ORANGE;
break;
- case IBlockStateWrapper.IrisBlockMaterial.NETHER_STONE:
+ case NETHER_STONE:
color = ColorUtil.DARK_RED;
break;
- case IBlockStateWrapper.IrisBlockMaterial.WATER:
+ case WATER:
color = ColorUtil.BLUE;
break;
- case IBlockStateWrapper.IrisBlockMaterial.GRASS:
+ case GRASS:
color = ColorUtil.GREEN;
break;
- case IBlockStateWrapper.IrisBlockMaterial.ILLUMINATED:
+ case ILLUMINATED:
color = ColorUtil.YELLOW;
break;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java
index 07744dcb9..ab3605bdd 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java
@@ -24,6 +24,7 @@ import java.nio.ByteOrder;
import java.util.*;
import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering;
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -50,7 +51,9 @@ public class LodQuadBuilder
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+ @Deprecated
public final boolean skipQuadsWithZeroSkylight;
+ @Deprecated
public final short skyLightCullingBelow;
@SuppressWarnings("unchecked")
@@ -122,7 +125,7 @@ public class LodQuadBuilder
// constructor //
//=============//
- public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean doTransparency, IClientLevelWrapper clientLevelWrapper)
+ public LodQuadBuilder(boolean doTransparency, IClientLevelWrapper clientLevelWrapper)
{
this.doTransparency = doTransparency;
for (int i = 0; i < 6; i++)
@@ -131,8 +134,8 @@ public class LodQuadBuilder
this.transparentQuads[i] = new ArrayList<>();
}
- this.skipQuadsWithZeroSkylight = enableSkylightCulling;
- this.skyLightCullingBelow = skyLightCullingBelow;
+ this.skipQuadsWithZeroSkylight = false;
+ this.skyLightCullingBelow = 0;
this.clientLevelWrapper = clientLevelWrapper;
this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get();
@@ -273,7 +276,7 @@ public class LodQuadBuilder
int color = quad.color;
// use custom side color logic for grass blocks
- if (quad.irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS)
+ if (quad.irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index)
{
// only use dirt colors if debug rendering is disabled
if (this.debugRenderingMode == EDhApiDebugRendering.OFF)
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
index 6f3117a6b..5c658e232 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core.dataObjects.transformers;
import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid;
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
@@ -32,6 +33,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
+import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
@@ -109,74 +111,85 @@ public class FullDataToRenderDataTransformer
}
columnSource.markNotEmpty();
+ int baseX = DhSectionPos.getMinCornerBlockX(pos);
+ int baseZ = DhSectionPos.getMinCornerBlockZ(pos);
- if (dataDetail == columnSource.getDataDetailLevel())
+ for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++)
{
- int baseX = DhSectionPos.getMinCornerBlockX(pos);
- int baseZ = DhSectionPos.getMinCornerBlockZ(pos);
-
- for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++)
+ for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++)
{
- for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++)
- {
- throwIfThreadInterrupted();
-
- ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
- LongArrayList dataColumn = fullDataSource.get(x, z);
- convertColumnData(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn);
- }
+ throwIfThreadInterrupted();
+
+ ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
+ LongArrayList dataColumn = fullDataSource.get(x, z);
+ updateRenderDataViewWithFullDataColumn(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn);
}
-
- columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL);
-
- }
- else
- {
- throw new UnsupportedOperationException("To be implemented");
- //FIXME: Implement different size creation of renderData
}
+
+ columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL);
+
return columnSource;
}
-
-
- //================//
- // helper methods //
- //================//
-
- /**
- * Called in loops that may run for an extended period of time.
- * This is necessary to allow canceling these transformers since running
- * them after the client has left a given world will throw exceptions.
- */
- private static void throwIfThreadInterrupted() throws InterruptedException
+ /** Updates the given {@link ColumnArrayView} to match the incoming Full data {@link LongArrayList} */
+ public static void updateRenderDataViewWithFullDataColumn(
+ IDhClientLevel level,
+ FullDataPointIdMap fullDataMapping, int blockX, int blockZ,
+ ColumnArrayView columnArrayView,
+ LongArrayList fullDataColumn)
{
- if (Thread.interrupted())
+ if (fullDataColumn == null || fullDataColumn.size() == 0)
{
- throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted.");
+ return;
+ }
+
+ int dataTotalLength = fullDataColumn.size();
+ if (dataTotalLength > columnArrayView.verticalSize())
+ {
+ ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength);
+ iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn);
+ columnArrayView.changeVerticalSizeFrom(totalColumnData);
+ }
+ else
+ {
+ iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits.
}
}
-
-
- // TODO what does this mean?
private static void iterateAndConvert(
- IDhClientLevel level, FullDataPointIdMap fullDataMapping,
- int blockX, int blockZ,
+ IDhClientLevel level, FullDataPointIdMap fullDataMapping,
+ int blockX, int blockZ,
ColumnArrayView renderColumnData, LongArrayList fullColumnData)
{
- boolean avoidSolidBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
+ boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
HashSet blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper());
+ HashSet caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(level.getLevelWrapper());
+
+ boolean caveCullingEnabled =
+ Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get()
+ && (
+ // dimensions with a ceiling will be all caves so we don't want cave culling
+ !level.getLevelWrapper().hasCeiling()
+ // the end has a lot of overhangs with 0 lighting above the void, which look broken with
+ // the current cave culling logic (this could probably be improved, but just skipping it works best for now)
+ && !level.getLevelWrapper().getDimensionType().isTheEnd()
+ );
boolean isVoid = true;
+
int colorToApplyToNextBlock = -1;
int lastColor = 0;
int lastBottom = -10000;
+
int skylightToApplyToNextBlock = -1;
int blocklightToApplyToNextBlock = -1;
int columnOffset = 0;
+ IBiomeWrapper biome = null;
+ IBlockStateWrapper block = null;
+
+
// goes from the top down
for (int i = 0; i < fullColumnData.size(); i++)
{
@@ -187,23 +200,6 @@ public class FullDataToRenderDataTransformer
int blockLight = FullDataPointUtil.getBlockLight(fullData);
int skyLight = FullDataPointUtil.getSkyLight(fullData);
- // TODO how should corrupted data be handled?
- // TODO why is the full data corrupted in the first place? FullDataPointUtil hasn't been changed in a long time, could one of the full data point objects be corrupted?
- // TODO if either of these happen the ID might also be invalid
- //if (bottomY + blockHeight > 300)
- //{
- // // this data point is too tall, it's probably a monolith
- // int k = 0;
- // throw new RuntimeException();
- //}
- //if (light > 16 || light < 0)
- //{
- // // light is out of range
- // throw new RuntimeException();
- //}
-
- IBiomeWrapper biome;
- IBlockStateWrapper block;
try
{
biome = fullDataMapping.getBiomeWrapper(id);
@@ -228,28 +224,72 @@ public class FullDataToRenderDataTransformer
}
- if (blockStatesToIgnore.contains(block))
+ //====================//
+ // ignored block and //
+ // cave culling check //
+ //====================//
+
+ boolean ignoreBlock = blockStatesToIgnore.contains(block);
+ boolean caveBlock = caveBlockStatesToIgnore.contains(block);
+ if (caveBlock)
{
- // Don't render: air, barriers, light blocks, etc.
+ if (caveCullingEnabled
+ // assume this data point is underground if it has no sky-light
+ && skyLight == LodUtil.MIN_MC_LIGHT
+ // cave culling shouldn't happen when at the top of the world
+ && columnOffset != 0
+ // cave culling can't happen when at the bottom of the world
+ && columnOffset != fullColumnData.size())
+ {
+ // we need to get the next sky/block lights because
+ // the air block here will always have a light of 0/0 due to only the top of the LOD's light being saved.
+ long nextFullData = fullColumnData.getLong(i+1);
+ int nextSkyLight = FullDataPointUtil.getSkyLight(nextFullData);
+
+ if (nextSkyLight == LodUtil.MIN_MC_LIGHT
+ && ColorUtil.getAlpha(lastColor) == 255)
+ {
+ // replace the previous block with new bottom
+ long columnData = renderColumnData.get(columnOffset - 1);
+ columnData = RenderDataPointUtil.setYMin(columnData, bottomY);
+ renderColumnData.set(columnOffset - 1, columnData);
+ }
+
+ continue;
+ }
+
+
+ if (ignoreBlock)
+ {
+ // this is a merged block and a cave block, so it should never be rendered
+ continue;
+ }
+ }
+ else if (ignoreBlock)
+ {
+ // this is an ignored block, but shouldn't be merged like a cave block
continue;
}
- // solid block check
- if (avoidSolidBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != IBlockStateWrapper.FULLY_OPAQUE)
+ //===================//
+ // solid block check //
+ //===================//
+
+ if (ignoreNonCollidingBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE)
{
if (colorBelowWithAvoidedBlocks)
{
int tempColor = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block);
- if (ColorUtil.getAlpha(tempColor) == 0)
+ // don't transfer the color when alpha is 0
+ if (ColorUtil.getAlpha(tempColor) != 0)
{
- //make sure to not transfer the color when alpha is 0
- continue;
+ // don't transfer alpha if for some reason grass is semi transparent
+ colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255);
+
+ skylightToApplyToNextBlock = skyLight;
+ blocklightToApplyToNextBlock = blockLight;
}
- //mare sure to not trnasfer alpha if for some reason grass is semi transparent
- colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255);
- skylightToApplyToNextBlock = skyLight;
- blocklightToApplyToNextBlock = blockLight;
}
// don't add this block
@@ -271,11 +311,11 @@ public class FullDataToRenderDataTransformer
skyLight = skylightToApplyToNextBlock;
blockLight = blocklightToApplyToNextBlock;
}
-
- //check if they share a top-bottom face and if they have same collor
+
+ //check if they share a top-bottom face and if they have same color
if (color == lastColor && bottomY + blockHeight == lastBottom && columnOffset > 0)
{
- //replace the previus block with new bottom
+ //replace the previous block with new bottom
long columnData = renderColumnData.get(columnOffset - 1);
columnData = RenderDataPointUtil.setYMin(columnData, bottomY);
renderColumnData.set(columnOffset - 1, columnData);
@@ -284,13 +324,13 @@ public class FullDataToRenderDataTransformer
{
// add the block
isVoid = false;
- long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getIrisBlockMaterialId());
+ long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getMaterialId());
renderColumnData.set(columnOffset, columnData);
columnOffset++;
}
lastBottom = bottomY;
lastColor = color;
-
+
}
@@ -300,24 +340,22 @@ public class FullDataToRenderDataTransformer
}
}
- // TODO what does this mean?
- public static void convertColumnData(IDhClientLevel level, FullDataPointIdMap fullDataMapping, int blockX, int blockZ, ColumnArrayView columnArrayView, LongArrayList fullDataColumn)
+
+
+ //================//
+ // helper methods //
+ //================//
+
+ /**
+ * Called in loops that may run for an extended period of time.
+ * This is necessary to allow canceling these transformers since running
+ * them after the client has left a given world will throw exceptions.
+ */
+ private static void throwIfThreadInterrupted() throws InterruptedException
{
- if (fullDataColumn == null || fullDataColumn.size() == 0)
+ if (Thread.interrupted())
{
- return;
- }
-
- int dataTotalLength = fullDataColumn.size();
- if (dataTotalLength > columnArrayView.verticalSize())
- {
- ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength);
- iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn);
- columnArrayView.changeVerticalSizeFrom(totalColumnData);
- }
- else
- {
- iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits.
+ throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted.");
}
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java
index 5f3001621..917aad07e 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java
@@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.EDhDirection;
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.util.FullDataPointUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
@@ -47,8 +46,6 @@ public class LodDataBuilder
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IBlockStateWrapper AIR = SingletonInjector.INSTANCE.get(IWrapperFactory.class).getAirBlockStateWrapper();
- /** how many chunks wide the {@link FullDataSourceV2} is. */
- private static final int NUMB_OF_CHUNKS_WIDE = FullDataSourceV2.WIDTH / LodUtil.CHUNK_WIDTH;
private static boolean getTopErrorLogged = false;
@@ -67,12 +64,8 @@ public class LodDataBuilder
- // get the section position
- int sectionPosX = chunkWrapper.getChunkPos().x;
- // negative positions start at -1 so the logic there is slightly different
- sectionPosX = (sectionPosX < 0) ? ((sectionPosX + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosX / NUMB_OF_CHUNKS_WIDE);
- int sectionPosZ = chunkWrapper.getChunkPos().z;
- sectionPosZ = (sectionPosZ < 0) ? ((sectionPosZ + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosZ / NUMB_OF_CHUNKS_WIDE);
+ int sectionPosX = getXOrZSectionPosFromChunkPos(chunkWrapper.getChunkPos().x);
+ int sectionPosZ = getXOrZSectionPosFromChunkPos(chunkWrapper.getChunkPos().z);
long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos);
@@ -98,30 +91,30 @@ public class LodDataBuilder
// -3 -> 1
// -4 -> 0 ---
// -5 -> 3
- chunkOffsetX = ((chunkOffsetX) % NUMB_OF_CHUNKS_WIDE);
+ chunkOffsetX = ((chunkOffsetX) % FullDataSourceV2.NUMB_OF_CHUNKS_WIDE);
if (chunkOffsetX != 0)
{
- chunkOffsetX += NUMB_OF_CHUNKS_WIDE;
+ chunkOffsetX += FullDataSourceV2.NUMB_OF_CHUNKS_WIDE;
}
}
else
{
- chunkOffsetX %= NUMB_OF_CHUNKS_WIDE;
+ chunkOffsetX %= FullDataSourceV2.NUMB_OF_CHUNKS_WIDE;
}
chunkOffsetX *= LodUtil.CHUNK_WIDTH;
int chunkOffsetZ = chunkWrapper.getChunkPos().z;
if (chunkWrapper.getChunkPos().z < 0)
{
- chunkOffsetZ = ((chunkOffsetZ) % NUMB_OF_CHUNKS_WIDE);
+ chunkOffsetZ = ((chunkOffsetZ) % FullDataSourceV2.NUMB_OF_CHUNKS_WIDE);
if (chunkOffsetZ != 0)
{
- chunkOffsetZ += NUMB_OF_CHUNKS_WIDE;
+ chunkOffsetZ += FullDataSourceV2.NUMB_OF_CHUNKS_WIDE;
}
}
else
{
- chunkOffsetZ %= NUMB_OF_CHUNKS_WIDE;
+ chunkOffsetZ %= FullDataSourceV2.NUMB_OF_CHUNKS_WIDE;
}
chunkOffsetZ *= LodUtil.CHUNK_WIDTH;
@@ -212,6 +205,7 @@ public class LodDataBuilder
longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight));
biome = newBiome;
blockState = newBlockState;
+
mappedId = dataSource.mapping.addIfNotPresentAndGetId(biome, blockState);
blockLight = newBlockLight;
skyLight = newSkyLight;
@@ -296,19 +290,28 @@ public class LodDataBuilder
// this block isn't on a chunk boundary, check if it is next to a transparent/air block
IBlockStateWrapper blockState = chunkWrapper.getBlockState(testBlockPos);
- return blockState.isAir() || blockState.getOpacity() != IBlockStateWrapper.FULLY_OPAQUE;
+ return blockState.isAir() || blockState.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE;
}
/** @throws ClassCastException if an API user returns the wrong object type(s) */
- public static FullDataSourceV2 createFromApiChunkData(DhApiChunk dataPoints) throws ClassCastException, DataCorruptedException
+ public static FullDataSourceV2 createFromApiChunkData(DhApiChunk apiChunk) throws ClassCastException, DataCorruptedException
{
- FullDataSourceV2 accessor = FullDataSourceV2.createEmpty(DhSectionPos.encode(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ)));
- for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
+ // get the section position
+ int sectionPosX = getXOrZSectionPosFromChunkPos(apiChunk.chunkPosX);
+ int sectionPosZ = getXOrZSectionPosFromChunkPos(apiChunk.chunkPosZ);
+ long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
+
+ // chunk relative block position in the data source
+ int relSourceBlockX = Math.floorMod(apiChunk.chunkPosX, 4) * LodUtil.CHUNK_WIDTH;
+ int relSourceBlockZ = Math.floorMod(apiChunk.chunkPosZ, 4) * LodUtil.CHUNK_WIDTH;
+
+ FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos);
+ for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++)
{
- for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
+ for (int relBlockX = 0; relBlockX < LodUtil.CHUNK_WIDTH; relBlockX++)
{
- List columnDataPoints = dataPoints.getDataPoints(relX, relZ);
+ List columnDataPoints = apiChunk.getDataPoints(relBlockX, relBlockZ);
// this null check does 2 nice things at the same time:
@@ -322,7 +325,7 @@ public class LodDataBuilder
{
DhApiTerrainDataPoint dataPoint = columnDataPoints.get(index);
- int id = accessor.mapping.addIfNotPresentAndGetId(
+ int id = dataSource.mapping.addIfNotPresentAndGetId(
(IBiomeWrapper) (dataPoint.biomeWrapper),
(IBlockStateWrapper) (dataPoint.blockStateWrapper)
);
@@ -330,7 +333,7 @@ public class LodDataBuilder
packedDataPoints.set(index, FullDataPointUtil.encode(
id,
dataPoint.topYBlockPos - dataPoint.bottomYBlockPos,
- dataPoint.bottomYBlockPos - dataPoints.topYBlockPos,
+ dataPoint.bottomYBlockPos - apiChunk.bottomYBlockPos,
(byte) (dataPoint.blockLightLevel),
(byte) (dataPoint.skyLightLevel)
));
@@ -338,11 +341,14 @@ public class LodDataBuilder
// TODO add the ability for API users to define a different compression mode
// or add a "unkown" compression mode
- accessor.setSingleColumn(packedDataPoints, relX, relZ, EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS);
+ dataSource.setSingleColumn(
+ packedDataPoints,
+ relBlockX + relSourceBlockX, relBlockZ + relSourceBlockZ,
+ EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS);
+ dataSource.isEmpty = false;
}
}
-
- return accessor;
+ return dataSource;
}
@@ -353,4 +359,13 @@ public class LodDataBuilder
public static boolean canGenerateLodFromChunk(IChunkWrapper chunk) { return chunk != null && chunk.isLightCorrect(); }
+ public static int getXOrZSectionPosFromChunkPos(int chunkXOrZPos)
+ {
+ // get the section position
+ int sectionPos = chunkXOrZPos;
+ // negative positions start at -1 so the logic there is slightly different
+ sectionPos = (sectionPos < 0) ? ((sectionPos + 1) / FullDataSourceV2.NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPos / FullDataSourceV2.NUMB_OF_CHUNKS_WIDE);
+ return sectionPos;
+ }
+
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java
index ff7cebd50..757200b18 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java
@@ -25,7 +25,7 @@ import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3i;
+import com.seibel.distanthorizons.core.util.math.Vec3i;
/**
* An (almost) exact copy of Minecraft's
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java
index 7c9c36308..628aabf80 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java
@@ -41,13 +41,17 @@ import java.util.*;
*/
public class ClientOnlySaveStructure extends AbstractSaveStructure
{
- final File folder;
- private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
- private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
+ public static final String SERVER_DATA_FOLDER_NAME = "Distant_Horizons_server_data";
+ public static final String REPLAY_SERVER_FOLDER_NAME = "REPLAY";
public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]";
- SubDimensionLevelMatcher subDimMatcher = null;
- final HashMap levelWrapperToFileMap = new HashMap<>();
+ private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
+ private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
+
+
+ private SubDimensionLevelMatcher subDimMatcher = null;
+ private final File folder;
+ private final HashMap levelWrapperToFileMap = new HashMap<>();
@@ -237,7 +241,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
private static String getSaveStructureFolderPath()
{
String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar
- + "Distant_Horizons_server_data" + File.separatorChar
+ + SERVER_DATA_FOLDER_NAME + File.separatorChar
+ getServerFolderName();
return path;
}
@@ -245,6 +249,14 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
/** Generated from the server the client is currently connected to. */
private static String getServerFolderName()
{
+ // if connected to a replay we won't have any server info
+ // use the dedicated replay server folder
+ if (MC_CLIENT.connectedToReplay())
+ {
+ return REPLAY_SERVER_FOLDER_NAME;
+ }
+
+
// parse the current server's IP
ParsedIp parsedIp = new ParsedIp(MC_CLIENT.getCurrentServerIp());
String serverIpCleaned = parsedIp.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java
index 6263bc23c..841b0b2f8 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java
@@ -190,7 +190,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable
}
FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk);
// convert to a data source for easier comparing
- FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encode(this.playerData.playerBlockPos));
+ FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos));
newDataSource.update(newChunkSizedFullDataView);
@@ -215,7 +215,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable
// get the data source to compare against
try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null))
{
- testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encode(this.playerData.playerBlockPos)).join();
+ testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)).join();
if (testFullDataSource == null)
{
continue;
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java
new file mode 100644
index 000000000..a3d5603e3
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java
@@ -0,0 +1,117 @@
+package com.seibel.distanthorizons.core.generation;
+
+import com.seibel.distanthorizons.core.pos.DhChunkPos;
+import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
+import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/** holds adjacent chunks without having to create new Pos objects */
+public class AdjacentChunkHolder
+{
+ final IChunkWrapper[] chunkArray = new IChunkWrapper[9];
+
+
+ //==============//
+ // constructors //
+ //==============//
+
+ public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; }
+
+ public AdjacentChunkHolder(IChunkWrapper centerWrapper, @NotNull ArrayList nearbyChunkList)
+ {
+ this.chunkArray[4] = centerWrapper;
+
+ DhChunkPos centerChunkPos = centerWrapper.getChunkPos();
+
+ // generate the list of chunk pos we need,
+ // currently a 3x3 grid
+ HashSet requestedAdjacentPositions = new HashSet<>(9);
+ for (int xOffset = -1; xOffset <= 1; xOffset++)
+ {
+ for (int zOffset = -1; zOffset <= 1; zOffset++)
+ {
+ DhChunkPos adjacentPos = new DhChunkPos(centerChunkPos.x + xOffset, centerChunkPos.z + zOffset);
+ requestedAdjacentPositions.add(adjacentPos);
+ }
+ }
+
+ 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);
+ if (chunk != null && requestedAdjacentPositions.contains(chunk.getChunkPos()))
+ {
+ // remove the newly found position
+ requestedAdjacentPositions.remove(chunk.getChunkPos());
+
+ // add the adjacent chunk
+ this.add(chunk);
+ }
+
+ if (requestedAdjacentPositions.isEmpty())
+ {
+ // we found every chunk we needed, we don't need to keep iterating
+ break;
+ }
+ }
+ }
+
+
+
+ //=========//
+ // methods //
+ //=========//
+
+ public void add(IChunkWrapper centerWrapper)
+ {
+ DhChunkPos centerPos = this.chunkArray[4].getChunkPos();
+ DhChunkPos offsetPos = centerWrapper.getChunkPos();
+
+ int offsetX = offsetPos.x - centerPos.x;
+ if (offsetX < -1 || offsetX > 1)
+ {
+ return;
+ }
+
+ int offsetZ = offsetPos.z - centerPos.z;
+ if (offsetZ < -1 || offsetZ > 1)
+ {
+ return;
+ }
+
+ // equivalent to 4 + offsetX + (offsetZ * 3).
+ this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper;
+ }
+
+ public IChunkWrapper getByBlockPos(int blockX, int blockZ)
+ {
+ int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4);
+ int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4);
+ IChunkWrapper centerChunk = this.chunkArray[4];
+ DhChunkPos centerPos = centerChunk.getChunkPos();
+ if (centerPos.x == chunkX && centerPos.z == chunkZ)
+ {
+ return centerChunk;
+ }
+
+ int offsetX = chunkX - centerPos.x;
+ if (offsetX < -1 || offsetX > 1)
+ {
+ return null;
+ }
+
+ int offsetZ = chunkZ - centerPos.z;
+ if (offsetZ < -1 || offsetZ > 1)
+ {
+ return null;
+ }
+
+ // equivalent to 4 + offsetX + (offsetZ * 3).
+ return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)];
+ }
+
+
+}
+
\ No newline at end of file
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 6648f62ee..c160ef482 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
@@ -26,7 +26,6 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
-import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
import org.apache.logging.log4j.Logger;
import java.util.*;
@@ -152,7 +151,7 @@ public class DhLightingEngine
for (int y = maxY; y >= minY; y--)
{
IBlockStateWrapper block = chunk.getBlockState(relX, y, relZ);
- if (block != null && block.getOpacity() != IBlockStateWrapper.FULLY_TRANSPARENT)
+ if (block != null && block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
// keep moving down until we find a non-transparent block
break;
@@ -306,64 +305,6 @@ public class DhLightingEngine
}
- /** holds the adjacent chunks without having to create new Pos objects */
- private static class AdjacentChunkHolder
- {
- final IChunkWrapper[] chunkArray = new IChunkWrapper[9];
-
-
- public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; }
-
-
- public void add(IChunkWrapper centerWrapper)
- {
- DhChunkPos centerPos = this.chunkArray[4].getChunkPos();
- DhChunkPos offsetPos = centerWrapper.getChunkPos();
-
- int offsetX = offsetPos.x - centerPos.x;
- if (offsetX < -1 || offsetX > 1)
- {
- return;
- }
-
- int offsetZ = offsetPos.z - centerPos.z;
- if (offsetZ < -1 || offsetZ > 1)
- {
- return;
- }
-
- // equivalent to 4 + offsetX + (offsetZ * 3).
- this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper;
- }
-
- public IChunkWrapper getByBlockPos(int blockX, int blockZ)
- {
- int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4);
- int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4);
- IChunkWrapper centerChunk = this.chunkArray[4];
- DhChunkPos centerPos = centerChunk.getChunkPos();
- if (centerPos.x == chunkX && centerPos.z == chunkZ)
- {
- return centerChunk;
- }
-
- int offsetX = chunkX - centerPos.x;
- if (offsetX < -1 || offsetX > 1)
- {
- return null;
- }
-
- int offsetZ = chunkZ - centerPos.z;
- if (offsetZ < -1 || offsetZ > 1)
- {
- return null;
- }
-
- // equivalent to 4 + offsetX + (offsetZ * 3).
- return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)];
- }
- }
-
/**
* 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/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java
index 452a7b97e..64b8e9db3 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java
@@ -79,7 +79,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
// TODO this logic isn't great and can cause a limit to how many threads could be used for world generation,
// however it won't cause duplicate requests or concurrency issues, so it will be good enough for now.
// A good long term fix may be to either:
- // 1. allow the generator to deal with larger sections (let the generator threads split up larger tasks into smaller one
+ // 1. allow the generator to deal with larger sections (let the generator threads split up larger tasks into smaller ones
// 2. batch requests better. instead of sending 4 individual tasks of detail level N, send 1 task of detail level n+1
private final ExecutorService queueingThread = ThreadUtil.makeSingleThreadPool("World Gen Queue");
private boolean generationQueueRunning = false;
@@ -227,6 +227,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
catch (Exception e)
{
LOGGER.error("queueing exception: " + e.getMessage(), e);
+ }
+ finally
+ {
this.generationQueueRunning = false;
}
});
@@ -374,7 +377,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
// don't log the shutdown exceptions
if (!LodUtil.isInterruptOrReject(exception))
{
- LOGGER.error("Error generating data for section " + taskPos, exception);
+ LOGGER.error("Error generating data for pos: " + DhSectionPos.toString(taskPos), exception);
}
newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateFail()));
@@ -384,11 +387,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(DhSectionPos.encode(granularity, DhSectionPos.getX(taskPos), DhSectionPos.getZ(taskPos)))));
}
boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, newTaskGroup);
- LodUtil.assertTrue(worked);
+ LodUtil.assertTrue(worked, "Unable to find in progress generator task with position ["+DhSectionPos.toString(taskPos)+"]");
}
catch (Exception e)
{
- LOGGER.error("Unexpected error completing world gen task: "+taskPos, e);
+ LOGGER.error("Unexpected error completing world gen task at pos: ["+DhSectionPos.toString(taskPos)+"].", e);
}
});
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java
index b556bdcff..1c3b1e08d 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java
@@ -19,15 +19,28 @@
package com.seibel.distanthorizons.core.level;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent;
+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.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder;
import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache;
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;
+import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO;
+import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
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.coreapi.DependencyInjection.ApiEventInjector;
import org.apache.logging.log4j.Logger;
@@ -35,8 +48,12 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
public abstract class AbstractDhLevel implements IDhLevel
{
@@ -47,21 +64,37 @@ public abstract class AbstractDhLevel implements IDhLevel
/** if this is null then the other handler is probably null too, but just in case */
@Nullable
public ChunkHashRepo chunkHashRepo;
+ /** if this is null then the other handler is probably null too, but just in case */
+ @Nullable
+ public BeaconBeamRepo beaconBeamRepo;
protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 2_000);
/** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */
protected final ConcurrentHashMap> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>();
+ /** Will be null if clouds shouldn't be rendered for this level. */
+ @Nullable
+ protected CloudRenderHandler cloudRenderHandler;
+ protected BeaconRenderHandler beaconRenderHandler;
+
//=============//
// constructor //
//=============//
- protected AbstractDhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); }
+ protected AbstractDhLevel()
+ {
+ this.chunkToLodBuilder = new ChunkToLodBuilder();
+ }
- protected void createAndSetChunkHashRepo(File databaseFile)
+ /**
+ * Creating the repos requires access to the level file, which isn't
+ * available at constructor time.
+ */
+ protected void createAndSetSupportingRepos(File databaseFile)
{
+ // chunk hash
ChunkHashRepo newChunkHashRepo = null;
try
{
@@ -72,6 +105,41 @@ public abstract class AbstractDhLevel implements IDhLevel
LOGGER.error("Unable to create [ChunkHashRepo], error: ["+e.getMessage()+"].", e);
}
this.chunkHashRepo = newChunkHashRepo;
+
+
+ // beacon beam
+ BeaconBeamRepo newBeaconBeamRepo = null;
+ try
+ {
+ newBeaconBeamRepo = new BeaconBeamRepo("jdbc:sqlite", databaseFile);
+ }
+ catch (SQLException e)
+ {
+ LOGGER.error("Unable to create [BeaconBeamRepo], error: ["+e.getMessage()+"].", e);
+ }
+ 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);
+ }
+ }
}
@@ -126,6 +194,13 @@ public abstract class AbstractDhLevel implements IDhLevel
}
+
+ //=======//
+ // repos //
+ //=======//
+
+ // chunk hash //
+
@Override
public int getChunkHash(DhChunkPos pos)
{
@@ -148,11 +223,55 @@ public abstract class AbstractDhLevel implements IDhLevel
+ //=================//
+ // beacon handling //
+ //=================//
+
+ @Override
+ public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList)
+ {
+ if (this.beaconRenderHandler != null)
+ {
+ this.beaconRenderHandler.setBeaconBeamsForChunk(chunkPos, newBeamList);
+ }
+ }
+
+ @Override
+ public void loadBeaconBeamsInPos(long pos)
+ {
+ if (this.beaconRenderHandler != null)
+ {
+ this.beaconRenderHandler.loadBeaconBeamsInPos(pos);
+ }
+ }
+ @Override
+ public void unloadBeaconBeamsInPos(long pos)
+ {
+ if (this.beaconRenderHandler != null)
+ {
+ this.beaconRenderHandler.unloadBeaconBeamsInPos(pos);
+ }
+ }
+
+
+
//================//
// base overrides //
//================//
@Override
- public void close() { this.chunkToLodBuilder.close(); }
+ public void close()
+ {
+ this.chunkToLodBuilder.close();
+
+ if (this.chunkHashRepo != null)
+ {
+ this.chunkHashRepo.close();
+ }
+ if (this.beaconBeamRepo != null)
+ {
+ this.beaconBeamRepo.close();
+ }
+ }
}
\ No newline at end of file
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java
index 7ed8e1e43..99dd8fcf7 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java
@@ -27,13 +27,12 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.file.AbstractDataSourceHandler;
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
-import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.render.LodQuadTree;
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.util.LodUtil;
-import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -41,9 +40,7 @@ import org.apache.logging.log4j.Logger;
import javax.annotation.WillNotClose;
import java.io.Closeable;
-import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicReference;
public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.IDataSourceUpdateFunc
@@ -56,6 +53,14 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
@WillNotClose
public final FullDataSourceProviderV2 fullDataSourceProvider;
public final AtomicReference ClientRenderStateRef = new AtomicReference<>();
+ /**
+ * This is handled outside of the {@link ClientRenderState} to prevent destroying
+ * the {@link GenericObjectRenderer} when changing render distances or enabling/disabling rendering.
+ *
+ * Destroying the {@link GenericObjectRenderer} would cause any existing bindings to be
+ * erroneously removed.
+ */
+ public final GenericObjectRenderer genericRenderer = new GenericObjectRenderer();
@@ -108,7 +113,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
}
clientRenderState.close();
- clientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider());
+ clientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer);
if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
{
//FIXME: How to handle this?
@@ -128,7 +133,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
}
if (isBuffersDirty)
{
- clientRenderState.renderer.bufferHandler.MarkAllBuffersDirty();
+ clientRenderState.lodRenderer.bufferHandler.MarkAllBuffersDirty();
}
}
@@ -141,7 +146,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
public boolean startRenderer(IClientLevelWrapper clientLevelWrapper)
{
// TODO why are we passing in a level wrapper? Our client level is already defined.
- ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider());
+ ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer);
if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState))
{
LOGGER.warn("Failed to start renderer due to concurrency");
@@ -167,7 +172,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
// either the renderer hasn't been started yet, or is being reloaded
return;
}
- ClientRenderState.renderer.drawLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler);
+ ClientRenderState.lodRenderer.drawLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler);
}
public void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler)
@@ -178,7 +183,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
// either the renderer hasn't been started yet, or is being reloaded
return;
}
- ClientRenderState.renderer.drawDeferredLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler);
+ ClientRenderState.lodRenderer.drawDeferredLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler);
}
public void stopRenderer()
@@ -275,15 +280,25 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
// helper classes //
//================//
- public static class ClientRenderState
+ public static class ClientRenderState implements Closeable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final IClientLevelWrapper clientLevelWrapper;
public final LodQuadTree quadtree;
- public final LodRenderer renderer;
+ public final RenderBufferHandler renderBufferHandler;
+ public final LodRenderer lodRenderer;
- public ClientRenderState(IDhClientLevel dhClientLevel, IClientLevelWrapper clientLevelWrapper, FullDataSourceProviderV2 fullDataSourceProvider)
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public ClientRenderState(
+ IDhClientLevel dhClientLevel, IClientLevelWrapper clientLevelWrapper,
+ FullDataSourceProviderV2 fullDataSourceProvider,
+ GenericObjectRenderer genericRenderer)
{
this.clientLevelWrapper = clientLevelWrapper;
@@ -292,17 +307,22 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I
0, 0,
fullDataSourceProvider);
- RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree);
- this.renderer = new LodRenderer(renderBufferHandler);
+ this.renderBufferHandler = new RenderBufferHandler(this.quadtree);
+ this.lodRenderer = new LodRenderer(this.renderBufferHandler, genericRenderer);
}
+ //================//
+ // base overrides //
+ //================//
+
+ @Override
public void close()
{
LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName());
- this.renderer.close();
+ this.lodRenderer.close();
this.quadtree.close();
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java
index 9c7eda259..af69a3d73 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java
@@ -34,6 +34,8 @@ import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue;
import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource;
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
+import com.seibel.distanthorizons.core.render.RenderBufferHandler;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
@@ -96,6 +98,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
LOGGER.warn("unable to create data folder.");
}
this.levelWrapper = clientLevelWrapper;
+ this.levelWrapper.setParentLevel(this);
this.saveStructure = saveStructure;
this.networkState = networkState;
@@ -117,7 +120,8 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
this.clientside = new ClientLevelModule(this);
- this.createAndSetChunkHashRepo(this.dataFileHandler.repo.databaseFile);
+ this.createAndSetSupportingRepos(this.dataFileHandler.repo.databaseFile);
+ this.runRepoReliantSetup();
if (enableRendering)
{
@@ -298,10 +302,11 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
this.eventSource.close();
}
+ this.levelWrapper.setParentLevel(null);
this.clientside.close();
super.close();
this.dataFileHandler.close();
- LOGGER.info("Closed " + DhClientLevel.class.getSimpleName() + " for " + this.levelWrapper);
+ LOGGER.info("Closed [" + DhClientLevel.class.getSimpleName() + "] for [" + this.levelWrapper + "]");
}
@Override
@@ -314,6 +319,15 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
public boolean hasSkyLight() { return this.levelWrapper.hasSkyLight(); }
+ @Override
+ public GenericObjectRenderer getGenericRenderer() { return this.clientside.genericRenderer; }
+ @Override
+ public RenderBufferHandler getRenderBufferHandler()
+ {
+ ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get();
+ return (renderState != null) ? renderState.renderBufferHandler : null;
+ }
+
@Override
public void onWorldGenTaskComplete(long pos)
{
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java
index 42698d6d7..274bc8c39 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java
@@ -24,11 +24,13 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
+import com.seibel.distanthorizons.core.render.RenderBufferHandler;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -66,9 +68,11 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
LOGGER.warn("unable to create data folder.");
}
this.serverLevelWrapper = serverLevelWrapper;
+ this.serverLevelWrapper.setParentLevel(this);
this.serverside = new ServerLevelModule(this, saveStructure);
this.clientside = new ClientLevelModule(this);
- this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile);
+ this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile);
+ this.runRepoReliantSetup();
LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure);
}
@@ -102,20 +106,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
if (shouldDoWorldGen && !isWorldGenRunning)
{
// start world gen
-
- // create a new queue
this.serverside.worldGenModule.startWorldGen(this.serverside.fullDataFileHandler, new ServerLevelModule.WorldGenState(this));
-
- // TODO I think this used to queue the world gen
- // is it still needed?
- // populate the queue based on the current rendering tree
- //ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get();
- //Iterator> iterator = renderState.quadtree.leafNodeIterator();
- //while (iterator.hasNext())
- //{
- // QuadNode node = iterator.next();
- // //this.serverside.dataFileHandler.getAsync(node.sectionPos);
- //}
}
else if (!shouldDoWorldGen && isWorldGenRunning)
{
@@ -160,7 +151,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
}
@Override
- public IClientLevelWrapper getClientLevelWrapper() { return this.serverLevelWrapper.tryGetClientLevelWrapper(); }
+ public IClientLevelWrapper getClientLevelWrapper() { return MC_CLIENT.getWrappedClientLevel(); }
@Override
public void clearRenderCache()
@@ -232,6 +223,16 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev
}
+ @Override
+ public GenericObjectRenderer getGenericRenderer() { return this.clientside.genericRenderer; }
+ @Override
+ public RenderBufferHandler getRenderBufferHandler()
+ {
+ ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get();
+ return (renderState != null) ? renderState.renderBufferHandler : null;
+ }
+
+
//===============//
// data handling //
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java
index a4ee82e46..7a84219f8 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java
@@ -40,9 +40,11 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
+import com.seibel.distanthorizons.core.render.RenderBufferHandler;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
import org.apache.logging.log4j.Logger;
import java.text.MessageFormat;
@@ -75,6 +77,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
private final ConcurrentMap requestGroupsByFutureId = new ConcurrentHashMap<>();
+ //=============//
+ // constructor //
+ //=============//
+
public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler)
{
if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs())
@@ -83,7 +89,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
}
this.serverLevelWrapper = serverLevelWrapper;
this.serverside = new ServerLevelModule(this, saveStructure);
- this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile);
+ this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile);
+ this.runRepoReliantSetup();
LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure);
@@ -245,6 +252,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
this.worldGenPlayerCenteringQueue.add(serverPlayer);
}
+ //=========//
+ // methods //
+ //=========//
+
public void removePlayer(IServerPlayerWrapper serverPlayer)
{
this.worldGenPlayerCenteringQueue.remove(serverPlayer);
@@ -444,6 +455,18 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel
}
}
+ @Override
+ public GenericObjectRenderer getGenericRenderer()
+ {
+ // server-only levels don't support rendering
+ return null;
+ }
+ @Override
+ public RenderBufferHandler getRenderBufferHandler()
+ {
+ // server-only levels don't support rendering
+ return null;
+ }
//===========//
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java
index 8f21f32ab..15b392a6c 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java
@@ -23,8 +23,12 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2;
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
+import com.seibel.distanthorizons.core.render.RenderBufferHandler;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
+import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
+import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -44,6 +48,10 @@ public interface IDhLevel extends AutoCloseable
void setChunkHash(DhChunkPos pos, int chunkHash);
void updateChunkAsync(IChunkWrapper chunk);
+ void loadBeaconBeamsInPos(long pos);
+ void setBeaconBeamsForChunk(DhChunkPos chunkPos, List beamList);
+ void unloadBeaconBeamsInPos(long pos);
+
FullDataSourceProviderV2 getFullDataProvider();
AbstractSaveStructure getSaveStructure();
@@ -60,5 +68,17 @@ public interface IDhLevel extends AutoCloseable
void addDebugMenuStringsToList(List messageList);
+ /**
+ * Will return null if the renderer isn't set up yet.
+ * Not supported on the server-side.
+ */
+ @Nullable
+ GenericObjectRenderer getGenericRenderer();
+ /**
+ * Will return null if the renderer isn't set up yet.
+ * Not supported on the server-side.
+ */
+ @Nullable
+ RenderBufferHandler getRenderBufferHandler();
}
\ No newline at end of file
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java
index eacad1045..cc3b817cb 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java
@@ -22,14 +22,13 @@ package com.seibel.distanthorizons.core.logging.f3;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.jetbrains.annotations.Nullable;
-import java.lang.ref.WeakReference;
import java.text.NumberFormat;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
@@ -42,23 +41,6 @@ public class F3Screen
- //============//
- // properties //
- //============//
-
- private static WeakReference renderBufferHandlerRef = new WeakReference<>(null);
- public static void setRenderBufferHandler(@Nullable RenderBufferHandler renderBufferHandler)
- {
- if (renderBufferHandler != null && renderBufferHandlerRef.get() != null)
- {
- LOGGER.warn("multiple RenderBufferHandlers are active at once, the F3 menu may not be accurate.");
- }
-
- renderBufferHandlerRef = new WeakReference<>(renderBufferHandler);
- }
-
-
-
//=================//
// injection point //
//=================//
@@ -106,23 +88,28 @@ public class F3Screen
// chunk updates
messageList.add(SharedApi.INSTANCE.getDebugMenuString());
messageList.add("");
- // rendering
- RenderBufferHandler renderBufferHandler = renderBufferHandlerRef.get();
- if (renderBufferHandler != null)
- {
- messageList.add(renderBufferHandler.getVboRenderDebugMenuString());
- String showPassString = renderBufferHandler.getShadowPassRenderDebugMenuString();
- if (showPassString != null)
- {
- messageList.add(showPassString);
- }
- messageList.add("");
- }
// world / levels
world.addDebugMenuStringsToList(messageList);
for (IDhLevel level : levelIterator)
{
level.addDebugMenuStringsToList(messageList);
+ // LOD rendering
+ RenderBufferHandler renderBufferHandler = level.getRenderBufferHandler();
+ if (renderBufferHandler != null)
+ {
+ messageList.add(renderBufferHandler.getVboRenderDebugMenuString());
+ String showPassString = renderBufferHandler.getShadowPassRenderDebugMenuString();
+ if (showPassString != null)
+ {
+ messageList.add(showPassString);
+ }
+ }
+ // Generic rendering
+ GenericObjectRenderer genericRenderer = level.getGenericRenderer();
+ if (genericRenderer != null)
+ {
+ messageList.add(genericRenderer.getVboRenderDebugMenuString());
+ }
}
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java
index fea474574..f81742b1f 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java
@@ -19,7 +19,8 @@
package com.seibel.distanthorizons.core.pos;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.LodUtil;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
public class DhChunkPos
{
@@ -31,6 +32,10 @@ public class DhChunkPos
+ //==============//
+ // constructors //
+ //==============//
+
public DhChunkPos(int x, int z)
{
this.x = x;
@@ -57,6 +62,10 @@ public class DhChunkPos
+ //=========//
+ // methods //
+ //=========//
+
public DhBlockPos center() { return new DhBlockPos(8 + this.x << 4, 0, 8 + this.z << 4); }
public DhBlockPos corner() { return new DhBlockPos(this.x << 4, 0, this.z << 4); }
@@ -70,6 +79,17 @@ public class DhChunkPos
public DhBlockPos2D getMinBlockPos() { return new DhBlockPos2D(this.x << 4, this.z << 4); }
+ public boolean contains(DhBlockPos pos)
+ {
+ int minBlockX = this.getMinBlockX();
+ int minBlockZ = this.getMinBlockZ();
+ int maxBlockX = minBlockX + LodUtil.CHUNK_WIDTH;
+ int maxBlockZ = minBlockZ + LodUtil.CHUNK_WIDTH;
+
+ return minBlockX <= pos.x && pos.x <= maxBlockX
+ && minBlockZ <= pos.z && pos.z <= maxBlockZ;
+ }
+
public long getLong() { return toLong(this.x, this.z); }
public double distance(DhChunkPos other)
@@ -77,6 +97,11 @@ public class DhChunkPos
return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2));
}
+
+ //================//
+ // base overrides //
+ //================//
+
@Override
public boolean equals(Object obj)
{
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java
index c37fac961..e55072dbd 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.core.pos;
+import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
@@ -101,21 +102,32 @@ public class DhSectionPos
return data;
}
- public static long encode(DhBlockPos pos) { return encodeBlockPos(pos.x, pos.z); }
- public static long encode(DhBlockPos2D pos) { return encodeBlockPos(pos.x, pos.z); }
- public static long encodeBlockPos(int blockX, int blockZ)
+ /** Returns the section pos at the requested detail level containing the given BlockPos */
+ public static long encodeContaining(byte outputSectionDetailLevel, DhBlockPos pos)
{
- long pos = encode(LodUtil.BLOCK_DETAIL_LEVEL, blockX, blockZ);
- pos = convertToDetailLevel(pos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL);
- return pos;
+ int sectionPosX = getXOrZSectionPosFromChunkOrBlockPos(pos.x, false);
+ int sectionPosZ = getXOrZSectionPosFromChunkOrBlockPos(pos.z, false);
+ long blockPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
+ return convertToDetailLevel(blockPos, outputSectionDetailLevel);
}
-
- public static long encode(DhChunkPos pos) { return encodeChunkPos(pos.x, pos.z); }
- public static long encodeChunkPos(int chunkX, int chunkZ)
+ /** Returns the section pos at the requested detail level containing the given ChunkPos */
+ public static long encodeContaining(byte outputSectionDetailLevel, DhChunkPos pos)
{
- long pos = encode(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ);
- pos = convertToDetailLevel(pos, DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL);
- return pos;
+ int sectionPosX = getXOrZSectionPosFromChunkOrBlockPos(pos.x, true);
+ int sectionPosZ = getXOrZSectionPosFromChunkOrBlockPos(pos.z, true);
+ long blockPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ);
+ return convertToDetailLevel(blockPos, outputSectionDetailLevel);
+ }
+ private static int getXOrZSectionPosFromChunkOrBlockPos(int chunkXOrZPos, boolean isChunkPos)
+ {
+ int sectionPos = chunkXOrZPos;
+ int fullDataSourceWidth = isChunkPos ? FullDataSourceV2.NUMB_OF_CHUNKS_WIDE : (FullDataSourceV2.NUMB_OF_CHUNKS_WIDE * LodUtil.CHUNK_WIDTH);
+
+ // negative positions start at -1 so the logic there is slightly different
+ sectionPos = (sectionPos < 0)
+ ? ((sectionPos + 1) / fullDataSourceWidth) - 1
+ : (sectionPos / fullDataSourceWidth);
+ return sectionPos;
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java b/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java
index cf18751b8..0ef0b5f56 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java
@@ -1,8 +1,9 @@
package com.seibel.distanthorizons.core.render;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum;
+import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.joml.FrustumIntersection;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
@@ -35,12 +36,12 @@ public class DhFrustumBounds implements IDhApiCullingFrustum
//=========//
@Override
- public void update(int worldMinBlockY, int worldMaxBlockY, Mat4f dhWorldViewProjection)
+ public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection)
{
this.worldMinY = worldMinBlockY;
this.worldMaxY = worldMaxBlockY;
- Matrix4f worldViewProjection = new Matrix4f(dhWorldViewProjection.createJomlMatrix());
+ Matrix4f worldViewProjection = new Matrix4f(Mat4f.createJomlMatrix(dhWorldViewProjection));
this.frustum.set(worldViewProjection);
Matrix4fc matWorldViewProjectionInv = new Matrix4f(worldViewProjection).invert();
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java
index 3c819d544..0b24aa5c5 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java
@@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
+import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
@@ -329,6 +330,10 @@ public class LodQuadTree extends QuadTree implements IDebugRen
}
// all child positions are loaded, disable this section and enable its children.
+ if (renderSection.renderingEnabled)
+ {
+ this.level.unloadBeaconBeamsInPos(renderSection.pos);
+ }
renderSection.renderingEnabled = false;
// walk back down the tree and enable the child sections //TODO there are probably more efficient ways of doing this, but this will work for now
@@ -383,6 +388,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen
if (!renderSection.renderingEnabled)
{
renderSection.renderingEnabled = true;
+ this.level.loadBeaconBeamsInPos(renderSection.pos);
// delete/disable children, all of them will be a lower detail level than requested
quadNode.deleteAllChildren((childRenderSection) ->
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
index fbfd9a2ed..bacd2eb7b 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java
@@ -292,7 +292,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
catch (Exception e)
{
- LOGGER.warn("Unable to get render source " + this.pos + ", error: " + e.getMessage(), e);
+ LOGGER.warn("Unable to get render source " + DhSectionPos.toString(this.pos) + ", error: " + e.getMessage(), e);
this.renderSourceLoadingRefFuture = null;
return null;
}
@@ -416,6 +416,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}
+ this.level.unloadBeaconBeamsInPos(this.pos);
if (this.renderBuffer != null)
{
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java b/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java
index 7e10b0b44..611aa0ad6 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java
@@ -2,8 +2,9 @@ package com.seibel.distanthorizons.core.render;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShadowCullingFrustum;
+import com.seibel.distanthorizons.api.objects.math.DhApiMat4f;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
/**
* Dummy {@link IDhApiCullingFrustum} that allows everything through.
@@ -24,7 +25,7 @@ public class NeverCullFrustum implements IDhApiCullingFrustum, IDhApiShadowCulli
//=========//
@Override
- public void update(int worldMinBlockY, int worldMaxBlockY, Mat4f dhWorldViewProjection) { /* update isn't needed */ }
+ public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection) { /* update isn't needed */ }
@Override
public boolean intersects(int lodBlockPosMinX, int lodBlockPosMinZ, int lodBlockWidth, int lodDetailLevel) { return true; }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java
index f36280ae8..d02a238fb 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java
@@ -41,9 +41,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
import org.apache.logging.log4j.Logger;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
@@ -100,9 +100,6 @@ public class RenderBufferHandler implements AutoCloseable
{
DhApi.overrides.bind(IDhApiShadowCullingFrustum.class, new NeverCullFrustum());
}
-
-
- F3Screen.setRenderBufferHandler(this);
}
@@ -247,7 +244,7 @@ public class RenderBufferHandler implements AutoCloseable
if (enableFrustumCulling)
{
int worldMinY = clientLevelWrapper.getMinHeight();
- int worldHeight = clientLevelWrapper.getHeight();
+ int worldHeight = clientLevelWrapper.getMaxHeight();
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
@@ -417,8 +414,6 @@ public class RenderBufferHandler implements AutoCloseable
renderSection.close();
}
}
-
- F3Screen.setRenderBufferHandler(null);
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java
index be4adf73e..8e0873b93 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java
@@ -173,7 +173,7 @@ public class LodFogConfig
str.append("" +
"float getNearFogThickness(float dist) \n" +
"{ \n" +
- " return linearFog(dist, nearFogStart, nearFogLength, 0.0, 1.0); \n" +
+ " return linearFog(dist, uNearFogStart, uNearFogLength, 0.0, 1.0); \n" +
"} \n");
@@ -182,7 +182,7 @@ public class LodFogConfig
str.append("\n" +
"float getFarFogThickness(float dist) { return 0.0; } \n" +
"float getHeightFogThickness(float dist) { return 0.0; } \n" +
- "float calculateFarFogDepth(float horizontal, float dist, float nearFogStart) { return 0.0; } \n" +
+ "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) { return 0.0; } \n" +
"float calculateHeightFogDepth(float vertical, float realY) { return 0.0; } \n" +
"float mixFogThickness(float near, float far, float height) \n" +
"{ \n" +
@@ -215,13 +215,13 @@ public class LodFogConfig
"} \n");
- // Generate method: calculateFarFogDepth(float horizontal, float dist, float nearFogStart);
+ // Generate method: calculateFarFogDepth(float horizontal, float dist, float uNearFogStart);
str.append("" +
- "float calculateFarFogDepth(float horizontal, float dist, float nearFogStart) \n" +
+ "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) \n" +
"{ \n" +
" return " + (heightFogMixMode == EDhApiHeightFogMixMode.BASIC ?
- "(dist - nearFogStart)/(1.0 - nearFogStart);" :
- "(horizontal - nearFogStart)/(1.0 - nearFogStart);") +
+ "(dist - uNearFogStart)/(1.0 - uNearFogStart);" :
+ "(horizontal - uNearFogStart)/(1.0 - uNearFogStart);") +
"} \n");
// Generate method: float mixFogThickness(float near, float far, float height);
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java
index ffe831b00..4bbfbd6e8 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java
@@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.objects.GLMessage;
import com.seibel.distanthorizons.core.util.objects.GLMessageOutputStream;
-import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.apache.logging.log4j.LogManager;
@@ -40,8 +39,6 @@ import org.lwjgl.opengl.GLUtil;
import java.io.PrintStream;
import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.concurrent.ConcurrentLinkedQueue;
/**
@@ -76,7 +73,9 @@ public class GLProxy
public boolean namedObjectSupported = false; // ~OpenGL 4.5 (UNUSED CURRENTLY)
public boolean bufferStorageSupported = false; // ~OpenGL 4.4
- public boolean VertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3
+ public boolean vertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3
+ public boolean instancedArraysSupported = false;
+ public boolean vertexAttribDivisorSupported = false; // OpenGL 3.3 or newer
private final EDhApiGpuUploadMethod preferredUploadMethod;
@@ -91,17 +90,15 @@ public class GLProxy
private GLProxy() throws IllegalStateException
{
// this must be created on minecraft's render context to work correctly
-
- GL_LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error.");
- GL_LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "].");
-
- // getting Minecraft's context has to be done on the render thread,
- // where the GL context is
if (GLFW.glfwGetCurrentContext() == 0L)
{
throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!");
}
+ GL_LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error.");
+ GL_LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "].");
+
+
//============================//
@@ -135,21 +132,26 @@ public class GLProxy
// get GPU capabilities //
//======================//
- // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after
- this.VertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr
-
// UNUSED currently
// Check if we can use the named version of all calls, which is available in GL4.5 or after
this.namedObjectSupported = this.glCapabilities.glNamedBufferData != 0L; //Nullptr
- // get specific capabilities
// Check if we can use the Buffer Storage, which is available in GL4.4 or after
this.bufferStorageSupported = this.glCapabilities.glBufferStorage != 0L; // Nullptr
if (!this.bufferStorageSupported)
{
- GL_LOGGER.warn("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods.");
+ GL_LOGGER.info("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods.");
}
+ // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after
+ this.vertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr
+
+ // used by instanced rendering
+ this.vertexAttribDivisorSupported = this.glCapabilities.OpenGL33;
+ // denotes if ARBInstancedArrays.glVertexAttribDivisorARB() is available or not
+ // can be used as a backup if MC didn't create a GL 3.3+ context
+ this.instancedArraysSupported = this.glCapabilities.GL_ARB_instanced_arrays;
+
// get the best automatic upload method
String vendor = GL32.glGetString(GL32.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION"
if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE"))
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java
index 7d8608a83..bdfee1f4b 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.core.render.glObject.buffer;
import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
+import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.util.LodUtil;
@@ -140,7 +141,10 @@ public class GLBuffer implements AutoCloseable
GL32.glDeleteBuffers(id);
bufferCount.decrementAndGet();
- //LOGGER.info("destroyed buffer ["+id+"], remaining: ["+BUFFER_ID_TO_PHANTOM.size()+"]");
+ if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get())
+ {
+ LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]");
+ }
}
});
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java
index 8ade515eb..fc821a461 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java
@@ -26,12 +26,13 @@ import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3i;
import org.lwjgl.opengl.GL32;
import org.lwjgl.system.MemoryStack;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
/**
@@ -154,7 +155,7 @@ public class ShaderProgram
* @return Location of the Uniform
* @throws RuntimeException if uniform not found
*/
- public int getUniformLocation(CharSequence name)
+ public int getUniformLocation(CharSequence name) throws RuntimeException
{
int i = GL32.glGetUniformLocation(id, name);
if (i == -1)
@@ -195,11 +196,10 @@ public class ShaderProgram
{
GL32.glUniform3f(location, value.x, value.y, value.z);
}
-
/** Requires ShaderProgram binded. */
- public void setUniform(int location, Vec3d value)
+ public void setUniform(int location, DhApiVec3i value)
{
- GL32.glUniform3f(location, (float) value.x, (float) value.y, (float) value.z);
+ GL32.glUniform3i(location, value.x, value.y, value.z);
}
/** Requires ShaderProgram binded. */
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java
index 3c7267c71..3f27d12b6 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java
@@ -48,7 +48,7 @@ public abstract class AbstractVertexAttribute
public static AbstractVertexAttribute create()
{
- if (GLProxy.getInstance().VertexAttributeBufferBindingSupported)
+ if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
{
return new VertexAttributePostGL43();
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java
index 42d893b9d..02243d36f 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java
@@ -36,9 +36,9 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute;
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
import org.apache.logging.log4j.LogManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -64,13 +64,13 @@ public class DebugRenderer
// rendering setup
private ShaderProgram basicShader;
- private GLVertexBuffer boxBuffer;
- private GLElementBuffer boxOutlineBuffer;
+ private GLVertexBuffer vertexBuffer;
+ private GLElementBuffer outlineIndexBuffer;
private AbstractVertexAttribute va;
private boolean init = false;
// used when rendering
- private Mat4f transformThiFrame;
+ private Mat4f transformationMatrixThisFrame;
private Vec3f camPosFloatThisFrame;
@@ -79,8 +79,8 @@ public class DebugRenderer
- // A box from 0,0,0 to 1,1,1
- private static final float[] box_vertices = {
+ /** A box from 0,0,0 to 1,1,1 */
+ private static final float[] BOX_VERTICES = {
// Pos x y z
0, 0, 0,
1, 0, 0,
@@ -92,7 +92,7 @@ public class DebugRenderer
0, 1, 1,
};
- private static final int[] box_outline_indices = {
+ private static final int[] BOX_OUTLINE_INDICES = {
0, 1,
1, 2,
2, 3,
@@ -115,7 +115,47 @@ public class DebugRenderer
// constructor //
//=============//
- public DebugRenderer() { }
+ private DebugRenderer() { }
+
+ public void init()
+ {
+ if (this.init)
+ {
+ return;
+ }
+ this.init = true;
+
+ this.va = AbstractVertexAttribute.create();
+ this.va.bind();
+ // Pos
+ this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false));
+ this.va.completeAndCheck(Float.BYTES * 3);
+ this.basicShader = new ShaderProgram("shaders/debug/vert.vert", "shaders/debug/frag.frag",
+ "fragColor", new String[]{"vPosition"});
+ this.createBuffer();
+ }
+
+ private void createBuffer()
+ {
+ // box vertices
+ ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES);
+ boxVerticesBuffer.order(ByteOrder.nativeOrder());
+ boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES);
+ boxVerticesBuffer.rewind();
+ this.vertexBuffer = new GLVertexBuffer(false);
+ this.vertexBuffer.bind();
+ this.vertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES);
+
+
+ // outline vertex indexes
+ ByteBuffer boxOutlineBuffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES);
+ boxOutlineBuffer.order(ByteOrder.nativeOrder());
+ boxOutlineBuffer.asIntBuffer().put(BOX_OUTLINE_INDICES);
+ boxOutlineBuffer.rewind();
+ this.outlineIndexBuffer = new GLElementBuffer(false);
+ this.outlineIndexBuffer.uploadBuffer(boxOutlineBuffer, EDhApiGpuUploadMethod.DATA, BOX_OUTLINE_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
+
+ }
@@ -141,53 +181,13 @@ public class DebugRenderer
-
-
//===========//
// rendering //
//===========//
- public void init()
- {
- if (this.init)
- {
- return;
- }
-
- this.init = true;
- this.va = AbstractVertexAttribute.create();
- this.va.bind();
- // Pos
- this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false));
- this.va.completeAndCheck(Float.BYTES * 3);
- this.basicShader = new ShaderProgram("shaders/debug/vert.vert", "shaders/debug/frag.frag",
- "fragColor", new String[]{"vPosition"});
- this.createBuffer();
- }
-
- private void createBuffer()
- {
- ByteBuffer buffer = ByteBuffer.allocateDirect(box_vertices.length * Float.BYTES);
- buffer.order(ByteOrder.nativeOrder());
- buffer.asFloatBuffer().put(box_vertices);
- buffer.rewind();
-
- this.boxBuffer = new GLVertexBuffer(false);
- this.boxBuffer.bind();
- this.boxBuffer.uploadBuffer(buffer, 8, EDhApiGpuUploadMethod.DATA, box_vertices.length * Float.BYTES);
-
- buffer = ByteBuffer.allocateDirect(box_outline_indices.length * Integer.BYTES);
- buffer.order(ByteOrder.nativeOrder());
- buffer.asIntBuffer().put(box_outline_indices);
- buffer.rewind();
-
- this.boxOutlineBuffer = new GLElementBuffer(false);
- this.boxOutlineBuffer.uploadBuffer(buffer, EDhApiGpuUploadMethod.DATA, box_outline_indices.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
- }
-
public void render(Mat4f transform)
{
- this.transformThiFrame = transform;
+ this.transformationMatrixThisFrame = transform;
Vec3d camPos = MC_RENDER.getCameraExactPosition();
this.camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z);
@@ -199,22 +199,23 @@ public class DebugRenderer
this.basicShader.bind();
this.va.bind();
- this.va.bindBufferToAllBindingPoints(this.boxBuffer.getId());
+ this.va.bindBufferToAllBindingPoints(this.vertexBuffer.getId());
- this.boxOutlineBuffer.bind();
+ this.outlineIndexBuffer.bind();
this.rendererLists.render(this);
BoxParticle head = null;
while ((head = this.particles.poll()) != null && head.isDead(System.nanoTime()))
- {
- }
+ { /* remove dead particles */ }
if (head != null)
{
+ // re-add the popped off head
this.particles.add(head);
}
+ GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
for (BoxParticle particle : this.particles)
{
this.renderBox(particle.getBox());
@@ -226,13 +227,13 @@ public class DebugRenderer
public void renderBox(Box box)
{
- Mat4f boxTransform = Mat4f.createTranslateMatrix(box.a.x - this.camPosFloatThisFrame.x, box.a.y - this.camPosFloatThisFrame.y, box.a.z - this.camPosFloatThisFrame.z);
- boxTransform.multiply(Mat4f.createScaleMatrix(box.b.x - box.a.x, box.b.y - box.a.y, box.b.z - box.a.z));
- Mat4f t = this.transformThiFrame.copy();
+ Mat4f boxTransform = Mat4f.createTranslateMatrix(box.minPos.x - this.camPosFloatThisFrame.x, box.minPos.y - this.camPosFloatThisFrame.y, box.minPos.z - this.camPosFloatThisFrame.z);
+ boxTransform.multiply(Mat4f.createScaleMatrix(box.maxPos.x - box.minPos.x, box.maxPos.y - box.minPos.y, box.maxPos.z - box.minPos.z));
+ Mat4f t = this.transformationMatrixThisFrame.copy();
t.multiply(boxTransform);
- this.basicShader.setUniform(this.basicShader.getUniformLocation("transform"), t);
+ this.basicShader.setUniform(this.basicShader.getUniformLocation("uTransform"), t);
this.basicShader.setUniform(this.basicShader.getUniformLocation("uColor"), box.color);
- GL32.glDrawElements(GL32.GL_LINES, box_outline_indices.length, GL32.GL_UNSIGNED_INT, 0);
+ GL32.glDrawElements(GL32.GL_LINES, BOX_OUTLINE_INDICES.length, GL32.GL_UNSIGNED_INT, 0);
}
@@ -243,25 +244,25 @@ public class DebugRenderer
public static final class Box
{
- public Vec3f a;
- public Vec3f b;
+ public Vec3f minPos;
+ public Vec3f maxPos;
public Color color;
- public Box(Vec3f a, Vec3f b, Color color)
+ public Box(Vec3f minPos, Vec3f maxPos, Color color)
{
- this.a = a;
- this.b = b;
+ this.minPos = minPos;
+ this.maxPos = maxPos;
this.color = color;
}
- public Box(Vec3f a, Vec3f b, Color color, Vec3f margin)
+ public Box(Vec3f minPos, Vec3f maxPos, Color color, Vec3f margin)
{
- this.a = a;
- this.a.add(margin);
- this.b = b;
- this.b.subtract(margin);
+ this.minPos = minPos;
+ this.minPos.add(margin);
+ this.maxPos = maxPos;
+ this.maxPos.subtract(margin);
this.color = color;
}
@@ -272,8 +273,8 @@ public class DebugRenderer
float edge = pos.getBlockWidth() * marginPercent;
Vec3f a = new Vec3f(blockMin.x + edge, minY, blockMin.z + edge);
Vec3f b = new Vec3f(blockMax.x - edge, maxY, blockMax.z - edge);
- this.a = a;
- this.b = b;
+ this.minPos = a;
+ this.maxPos = b;
this.color = color;
}
@@ -285,8 +286,8 @@ public class DebugRenderer
float edge = pos.getBlockWidth() * marginPercent;
Vec3f a = new Vec3f(blockMin.x + edge, hashY, blockMin.z + edge);
Vec3f b = new Vec3f(blockMax.x - edge, hashY, blockMax.z - edge);
- this.a = a;
- this.b = b;
+ this.minPos = a;
+ this.maxPos = b;
this.color = color;
}
@@ -334,7 +335,7 @@ public class DebugRenderer
float percent = (now - this.startTime) / (float) this.duration;
percent = (float) Math.pow(percent, 4);
float yDiff = this.yChange * percent;
- return new Box(new Vec3f(this.box.a.x, this.box.a.y + yDiff, this.box.a.z), new Vec3f(this.box.b.x, this.box.b.y + yDiff, this.box.b.z), this.box.color);
+ return new Box(new Vec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z), new Vec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z), this.box.color);
}
public boolean isDead(long time) { return (time - this.startTime) > this.duration; }
@@ -350,7 +351,7 @@ public class DebugRenderer
public BoxWithLife(Box box, long ns, float yChange, Color deathColor)
{
this.box = box;
- this.particaleOnClose = new BoxParticle(new Box(box.a, box.b, deathColor), -1, ns, yChange);
+ this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), -1, ns, yChange);
register(this, null);
}
@@ -360,7 +361,7 @@ public class DebugRenderer
public BoxWithLife(Box box, double s, float yChange, Color deathColor)
{
this.box = box;
- this.particaleOnClose = new BoxParticle(new Box(box.a, box.b, deathColor), s, yChange);
+ this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), s, yChange);
}
public BoxWithLife(Box box, double s, float yChange) { this(box, s, yChange, box.color); }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java
similarity index 52%
rename from core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java
rename to core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java
index 5f95bb689..058b6b63e 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.glObject.shader.Shader;
@@ -31,39 +32,38 @@ import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexAtt
import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderUtil;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
-public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgram
+/**
+ * Handles rendering the normal LOD terrain.
+ */
+public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShaderProgram
{
- public static final String VERTEX_SHADER_PATH = "shaders/standard.vert";
- public static final String VERTEX_CURVE_SHADER_PATH = "shaders/curve.vert";
- public static final String FRAGMENT_SHADER_PATH = "shaders/flat_shaded.frag";
-
public final AbstractVertexAttribute vao;
// Uniforms
- public final int combinedMatUniform;
- public final int modelOffsetUniform;
- public final int worldYOffsetUniform;
+ public final int uCombinedMatrix;
+ public final int uModelOffset;
+ public final int uWorldYOffset;
- public final int mircoOffsetUniform;
+ public final int uMircoOffset;
- public final int earthRadiusUniform;
+ public final int uEarthRadius;
- public final int lightMapUniform;
+ public final int uLightMap;
// Fog/Clip Uniforms
- public final int clipDistanceUniform;
+ public final int uClipDistance;
// Noise Uniforms
- public final int noiseEnabledUniform;
- public final int noiseStepsUniform;
- public final int noiseIntensityUniform;
- public final int noiseDropoffUniform;
+ public final int uNoiseEnabled;
+ public final int uNoiseSteps;
+ public final int uNoiseIntensity;
+ public final int uNoiseDropoff;
// Debug Uniform
- public final int whiteWorldUniform;
+ public final int uWhiteWorld;
@@ -72,50 +72,57 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr
//=============//
// This will bind AbstractVertexAttribute
- public LodRenderProgram()
+ public DhTerrainShaderProgram()
{
- super(() -> Shader.loadFile(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get() != 0 ? VERTEX_CURVE_SHADER_PATH : VERTEX_SHADER_PATH,
+ super(
+ () -> Shader.loadFile(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get() != 0
+ ? "shaders/curve.vert"
+ : "shaders/standard.vert",
false, new StringBuilder()).toString(),
- () -> Shader.loadFile(FRAGMENT_SHADER_PATH, false, new StringBuilder()).toString(),
+ () -> Shader.loadFile("shaders/flat_shaded.frag", false, new StringBuilder()).toString(),
"fragColor", new String[]{"vPosition", "color"});
- combinedMatUniform = getUniformLocation("combinedMatrix");
- modelOffsetUniform = getUniformLocation("modelOffset");
- worldYOffsetUniform = tryGetUniformLocation("worldYOffset");
- mircoOffsetUniform = getUniformLocation("mircoOffset");
- earthRadiusUniform = tryGetUniformLocation("earthRadius");
+ this.uCombinedMatrix = this.getUniformLocation("uCombinedMatrix");
+ this.uModelOffset = this.getUniformLocation("uModelOffset");
+ this.uWorldYOffset = this.tryGetUniformLocation("uWorldYOffset");
+ this.uMircoOffset = this.getUniformLocation("uMircoOffset");
+ this.uEarthRadius = this.tryGetUniformLocation("uEarthRadius");
- lightMapUniform = getUniformLocation("lightMap");
+ this.uLightMap = this.getUniformLocation("uLightMap");
// Fog/Clip Uniforms
- clipDistanceUniform = getUniformLocation("clipDistance");
+ this.uClipDistance = this.getUniformLocation("uClipDistance");
// Noise Uniforms
- noiseEnabledUniform = getUniformLocation("noiseEnabled");
- noiseStepsUniform = getUniformLocation("noiseSteps");
- noiseIntensityUniform = getUniformLocation("noiseIntensity");
- noiseDropoffUniform = getUniformLocation("noiseDropoff");
+ this.uNoiseEnabled = this.getUniformLocation("uNoiseEnabled");
+ this.uNoiseSteps = this.getUniformLocation("uNoiseSteps");
+ this.uNoiseIntensity = this.getUniformLocation("uNoiseIntensity");
+ this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff");
// Debug Uniform
- whiteWorldUniform = getUniformLocation("whiteWorld");
+ this.uWhiteWorld = this.getUniformLocation("uWhiteWorld");
// TODO: Add better use of the LODFormat thing
int vertexByteCount = LodUtil.LOD_VERTEX_FORMAT.getByteSize();
- if (GLProxy.getInstance().VertexAttributeBufferBindingSupported)
- vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute
+ if (GLProxy.getInstance().vertexAttributeBufferBindingSupported)
+ {
+ this.vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute
+ }
else
- vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute
- vao.bind();
+ {
+ this.vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute
+ }
+ this.vao.bind();
// TODO comment what each attribute represents
- vao.setVertexAttribute(0, 0, VertexPointer.addUnsignedShortsPointer(4, false, true)); // 2+2+2+2 // TODO probably color, blockpos
- vao.setVertexAttribute(0, 1, VertexPointer.addUnsignedBytesPointer(4, true, false)); // +4 // TODO ?
- vao.setVertexAttribute(0, 2, VertexPointer.addUnsignedBytesPointer(4, true, true)); // +4 // TODO probably normal index and Iris block ID
+ this.vao.setVertexAttribute(0, 0, VertexPointer.addUnsignedShortsPointer(4, false, true)); // 2+2+2+2 // TODO probably color, blockpos
+ this.vao.setVertexAttribute(0, 1, VertexPointer.addUnsignedBytesPointer(4, true, false)); // +4 // TODO ?
+ this.vao.setVertexAttribute(0, 2, VertexPointer.addUnsignedBytesPointer(4, true, true)); // +4 // TODO probably normal index and Iris block ID
try
{
- vao.completeAndCheck(vertexByteCount);
+ this.vao.completeAndCheck(vertexByteCount);
}
catch (RuntimeException e)
{
@@ -123,15 +130,15 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr
throw e;
}
- if (earthRadiusUniform != -1) setUniform(earthRadiusUniform,
+ if (this.uEarthRadius != -1) this.setUniform(this.uEarthRadius,
/*6371KM*/ 6371000.0f / Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get());
// Noise Uniforms
- setUniform(noiseEnabledUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get());
- setUniform(noiseStepsUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get());
- setUniform(noiseIntensityUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue());
- setUniform(noiseDropoffUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get());
+ this.setUniform(this.uNoiseEnabled, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get());
+ this.setUniform(this.uNoiseSteps, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get());
+ this.setUniform(this.uNoiseIntensity, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue());
+ this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get());
}
@@ -144,27 +151,25 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr
public void bind()
{
super.bind();
- vao.bind();
+ this.vao.bind();
}
@Override
public void unbind()
{
super.unbind();
- vao.unbind();
+ this.vao.unbind();
}
@Override
public void free()
{
- vao.free();
+ this.vao.free();
super.free();
}
@Override
public void bindVertexBuffer(int vbo) { this.vao.bindBufferToAllBindingPoints(vbo); }
- public void unbindVertexBuffer() { this.vao.unbindBuffersFromAllBindingPoint(); }
-
@Override
public void fillUniformData(DhApiRenderParam renderParameters)
{
@@ -174,16 +179,16 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr
super.bind();
// uniforms
- setUniform(combinedMatUniform, combinedMatrix);
- setUniform(mircoOffsetUniform, 0.01f); // 0.01 block offset
+ this.setUniform(this.uCombinedMatrix, combinedMatrix);
+ this.setUniform(this.uMircoOffset, 0.01f); // 0.01 block offset
// setUniform(skyLightUniform, skyLight);
- setUniform(lightMapUniform, 0); // TODO this should probably be passed in
+ this.setUniform(this.uLightMap, 0); // TODO this should probably be passed in
- if (worldYOffsetUniform != -1) setUniform(worldYOffsetUniform, (float) renderParameters.worldYOffset);
+ if (this.uWorldYOffset != -1) this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset);
// Debug
- setUniform(whiteWorldUniform, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
+ this.setUniform(this.uWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
// Clip Uniform
float dhNearClipDistance = RenderUtil.getNearClipPlaneDistanceInBlocks(renderParameters.partialTicks);
@@ -197,11 +202,11 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr
{
dhNearClipDistance = 1.0f;
}
- this.setUniform(this.clipDistanceUniform, dhNearClipDistance);
+ this.setUniform(this.uClipDistance, dhNearClipDistance);
}
@Override
- public void setModelOffsetPos(Vec3f modelOffsetPos) { this.setUniform(this.modelOffsetUniform, modelOffsetPos); }
+ public void setModelOffsetPos(DhApiVec3f modelOffsetPos) { this.setUniform(this.uModelOffset, new Vec3f(modelOffsetPos)); }
@Override
public int getId() { return this.id; }
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java
new file mode 100644
index 000000000..1f2f17491
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java
@@ -0,0 +1,135 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.core.render.renderer;
+
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.render.glObject.GLState;
+import com.seibel.distanthorizons.core.render.renderer.shaders.FogApplyShader;
+import com.seibel.distanthorizons.core.render.renderer.shaders.FogShader;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import org.lwjgl.opengl.GL32;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Handles adding SSAO via {@link FogShader} and {@link FogApplyShader}.
+ *
+ * {@link FogShader} - draws the Fog to a texture.
+ * {@link FogApplyShader} - draws the Fog texture to DH's FrameBuffer.
+ */
+public class FogRenderer
+{
+ public static FogRenderer INSTANCE = new FogRenderer();
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+
+
+ private boolean init = false;
+
+ private int width = -1;
+ private int height = -1;
+ private int fogFramebuffer = -1;
+
+ private int fogTexture = -1;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ private FogRenderer() { }
+
+ public void init()
+ {
+ if (this.init) return;
+ this.init = true;
+
+ FogShader.INSTANCE.init();
+ FogApplyShader.INSTANCE.init();
+ }
+
+ private void createFramebuffer(int width, int height)
+ {
+ if (this.fogFramebuffer != -1)
+ {
+ GL32.glDeleteFramebuffers(this.fogFramebuffer);
+ this.fogFramebuffer = -1;
+ }
+
+ if (this.fogTexture != -1)
+ {
+ GL32.glDeleteTextures(this.fogTexture);
+ this.fogTexture = -1;
+ }
+
+ this.fogFramebuffer = GL32.glGenFramebuffers();
+ GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer);
+
+ this.fogTexture = GL32.glGenTextures();
+ GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.fogTexture);
+ GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
+ GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
+ GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0);
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ public void render(GLState primaryState, Mat4f projectionMatrix, float partialTicks)
+ {
+ GLState state = new GLState();
+ this.init();
+
+ // resize the framebuffer if necessary
+ int width = MC_RENDER.getTargetFrameBufferViewportWidth();
+ int height = MC_RENDER.getTargetFrameBufferViewportHeight();
+ if (this.width != width || this.height != height)
+ {
+ this.width = width;
+ this.height = height;
+ this.createFramebuffer(width, height);
+ }
+
+ FogShader.INSTANCE.frameBuffer = this.fogFramebuffer;
+ FogShader.INSTANCE.setProjectionMatrix(projectionMatrix);
+ FogShader.INSTANCE.render(partialTicks);
+
+ // restored so we can write the SSAO texture to the main frame buffer
+ primaryState.restore();
+
+ FogApplyShader.INSTANCE.fogTexture = this.fogTexture;
+ FogApplyShader.INSTANCE.render(partialTicks);
+
+ state.restore();
+ }
+
+ public void free()
+ {
+ FogShader.INSTANCE.free();
+ FogApplyShader.INSTANCE.free();
+ }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java
index 6ea356fd8..2a6e2e688 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java
@@ -38,6 +38,7 @@ import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer;
import com.seibel.distanthorizons.core.render.glObject.texture.*;
+import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
import com.seibel.distanthorizons.core.render.renderer.shaders.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
@@ -50,16 +51,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccess
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3d;
-import com.seibel.distanthorizons.coreapi.util.math.Vec3f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
import org.apache.logging.log4j.LogManager;
import org.lwjgl.opengl.GL32;
import java.awt.*;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -97,90 +95,14 @@ public class LodRenderer
private int cachedWidth;
private int cachedHeight;
-
- /** called by each {@link ColumnRenderBuffer} before rendering */
- public void setModelViewMatrixOffset(DhBlockPos pos, DhApiRenderParam renderEventParam) throws IllegalStateException
- {
- Vec3d cam = MC_RENDER.getCameraExactPosition();
- Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z));
-
-
- IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
- IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
- if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
- {
- shaderProgram = shaderProgramOverride;
- }
-
- if (!GL32.glIsProgram(shaderProgram.getId()))
- {
- throw new IllegalStateException("No GL program exists with the ID: [" + shaderProgram.getId() + "]. This either means a shader program was freed while it was still in use or was never created.");
- }
-
- shaderProgram.bind();
- shaderProgram.setModelOffsetPos(modelPos);
-
- ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos));
- }
-
- public void drawVbo(GLVertexBuffer vbo)
- {
- //// can be uncommented to add additional debug validation to prevent crashes if invalid buffers are being created
- //// shouldn't be used in production due to the performance hit
- //if (GL32.glIsBuffer(vbo.getId()))
- {
- IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
- IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
- if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
- {
- shaderProgram = shaderProgramOverride;
- }
-
-
- vbo.bind();
- shaderProgram.bindVertexBuffer(vbo.getId());
- GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent?
- this.quadIBO.getType(), 0);
- vbo.unbind();
- }
- //else
- //{
- // // will spam the log if uncommented, but helpful for validation
- // //LOGGER.warn("Unable to draw VBO: "+vbo.getId());
- //}
- }
-
-
- public static class LagSpikeCatcher
- {
- long timer = System.nanoTime();
-
- public LagSpikeCatcher() { }
-
- public void end(String source)
- {
- if (!ENABLE_DRAW_LAG_SPIKE_LOGGING)
- {
- return;
- }
-
- this.timer = System.nanoTime() - this.timer;
- if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS)
- {
- //4 ms
- EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!");
- }
-
- }
-
- }
-
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private final ReentrantLock setupLock = new ReentrantLock();
public final RenderBufferHandler bufferHandler;
+ public final GenericObjectRenderer genericObjectRenderer;
+
// The shader program
IDhApiShaderProgram lodRenderProgram = null;
@@ -205,9 +127,10 @@ public class LodRenderer
// constructor //
//=============//
- public LodRenderer(RenderBufferHandler bufferHandler)
+ public LodRenderer(RenderBufferHandler bufferHandler, GenericObjectRenderer genericObjectRenderer)
{
this.bufferHandler = bufferHandler;
+ this.genericObjectRenderer = genericObjectRenderer;
}
private boolean rendererClosed = false;
@@ -374,16 +297,41 @@ public class LodRenderer
// Disable blending for opaque rendering
GL32.glDisable(GL32.GL_BLEND);
+
+ // terrain
profiler.popPush("LOD Opaque");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam);
-
- // TODO: Directional culling
this.bufferHandler.renderOpaque(this, renderEventParam);
+ // custom objects with SSAO
+ if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get())
+ {
+ profiler.popPush("Custom Objects");
+ this.genericObjectRenderer.render(renderEventParam, profiler, true);
+ }
+
+
+ // SSAO
if (Config.Client.Advanced.Graphics.Ssao.enabled.get())
{
profiler.popPush("LOD SSAO");
- SSAORenderer.INSTANCE.render(minecraftGlState, renderEventParam.dhProjectionMatrix, renderEventParam.partialTicks);
+ SSAORenderer.INSTANCE.render(minecraftGlState, new Mat4f(renderEventParam.dhProjectionMatrix), renderEventParam.partialTicks);
+ }
+
+
+ // custom objects without SSAO
+ if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get())
+ {
+ profiler.popPush("Custom Objects");
+ this.genericObjectRenderer.render(renderEventParam, profiler, false);
+ }
+
+
+ //DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker
+
+ if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
+ {
+ this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks);
}
@@ -394,16 +342,9 @@ public class LodRenderer
Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
- FogShader.INSTANCE.setModelViewProjectionMatrix(combinedMatrix);
- FogShader.INSTANCE.render(renderEventParam.partialTicks);
+ FogRenderer.INSTANCE.render(minecraftGlState, combinedMatrix, renderEventParam.partialTicks);
}
- //DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker
-
- if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
- {
- this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks);
- }
drawLagSpikeCatcher.end("LodDraw");
@@ -422,9 +363,10 @@ public class LodRenderer
// Note: this can be very slow if a lot of boxes are being rendered
DebugRenderer.INSTANCE.render(combinedMatrix);
- profiler.popPush("LOD cleanup");
}
+ profiler.popPush("LOD cleanup");
+
if (this.usingMcFrameBuffer)
@@ -462,6 +404,17 @@ public class LodRenderer
if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled)
{
this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks);
+
+
+ if (Config.Client.Advanced.Graphics.Fog.drawMode.get() != EDhApiFogDrawMode.FOG_DISABLED)
+ {
+ profiler.popPush("LOD Fog");
+
+ Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix);
+ combinedMatrix.multiply(renderEventParam.dhModelViewMatrix);
+
+ FogRenderer.INSTANCE.render(minecraftGlState, combinedMatrix, renderEventParam.partialTicks);
+ }
}
drawLagSpikeCatcher.end("LodTranslucentDraw");
@@ -516,14 +469,56 @@ public class LodRenderer
this.bufferHandler.renderTransparent(this, renderEventParam);
GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it...
-
- if (Config.Client.Advanced.Graphics.Fog.drawMode.get() != EDhApiFogDrawMode.FOG_DISABLED)
- {
- profiler.popPush("LOD Fog");
- FogShader.INSTANCE.render(partialTicks);
- }
}
+ /** called by each {@link ColumnRenderBuffer} before rendering */
+ public void setModelViewMatrixOffset(DhBlockPos pos, DhApiRenderParam renderEventParam) throws IllegalStateException
+ {
+ Vec3d cam = MC_RENDER.getCameraExactPosition();
+ Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z));
+
+
+ IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
+ IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
+ if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
+ {
+ shaderProgram = shaderProgramOverride;
+ }
+
+ shaderProgram.bind();
+ shaderProgram.setModelOffsetPos(modelPos);
+
+ ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos));
+ }
+
+ public void drawVbo(GLVertexBuffer vbo)
+ {
+ //// can be uncommented to add additional debug validation to prevent crashes if invalid buffers are being created
+ //// shouldn't be used in production due to the performance hit
+ //if (GL32.glIsBuffer(vbo.getId()))
+ {
+ IDhApiShaderProgram shaderProgram = this.lodRenderProgram;
+ IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class);
+ if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
+ {
+ shaderProgram = shaderProgramOverride;
+ }
+
+
+ vbo.bind();
+ shaderProgram.bindVertexBuffer(vbo.getId());
+ GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent?
+ this.quadIBO.getType(), 0);
+ vbo.unbind();
+ }
+ //else
+ //{
+ // // will spam the log if uncommented, but helpful for validation
+ // //LOGGER.warn("Unable to draw VBO: "+vbo.getId());
+ //}
+ }
+
+
//=================//
@@ -616,6 +611,7 @@ public class LodRenderer
boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get();
if (renderWireframe)
{
+ // TODO fix
GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE);
//GL32.glDisable(GL32.GL_CULL_FACE);
}
@@ -650,7 +646,7 @@ public class LodRenderer
this.fogConfig = newFogConfig;
this.lodRenderProgram.free();
- this.lodRenderProgram = new LodRenderProgram();
+ this.lodRenderProgram = new DhTerrainShaderProgram();
FogShader.INSTANCE.free();
FogShader.INSTANCE = new FogShader(newFogConfig);
@@ -690,7 +686,7 @@ public class LodRenderer
EVENT_LOGGER.info("Setting up renderer");
this.isSetupComplete = true;
- this.lodRenderProgram = new LodRenderProgram();
+ this.lodRenderProgram = new DhTerrainShaderProgram();
if (ENABLE_IBO)
{
this.quadIBO = new QuadElementBuffer();
@@ -868,4 +864,35 @@ public class LodRenderer
}
}
+
+
+ //================//
+ // helper classes //
+ //================//
+
+ public static class LagSpikeCatcher
+ {
+ long timer = System.nanoTime();
+
+ public LagSpikeCatcher() { }
+
+ public void end(String source)
+ {
+ if (!ENABLE_DRAW_LAG_SPIKE_LOGGING)
+ {
+ return;
+ }
+
+ this.timer = System.nanoTime() - this.timer;
+ if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS)
+ {
+ //4 ms
+ EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!");
+ }
+
+ }
+
+ }
+
+
}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java
index 77519aa8c..b9953cd31 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.render.glObject.GLState;
import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOApplyShader;
import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOShader;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
@@ -51,6 +51,7 @@ public class SSAORenderer
private int ssaoTexture = -1;
+
//=============//
// constructor //
//=============//
@@ -92,6 +93,7 @@ public class SSAORenderer
}
+
//========//
// render //
//========//
@@ -99,12 +101,11 @@ public class SSAORenderer
public void render(GLState primaryState, Mat4f projectionMatrix, float partialTicks)
{
GLState state = new GLState();
-
this.init();
-
+
+ // resize the framebuffer if necessary
int width = MC_RENDER.getTargetFrameBufferViewportWidth();
int height = MC_RENDER.getTargetFrameBufferViewportHeight();
-
if (this.width != width || this.height != height)
{
this.width = width;
@@ -116,6 +117,7 @@ public class SSAORenderer
SSAOShader.INSTANCE.setProjectionMatrix(projectionMatrix);
SSAOShader.INSTANCE.render(partialTicks);
+ // restored so we can write the SSAO texture to the main frame buffer
primaryState.restore();
SSAOApplyShader.INSTANCE.ssaoTexture = this.ssaoTexture;
@@ -129,4 +131,5 @@ public class SSAORenderer
SSAOShader.INSTANCE.free();
SSAOApplyShader.INSTANCE.free();
}
+
}
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
new file mode 100644
index 000000000..d1beb0701
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java
@@ -0,0 +1,241 @@
+/*
+ * 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 .
+ */
+
+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.objects.math.DhApiVec3d;
+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.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 com.seibel.distanthorizons.coreapi.ModInfo;
+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);
+
+ 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;
+
+ private final IDhApiRenderableBoxGroup beaconBoxGroup;
+ private final HashMap beaconRefCountByBlockPos = new HashMap<>();
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public BeaconRenderHandler(@NotNull BeaconBeamRepo beaconBeamRepo, @NotNull GenericObjectRenderer renderer)
+ {
+ this.beaconBeamRepo = beaconBeamRepo;
+
+ this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0));
+ this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
+ 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()));
+
+ renderer.add(this.beaconBoxGroup);
+ }
+
+
+
+ //=========================//
+ // level loading/unloading //
+ //=========================//
+
+ public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList)
+ {
+ // synchronized to prevent two threads from updating the same chunk at the same time
+ synchronized (this)
+ {
+ HashSet allPosSet = new HashSet<>();
+
+ // sort new beams
+ HashMap 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 existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos);
+ HashMap 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
+ if (!existingBeam.color.equals(newBeam.color))
+ {
+ // beam colors were changed
+ this.beaconBeamRepo.save(newBeam);
+ this.updateBeaconColor(newBeam);
+ }
+ }
+ 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 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 existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos);
+ for (int i = 0; i < existingBeamList.size(); i++)
+ {
+ BeaconBeamDTO beam = existingBeamList.get(i);
+ this.stopRenderingBeaconAtPos(beam.pos);
+ }
+ }
+
+
+
+ //=================//
+ // render handling //
+ //=================//
+
+ 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 DhApiVec3d(beacon.pos.x, beacon.pos.y+1, beacon.pos.z),
+ new DhApiVec3d(beacon.pos.x+1, BEAM_TOP_Y, beacon.pos.z+1),
+ beacon.color,
+ EDhApiBlockMaterial.ILLUMINATED
+ );
+
+ 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;
+ }
+ });
+ }
+
+ 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/render/renderer/generic/CloudRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java
new file mode 100644
index 000000000..490522720
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java
@@ -0,0 +1,403 @@
+/*
+ * 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 .
+ */
+
+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.objects.math.DhApiVec3d;
+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.util.LodUtil;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import com.seibel.distanthorizons.coreapi.ModInfo;
+import org.apache.logging.log4j.Logger;
+
+import javax.imageio.ImageIO;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+public class CloudRenderHandler
+{
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+
+ private static final String CLOUD_RESOURCE_TEXTURE_PATH = "assets/distanthorizons/textures/clouds.png";
+ // FIXME transparency sorting makes having transparent clouds impossible
+ private static final Color CLOUD_COLOR = new Color(255,255,255,255);
+
+ private static final boolean DEBUG_BORDER_COLORS = false;
+
+ /**
+ * How wide an individual box is.
+ * Measured in blocks.
+ */
+ private static final int CLOUD_BOX_WIDTH = 64;
+ /** measured in blocks */
+ private static final int CLOUD_BOX_THICKNESS = 16;
+
+ 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;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public CloudRenderHandler(IDhLevel level, GenericObjectRenderer renderer)
+ {
+ this.level = level;
+ this.renderer = renderer;
+
+
+
+ //=======================//
+ // get the cloud texture //
+ //=======================//
+
+ // default to a single empty slot in case the texture is broken
+ boolean[][] cloudLocations = new boolean[1][1];
+ try
+ {
+ cloudLocations = getCloudsFromTexture();
+ }
+ catch (FileNotFoundException e)
+ {
+ LOGGER.error(e.getMessage(), e);
+ }
+ catch (IOException e)
+ {
+ LOGGER.error("Unexpected issue getting cloud texture, error: ["+e.getMessage()+"].", e);
+ }
+
+ if (cloudLocations.length != 0 &&
+ cloudLocations.length != cloudLocations[0].length)
+ {
+ LOGGER.warn("Non-square cloud texture found, some parts of the texture will be clipped off.");
+ }
+
+
+
+ //===================//
+ // parse the texture //
+ //===================//
+
+ int textureWidth = cloudLocations.length;
+ ArrayList boxList = new ArrayList<>(512);
+ for (int x = 0; x < textureWidth; x ++)
+ {
+ for (int z = 0; z < textureWidth; z ++)
+ {
+ if (cloudLocations[x][z])
+ {
+ // start a new box in Z direction
+ int startZ = z;
+ int startX = x;
+ int endZ = startZ;
+ int endX = x+1;
+
+
+
+ //==========================//
+ // merge in the Z direction //
+ //==========================//
+
+ // Find the cloud's length in the Z direction
+ while (endZ < textureWidth
+ && cloudLocations[x][endZ])
+ {
+ endZ++;
+ }
+ // update the z iterator so we can skip over everything included in this cloud
+ z = endZ - 1;
+
+
+
+ //==========================//
+ // merge in the X direction //
+ //==========================//
+
+ for (int currentX = startX + 1; currentX < textureWidth; currentX++)
+ {
+ boolean canMergeInXDir = true;
+
+ // check if all locations in this column are true
+ for (int adjacentZ = startZ; adjacentZ < endZ; adjacentZ++)
+ {
+ if (!cloudLocations[currentX][adjacentZ])
+ {
+ // at least one pixel in the texture is false,
+ // so we can't merge in this direction
+ canMergeInXDir = false;
+ break;
+ }
+ }
+
+
+ if (canMergeInXDir)
+ {
+ // mark the adjacent column as processed
+ for (int currentZ = startZ; currentZ < endZ; currentZ++)
+ {
+ // by flipping all the pixels in the adjacent column to false,
+ // we don't have to worry about adding another cloud
+ cloudLocations[currentX][currentZ] = false;
+ }
+
+ endX = (currentX + 1);
+ }
+ else
+ {
+ break;
+ }
+ }
+
+
+
+ //============================//
+ // Create the renderable box //
+ //============================//
+
+ // endZ contains the last cloud index
+ // so the cloud now goes from startZ to endZ (inclusive)
+ int minXBlockPos = startX * CLOUD_BOX_WIDTH;
+ int minZBlockPos = startZ * CLOUD_BOX_WIDTH;
+ int maxXBlockPos = endX * CLOUD_BOX_WIDTH;
+ int maxZBlockPos = endZ * CLOUD_BOX_WIDTH;
+
+ Color color = CLOUD_COLOR;
+ if (DEBUG_BORDER_COLORS)
+ {
+ // equals is included so the board is 2 blocks wide, it makes it easier to see
+ if (startX <= 1) { color = Color.RED; }
+ else if (startX >= textureWidth - 2) { color = Color.GREEN; }
+ if (startZ <= 1) { color = Color.BLUE; }
+ else if (endZ >= textureWidth - 2) { color = Color.BLACK; }
+ }
+
+ DhApiRenderableBox box = new DhApiRenderableBox(
+ new DhApiVec3d(minXBlockPos, 0, minZBlockPos),
+ new DhApiVec3d(maxXBlockPos, CLOUD_BOX_THICKNESS, maxZBlockPos),
+ color,
+ EDhApiBlockMaterial.UNKNOWN
+ );
+ boxList.add(box);
+ }
+ }
+ }
+
+
+
+ //========================//
+ // create the renderables //
+ //========================//
+
+ // slightly lighter shading than the default
+ DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded();
+ cloudShading.north = cloudShading.south = 0.9f;
+ cloudShading.east = cloudShading.west = 0.8f;
+ cloudShading.top = 1.0f;
+ cloudShading.bottom = 0.7f;
+
+ // 3x3 area so we clouds should always be overhead
+ for (int x = -1; x <= 1; x++)
+ {
+ for (int z = -1; z <= 1; z++)
+ {
+ IDhApiRenderableBoxGroup boxGroup = GenericRenderObjectFactory.INSTANCE.createRelativePositionedGroup(
+ ModInfo.NAME + ":Clouds",
+ new DhApiVec3d(0, 0, 0), // the offset will be set during rendering
+ boxList);
+ boxGroup.setBlockLight(LodUtil.MIN_MC_LIGHT);
+ boxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
+ boxGroup.setSsaoEnabled(false);
+ boxGroup.setShading(cloudShading);
+
+ CloudParams params = new CloudParams(textureWidth, x, z);
+ boxGroup.setPreRenderFunc((renderParam) -> this.preRender(params));
+
+ renderer.add(boxGroup);
+ this.boxGroupByOffset[x+1][z+1] = boxGroup;
+ }
+ }
+ }
+
+ private void preRender(CloudParams clouds)
+ {
+ IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[clouds.instanceOffsetX+1][clouds.instanceOffsetZ+1];
+
+ boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get();
+ boxGroup.setActive(renderClouds);
+ if(!renderClouds)
+ {
+ return;
+ }
+
+ if (!this.renderer.getUseInstancedRendering())
+ {
+ if (!this.disabledWarningLogged)
+ {
+ this.disabledWarningLogged = true;
+ LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled.");
+ }
+ boxGroup.setActive(false);
+ return;
+ }
+
+
+ //================//
+ // cloud movement //
+ //================//
+
+ long currentTime = System.currentTimeMillis();
+ float deltaTime = (currentTime - clouds.lastFrameTime) / 1000.0f; // Delta time in seconds
+ clouds.lastFrameTime = currentTime;
+
+ float deltaX = this.moveSpeedInBlocksPerSecond * deltaTime;
+ // negative delta is to match vanilla's cloud movement
+ clouds.xOffset -= deltaX;
+ // wrap the cloud around after reaching the edge
+ clouds.xOffset %= clouds.widthInBlocks;
+
+
+
+ //============================//
+ // camera movement and offset //
+ //============================//
+
+ // camera position
+ int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x;
+ int cameraPosZ = (int)MC_RENDER.getCameraExactPosition().z;
+ // offset the camera position by negative 1 width when below zero to fix off-by-one errors in the negative direction
+ if (cameraPosX < 0) { cameraPosX -= clouds.widthInBlocks; }
+ if (cameraPosZ < 0) { cameraPosZ -= clouds.widthInBlocks; }
+
+ // determine how many cloud instances away from the origin we are
+ int cloudInstanceOffsetX = cameraPosX / (int)clouds.widthInBlocks;
+ int cloudInstanceOffsetZ = cameraPosZ / (int)clouds.widthInBlocks;
+
+ // calculate the new offset
+ float xOffset = (cloudInstanceOffsetX * clouds.widthInBlocks);
+ float zOffset = (cloudInstanceOffsetZ * clouds.widthInBlocks);
+
+
+
+ //==============//
+ // update group //
+ //==============//
+
+ boxGroup.setOriginBlockPos(
+ new DhApiVec3d(
+ clouds.xOffset + (clouds.instanceOffsetX * clouds.widthInBlocks) + xOffset + clouds.halfWidthInBlocks,
+ this.level.getLevelWrapper().getMaxHeight() + 200,
+ clouds.zOffset + (clouds.instanceOffsetZ * clouds.widthInBlocks) + zOffset + clouds.halfWidthInBlocks
+ )
+ );
+ }
+
+
+
+ //==================//
+ // texture handling //
+ //==================//
+
+ private static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException
+ {
+ final ClassLoader loader = Thread.currentThread().getContextClassLoader();
+
+ boolean[][] whitePixels = null;
+ try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH))
+ {
+ if (imageInputStream == null)
+ {
+ throw new FileNotFoundException("Unable to find cloud texture at resource path: ["+CLOUD_RESOURCE_TEXTURE_PATH+"].");
+ }
+
+ BufferedImage image = ImageIO.read(imageInputStream);
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ whitePixels = new boolean[width][height];
+
+ for (int x = 0; x < width; x ++)
+ {
+ for (int z = 0; z < width; z ++)
+ {
+ Color color = new Color(image.getRGB(x,z));
+ whitePixels[x][z] = color.equals(Color.WHITE);
+ }
+ }
+ }
+
+ return whitePixels;
+ }
+
+
+
+ //================//
+ // helper classes //
+ //================//
+
+ private static class CloudParams
+ {
+ public final float textureWidth;
+ public final float widthInBlocks;
+ public final float halfWidthInBlocks;
+
+ public final int instanceOffsetX;
+ public final int instanceOffsetZ;
+
+
+ public float xOffset = 0;
+ public float zOffset = 0;
+
+ public long lastFrameTime = System.currentTimeMillis();
+
+
+
+ // constructor //
+
+ public CloudParams(float textureWidth, int instanceOffsetX, int instanceOffsetZ)
+ {
+ this.textureWidth = textureWidth;
+ this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH);
+ this.halfWidthInBlocks = this.widthInBlocks / 2;
+
+ this.instanceOffsetX = instanceOffsetX;
+ this.instanceOffsetZ = instanceOffsetZ;
+ }
+
+ }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java
new file mode 100644
index 000000000..b3768548d
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java
@@ -0,0 +1,607 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.core.render.renderer.generic;
+
+import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod;
+import com.seibel.distanthorizons.api.enums.config.EDhApiLoggerMode;
+import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
+import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
+import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
+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;
+import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
+import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.logging.f3.F3Screen;
+import com.seibel.distanthorizons.core.render.glObject.GLProxy;
+import com.seibel.distanthorizons.core.render.glObject.GLState;
+import com.seibel.distanthorizons.core.render.glObject.buffer.GLElementBuffer;
+import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer;
+import com.seibel.distanthorizons.core.util.LodUtil;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
+import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
+import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
+import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector;
+import com.seibel.distanthorizons.coreapi.ModInfo;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.lwjgl.opengl.ARBInstancedArrays;
+import org.lwjgl.opengl.GL32;
+import org.lwjgl.opengl.GL33;
+
+import java.awt.*;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Handles rendering generic groups of {@link DhApiRenderableBox}.
+ *
+ * @see IDhApiCustomRenderRegister
+ * @see DhApiRenderableBox
+ */
+public class GenericObjectRenderer implements IDhApiCustomRenderRegister
+{
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+ public static final ConfigBasedSpamLogger SPAM_LOGGER = new ConfigBasedSpamLogger(LogManager.getLogger(GenericObjectRenderer.class), () -> EDhApiLoggerMode.LOG_ALL_TO_CHAT, 1);
+
+ private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
+
+ /**
+ * Can be used to troubleshoot the renderer.
+ * If enabled several debug objects will render around (0,150,0).
+ */
+ public static final boolean RENDER_DEBUG_OBJECTS = false;
+
+
+ // rendering setup
+ private boolean init = false;
+
+ private IDhApiGenericObjectShaderProgram shaderProgram;
+ private GLVertexBuffer boxVertexBuffer;
+ private GLElementBuffer boxIndexBuffer;
+
+ private boolean useInstancedRendering;
+ private boolean vertexAttribDivisorSupported;
+ private boolean instancedArraysSupported;
+
+
+
+ private final ConcurrentHashMap boxGroupById = new ConcurrentHashMap<>();
+
+
+
+ /** A box from 0,0,0 to 1,1,1 */
+ private static final float[] BOX_VERTICES = {
+ // Pos x y z
+
+ // min X, vertical face
+ 0, 0, 0,
+ 1, 0, 0,
+ 1, 1, 0,
+ 0, 1, 0,
+ // max X, vertical face
+ 0, 1, 1,
+ 1, 1, 1,
+ 1, 0, 1,
+ 0, 0, 1,
+
+ // min Z, vertical face
+ 0, 0, 1,
+ 0, 0, 0,
+ 0, 1, 0,
+ 0, 1, 1,
+ // max Z, vertical face
+ 1, 0, 1,
+ 1, 1, 1,
+ 1, 1, 0,
+ 1, 0, 0,
+
+ // min Y, horizontal face
+ 0, 0, 1,
+ 1, 0, 1,
+ 1, 0, 0,
+ 0, 0, 0,
+ // max Y, horizontal face
+ 0, 1, 1,
+ 1, 1, 1,
+ 1, 1, 0,
+ 0, 1, 0,
+ };
+
+ private static final int[] BOX_INDICES = {
+ // min X, vertical face
+ 2, 1, 0,
+ 0, 3, 2,
+ // max X, vertical face
+ 6, 5, 4,
+ 4, 7, 6,
+
+ // min Z, vertical face
+ 10, 9, 8,
+ 8, 11, 10,
+ // max Z, vertical face
+ 14, 13, 12,
+ 12, 15, 14,
+
+ // min Y, horizontal face
+ 18, 17, 16,
+ 16, 19, 18,
+ // max Y, horizontal face
+ 20, 21, 22,
+ 22, 23, 20,
+ };
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public GenericObjectRenderer() { }
+
+ public void init()
+ {
+ if (this.init)
+ {
+ return;
+ }
+ this.init = true;
+
+ this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported;
+ this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported;
+ this.useInstancedRendering = this.vertexAttribDivisorSupported || this.instancedArraysSupported;
+ if (!this.useInstancedRendering)
+ {
+ LOGGER.warn("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering will be slow.");
+ }
+
+ this.shaderProgram = new GenericObjectShaderProgram(this.useInstancedRendering);
+
+
+ this.createBuffers();
+
+ if (RENDER_DEBUG_OBJECTS)
+ {
+ this.addGenericDebugObjects();
+ }
+ }
+ private void createBuffers()
+ {
+ // box vertices
+ ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES);
+ boxVerticesBuffer.order(ByteOrder.nativeOrder());
+ boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES);
+ boxVerticesBuffer.rewind();
+ this.boxVertexBuffer = new GLVertexBuffer(false);
+ this.boxVertexBuffer.bind();
+ this.boxVertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES);
+
+
+ // box vertex indexes
+ ByteBuffer solidIndexBuffer = ByteBuffer.allocateDirect(BOX_INDICES.length * Integer.BYTES);
+ solidIndexBuffer.order(ByteOrder.nativeOrder());
+ solidIndexBuffer.asIntBuffer().put(BOX_INDICES);
+ solidIndexBuffer.rewind();
+ this.boxIndexBuffer = new GLElementBuffer(false);
+ this.boxIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW);
+ this.boxIndexBuffer.bind();
+
+ }
+ private void addGenericDebugObjects()
+ {
+ GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE;
+
+
+ // single giant box
+ IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox(
+ ModInfo.NAME + ":CyanChunkBox",
+ new DhApiRenderableBox(
+ new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16),
+ new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125),
+ EDhApiBlockMaterial.WATER)
+ );
+ singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
+ singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
+ this.add(singleGiantBoxGroup);
+
+
+ // single slender box
+ IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox(
+ ModInfo.NAME + ":GreenBeacon",
+ new DhApiRenderableBox(
+ new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32),
+ new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125),
+ EDhApiBlockMaterial.ILLUMINATED)
+ );
+ singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT);
+ singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT);
+ this.add(singleTallBoxGroup);
+
+
+ // absolute box group
+ ArrayList absBoxList = new ArrayList<>();
+ for (int i = 0; i < 18; i++)
+ {
+ absBoxList.add(new DhApiRenderableBox(
+ new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25),
+ new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()),
+ EDhApiBlockMaterial.LAVA
+ )
+ );
+ }
+ IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList);
+ this.add(absolutePosBoxGroup);
+
+
+ // relative box group
+ ArrayList relBoxList = new ArrayList<>();
+ for (int i = 0; i < 8; i+=2)
+ {
+ relBoxList.add(new DhApiRenderableBox(
+ new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1),
+ new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()),
+ EDhApiBlockMaterial.METAL
+ )
+ );
+ }
+ IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup(
+ ModInfo.NAME + ":MovingMagentaGroup",
+ new DhApiVec3d(24, 140, 24),
+ relBoxList);
+ relativePosBoxGroup.setPreRenderFunc((event) ->
+ {
+ DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos();
+ pos.x += event.partialTicks / 2;
+ pos.x %= 32;
+ relativePosBoxGroup.setOriginBlockPos(pos);
+ });
+ this.add(relativePosBoxGroup);
+
+
+ // massive relative box group
+ ArrayList massRelBoxList = new ArrayList<>();
+ for (int x = 0; x < 50*2; x+=2)
+ {
+ for (int z = 0; z < 50*2; z+=2)
+ {
+ massRelBoxList.add(new DhApiRenderableBox(
+ new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z),
+ new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()),
+ EDhApiBlockMaterial.TERRACOTTA
+ )
+ );
+ }
+ }
+ IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup(
+ ModInfo.NAME + ":MassRedGroup",
+ new DhApiVec3d(-25, 140, 0),
+ massRelBoxList);
+ massRelativePosBoxGroup.setPreRenderFunc((event) ->
+ {
+ DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos();
+ blockPos.y += event.partialTicks / 4;
+ if (blockPos.y > 150f)
+ {
+ blockPos.y = 140f;
+
+ Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED;
+ massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; });
+ massRelativePosBoxGroup.triggerBoxChange();
+ }
+
+ massRelativePosBoxGroup.setOriginBlockPos(blockPos);
+ });
+ this.add(massRelativePosBoxGroup);
+ }
+
+
+
+ //==============//
+ // registration //
+ //==============//
+
+ @Override
+ public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException
+ {
+ if (!(iBoxGroup instanceof RenderableBoxGroup))
+ {
+ throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"].");
+ }
+ RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup;
+
+
+ long id = boxGroup.getId();
+ if (this.boxGroupById.containsKey(id))
+ {
+ throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present.");
+ }
+
+ this.boxGroupById.put(id, boxGroup);
+ }
+
+ @Override
+ public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); }
+
+ public void clear() { this.boxGroupById.clear(); }
+
+
+
+ //===========//
+ // rendering //
+ //===========//
+
+ /**
+ * @param renderingWithSsao
+ * if true that means this render call is happening before the SSAO pass
+ * and any objects rendered in this pass will have SSAO applied to them.
+ */
+ public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao)
+ {
+ // render setup //
+ profiler.push("setup");
+
+ GLState glState = new GLState();
+ this.init();
+
+ ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam);
+
+ GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL);
+ GL32.glEnable(GL32.GL_DEPTH_TEST);
+
+ GL32.glEnable(GL32.GL_BLEND);
+ GL32.glBlendEquation(GL32.GL_FUNC_ADD);
+ GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
+
+ IDhApiGenericObjectShaderProgram shaderProgram = this.shaderProgram;
+ IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class);
+ if (shaderProgramOverride != null && shaderProgram.overrideThisFrame())
+ {
+ shaderProgram = shaderProgramOverride;
+ }
+
+ shaderProgram.bind(renderEventParam);
+ shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId());
+
+ this.boxIndexBuffer.bind();
+
+ Vec3d camPos = MC_RENDER.getCameraExactPosition();
+
+
+
+ // rendering //
+
+ Collection boxList = this.boxGroupById.values();
+ for (RenderableBoxGroup boxGroup : boxList)
+ {
+ // skip boxes that shouldn't render this pass
+ if (boxGroup.ssaoEnabled == renderingWithSsao)
+ {
+ profiler.popPush("render prep");
+ boxGroup.preRender(renderEventParam);
+
+ // ignore inactive groups
+ if (boxGroup.active)
+ {
+ boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup));
+ if (!cancelRendering)
+ {
+ profiler.popPush("rendering");
+ profiler.push(boxGroup.getResourceLocationNamespace());
+ profiler.push(boxGroup.getResourceLocationPath());
+ if (this.useInstancedRendering)
+ {
+ this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos);
+ }
+ else
+ {
+ this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos);
+ }
+ profiler.pop(); // resource path
+ profiler.pop(); // resource namespace
+
+ boxGroup.postRender(renderEventParam);
+ }
+ }
+ }
+ }
+
+
+
+ // clean up //
+ profiler.popPush("cleanup");
+
+ ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam);
+
+ shaderProgram.unbind();
+ glState.restore();
+
+ profiler.pop();
+ }
+
+
+
+ //=====================//
+ // instanced rendering //
+ //=====================//
+
+ private void renderBoxGroupInstanced(IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam, RenderableBoxGroup boxGroup, Vec3d camPos)
+ {
+ // update instance data //
+
+ boxGroup.updateVertexAttributeData();
+
+ DhApiRenderableBoxGroupShading shading = boxGroup.shading;
+ if (shading == null)
+ {
+ shading = DhApiRenderableBoxGroupShading.getUnshaded();
+ }
+
+ shaderProgram.fillIndirectUniformData(
+ renderEventParam,
+ shading, boxGroup,
+ camPos);
+
+
+
+ // Bind instance data //
+
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceColorVbo);
+ GL32.glEnableVertexAttribArray(1);
+ GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0);
+ this.vertexAttribDivisor(1, 1);
+
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceScaleVbo);
+ GL32.glEnableVertexAttribArray(2);
+ this.vertexAttribDivisor(2, 1);
+ GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0);
+
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceChunkPosVbo);
+ GL32.glEnableVertexAttribArray(3);
+ this.vertexAttribDivisor(3, 1);
+ GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0);
+
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceSubChunkPosVbo);
+ GL32.glEnableVertexAttribArray(4);
+ this.vertexAttribDivisor(4, 1);
+ GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0);
+
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceMaterialVbo);
+ GL32.glEnableVertexAttribArray(5);
+ this.vertexAttribDivisor(5, 1);
+ GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0);
+
+
+ // Draw instanced
+ if (boxGroup.uploadedBoxCount > 0)
+ {
+ GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, boxGroup.uploadedBoxCount);
+ }
+
+
+ // Clean up
+ GL32.glDisableVertexAttribArray(1);
+ GL32.glDisableVertexAttribArray(2);
+ GL32.glDisableVertexAttribArray(3);
+ GL32.glDisableVertexAttribArray(4);
+ GL32.glDisableVertexAttribArray(5);
+ }
+ /**
+ * Clean way to handle both {@link GL33#glVertexAttribDivisor} and {@link ARBInstancedArrays#glVertexAttribDivisorARB}
+ * based on which one is supported.
+ */
+ private void vertexAttribDivisor(int index, int divisor)
+ {
+ if (this.vertexAttribDivisorSupported)
+ {
+ GL33.glVertexAttribDivisor(index, divisor);
+ }
+ else if(this.instancedArraysSupported)
+ {
+ ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
+ }
+ else
+ {
+ throw new IllegalStateException("Instanced rendering isn't supported by this machine. Direct rendering should have been used instead.");
+ }
+ }
+
+
+
+
+ //==================//
+ // direct rendering //
+ //==================//
+
+ private void renderBoxGroupDirect(IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam, RenderableBoxGroup boxGroup, Vec3d camPos)
+ {
+ DhApiRenderableBoxGroupShading shading = boxGroup.shading;
+ if (shading == null)
+ {
+ shading = DhApiRenderableBoxGroupShading.getUnshaded();
+ }
+
+ shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos);
+
+ for (DhApiRenderableBox box : boxGroup)
+ {
+ this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos);
+ }
+ }
+ private void renderBox(
+ IDhApiGenericObjectShaderProgram shaderProgram,
+ DhApiRenderParam renderEventParam,
+ RenderableBoxGroup boxGroup, DhApiRenderableBox box,
+ Vec3d camPos)
+ {
+ shaderProgram.fillDirectUniformData(renderEventParam, boxGroup, box, camPos);
+ GL32.glDrawElements(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0);
+ }
+
+
+
+ //=========//
+ // getters //
+ //=========//
+
+ /** @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;
+ }
+
+
+
+ //=========//
+ // F3 menu //
+ //=========//
+
+ public String getVboRenderDebugMenuString()
+ {
+ // get counts
+ int totalCount = this.boxGroupById.size();
+ int activeCount = 0;
+ for (long key : this.boxGroupById.keySet())
+ {
+ RenderableBoxGroup renderGroup = this.boxGroupById.get(key);
+ if (renderGroup.active)
+ {
+ activeCount++;
+ }
+ }
+
+
+ String totalCountText = F3Screen.NUMBER_FORMAT.format(totalCount);
+ String activeCountText = F3Screen.NUMBER_FORMAT.format(activeCount);
+ return LodUtil.formatLog("Generic Obj Count: " + activeCountText + "/" + totalCountText);
+ }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java
new file mode 100644
index 000000000..d021d04e0
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java
@@ -0,0 +1,229 @@
+package com.seibel.distanthorizons.core.render.renderer.generic;
+
+import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram;
+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.math.DhApiVec3i;
+import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
+import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading;
+import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute;
+import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer;
+import com.seibel.distanthorizons.core.util.LodUtil;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
+
+public class GenericObjectShaderProgram extends ShaderProgram implements IDhApiGenericObjectShaderProgram
+{
+ public static final String VERTEX_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/vert.vert";
+ public static final String VERTEX_SHADER_DIRECT_PATH = "shaders/genericObject/direct/vert.vert";
+ public static final String FRAGMENT_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/frag.frag";
+ public static final String FRAGMENT_SHADER_DIRECT_PATH = "shaders/genericObject/direct/frag.frag";
+
+ public final AbstractVertexAttribute va;
+
+
+ // shader uniforms
+ private final int directShaderTransformUniform;
+ private final int directShaderColorUniform;
+
+ private final int instancedShaderOffsetChunkUniform;
+ private final int instancedShaderOffsetSubChunkUniform;
+ private final int instancedShaderCameraChunkPosUniform;
+ private final int instancedShaderCameraSubChunkPosUniform;
+ private final int instancedShaderProjectionModelViewMatrixUniform;
+
+ private final int lightMapUniform;
+ private final int skyLightUniform;
+ private final int blockLightUniform;
+
+ private final int northShadingUniform;
+ private final int southShadingUniform;
+ private final int eastShadingUniform;
+ private final int westShadingUniform;
+ private final int topShadingUniform;
+ private final int bottomShadingUniform;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public GenericObjectShaderProgram(boolean useInstancedRendering)
+ {
+ super(
+ useInstancedRendering ? VERTEX_SHADER_INSTANCED_PATH : VERTEX_SHADER_DIRECT_PATH,
+ useInstancedRendering ? FRAGMENT_SHADER_INSTANCED_PATH : FRAGMENT_SHADER_DIRECT_PATH,
+ "fragColor", new String[]{"vPosition"});
+
+ this.va = AbstractVertexAttribute.create();
+ this.va.bind();
+ // Pos
+ this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false));
+ this.va.completeAndCheck(Float.BYTES * 3);
+
+ this.directShaderTransformUniform = this.tryGetUniformLocation("uTransform");
+ this.directShaderColorUniform = this.tryGetUniformLocation("uColor");
+
+ this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation("uOffsetChunk");
+ this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation("uOffsetSubChunk");
+ this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation("uCameraPosChunk");
+ this.instancedShaderCameraSubChunkPosUniform = this.tryGetUniformLocation("uCameraPosSubChunk");
+ this.instancedShaderProjectionModelViewMatrixUniform = this.tryGetUniformLocation("uProjectionMvm");
+
+ this.lightMapUniform = this.getUniformLocation("uLightMap");
+ this.skyLightUniform = this.getUniformLocation("uSkyLight");
+ this.blockLightUniform = this.getUniformLocation("uBlockLight");
+ this.northShadingUniform = this.getUniformLocation("uNorthShading");
+ this.southShadingUniform = this.getUniformLocation("uSouthShading");
+ this.eastShadingUniform = this.getUniformLocation("uEastShading");
+ this.westShadingUniform = this.getUniformLocation("uWestShading");
+ this.topShadingUniform = this.getUniformLocation("uTopShading");
+ this.bottomShadingUniform = this.getUniformLocation("uBottomShading");
+
+ }
+
+
+
+ //=========//
+ // methods //
+ //=========//
+
+ @Override
+ public void bind(DhApiRenderParam renderEventParam)
+ {
+ super.bind();
+ this.va.bind();
+ }
+ @Override
+ public void unbind()
+ {
+ super.unbind();
+ this.va.unbind();
+ }
+
+ @Override
+ public void free()
+ {
+ this.va.free();
+ super.free();
+ }
+
+ @Override
+ public void bindVertexBuffer(int vbo) { this.va.bindBufferToAllBindingPoints(vbo); }
+
+ @Override
+ public void fillIndirectUniformData(
+ DhApiRenderParam renderParameters,
+ DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
+ DhApiVec3d camPos
+ )
+ {
+ Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
+ projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix);
+
+ super.bind();
+
+
+
+
+ this.setUniform(this.instancedShaderOffsetChunkUniform,
+ new DhApiVec3i(
+ LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
+ LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
+ LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
+ ));
+ this.setUniform(this.instancedShaderOffsetSubChunkUniform,
+ new Vec3f(
+ LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x),
+ LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y),
+ LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z)
+ ));
+
+ this.setUniform(this.instancedShaderCameraChunkPosUniform,
+ new DhApiVec3i(
+ LodUtil.getChunkPosFromDouble(camPos.x),
+ LodUtil.getChunkPosFromDouble(camPos.y),
+ LodUtil.getChunkPosFromDouble(camPos.z)
+ ));
+ this.setUniform(this.instancedShaderCameraSubChunkPosUniform,
+ new Vec3f(
+ LodUtil.getSubChunkPosFromDouble(camPos.x),
+ LodUtil.getSubChunkPosFromDouble(camPos.y),
+ LodUtil.getSubChunkPosFromDouble(camPos.z)
+ ));
+
+ this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, projectionMvmMatrix);
+
+ this.setUniform(this.lightMapUniform, 0); // TODO this should probably be passed in
+ this.setUniform(this.skyLightUniform, boxGroup.getSkyLight());
+ this.setUniform(this.blockLightUniform, boxGroup.getBlockLight());
+
+
+ this.setUniform(this.northShadingUniform, shading.north);
+ this.setUniform(this.southShadingUniform, shading.south);
+ this.setUniform(this.eastShadingUniform, shading.east);
+ this.setUniform(this.westShadingUniform, shading.west);
+ this.setUniform(this.topShadingUniform, shading.top);
+ this.setUniform(this.bottomShadingUniform, shading.bottom);
+
+
+ }
+
+
+ @Override
+ public void fillSharedDirectUniformData(
+ DhApiRenderParam renderParameters,
+ DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup,
+ DhApiVec3d camPos)
+ {
+
+ this.setUniform(this.lightMapUniform, 0); // TODO this should probably be passed in
+ this.setUniform(this.skyLightUniform, boxGroup.getSkyLight());
+ this.setUniform(this.blockLightUniform, boxGroup.getBlockLight());
+
+
+ this.setUniform(this.northShadingUniform, shading.north);
+ this.setUniform(this.southShadingUniform, shading.south);
+ this.setUniform(this.eastShadingUniform, shading.east);
+ this.setUniform(this.westShadingUniform, shading.west);
+ this.setUniform(this.topShadingUniform, shading.top);
+ this.setUniform(this.bottomShadingUniform, shading.bottom);
+
+ }
+
+ public void fillDirectUniformData(
+ DhApiRenderParam renderParameters,
+ IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box,
+ DhApiVec3d camPos)
+ {
+ Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix);
+ projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix);
+
+ Mat4f boxTransform = Mat4f.createTranslateMatrix(
+ (float) (box.minPos.x + boxGroup.getOriginBlockPos().x - camPos.x),
+ (float) (box.minPos.y + boxGroup.getOriginBlockPos().y - camPos.y),
+ (float) (box.minPos.z + boxGroup.getOriginBlockPos().z - camPos.z));
+ boxTransform.multiply(Mat4f.createScaleMatrix(
+ (float) (box.maxPos.x - box.minPos.x),
+ (float) (box.maxPos.y - box.minPos.y),
+ (float) (box.maxPos.z - box.minPos.z)));
+ projectionMvmMatrix.multiply(boxTransform);
+ this.setUniform(this.directShaderTransformUniform, projectionMvmMatrix);
+
+ this.setUniform(this.directShaderColorUniform, box.color);
+
+ }
+
+
+
+ @Override
+ public int getId() { return this.id; }
+
+ /** The base DH render program should always render */
+ @Override
+ public boolean overrideThisFrame() { return true; }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java
new file mode 100644
index 000000000..ca0590957
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java
@@ -0,0 +1,78 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.core.render.renderer.generic;
+
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
+import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3d;
+import com.seibel.distanthorizons.api.objects.math.DhApiVec3f;
+import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox;
+import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
+import com.seibel.distanthorizons.core.util.math.Vec3d;
+import com.seibel.distanthorizons.core.util.math.Vec3f;
+import org.apache.logging.log4j.Logger;
+
+import java.util.List;
+import java.util.*;
+
+/**
+ * Handles creating {@link DhApiRenderableBox}.
+ *
+ * @see IDhApiCustomRenderRegister
+ * @see DhApiRenderableBox
+ */
+public class GenericRenderObjectFactory implements IDhApiCustomRenderObjectFactory
+{
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+
+ public static final GenericRenderObjectFactory INSTANCE = new GenericRenderObjectFactory();
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ private GenericRenderObjectFactory() { }
+
+
+
+ //================//
+ // group creation //
+ //================//
+
+ @Override
+ public IDhApiRenderableBoxGroup createForSingleBox(String resourceLocation, DhApiRenderableBox box)
+ {
+ ArrayList list = new ArrayList<>();
+ list.add(box);
+ return this.createAbsolutePositionedGroup(resourceLocation, list);
+ }
+
+ @Override
+ public IDhApiRenderableBoxGroup createRelativePositionedGroup(String resourceLocation, DhApiVec3d originBlockPos, List boxList)
+ { return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(originBlockPos.x, originBlockPos.y, originBlockPos.z), boxList, true); }
+
+ @Override
+ public IDhApiRenderableBoxGroup createAbsolutePositionedGroup(String resourceLocation, List boxList)
+ { return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(0, 0, 0), boxList, false); }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java
new file mode 100644
index 000000000..e02588c86
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java
@@ -0,0 +1,354 @@
+package com.seibel.distanthorizons.core.render.renderer.generic;
+
+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;
+import com.seibel.distanthorizons.core.render.glObject.GLProxy;
+import com.seibel.distanthorizons.core.util.LodUtil;
+import org.jetbrains.annotations.Nullable;
+import org.lwjgl.opengl.GL32;
+
+import java.awt.*;
+import java.io.Closeable;
+import java.util.*;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.UnaryOperator;
+import java.util.stream.Stream;
+
+public class RenderableBoxGroup
+ extends AbstractList
+ implements IDhApiRenderableBoxGroup, Closeable
+ {
+ public final static AtomicInteger NEXT_ID_ATOMIC_INT = new AtomicInteger(0);
+
+
+
+ public final long id;
+
+ public final String resourceLocationNamespace;
+ public final String resourceLocationPath;
+
+ /** If false the boxes will be positioned relative to the level's origin */
+ public final boolean positionBoxesRelativeToGroupOrigin;
+
+ private final ArrayList boxList;
+
+ private final DhApiVec3d originBlockPos;
+
+
+ public boolean active = true;
+ public boolean ssaoEnabled = true;
+ private boolean vertexDataDirty = true;
+
+ public int skyLight = 15;
+ public int blockLight = 0;
+ public DhApiRenderableBoxGroupShading shading = DhApiRenderableBoxGroupShading.getDefaultShaded();
+
+ @Nullable
+ public Consumer beforeRenderFunc;
+ public Consumer afterRenderFunc;
+
+ // instance data
+ public int instanceColorVbo = 0;
+ public int instanceMaterialVbo = 0;
+ public int instanceScaleVbo = 0;
+ public int instanceChunkPosVbo = 0;
+ public int instanceSubChunkPosVbo = 0;
+
+ public int uploadedBoxCount = -1;
+
+
+
+ // setters/getters //
+
+ @Override
+ public long getId() { return this.id; }
+
+ @Override
+ public String getResourceLocationNamespace() { return this.resourceLocationNamespace; }
+ @Override
+ public String getResourceLocationPath() { return this.resourceLocationPath; }
+
+ @Override
+ public void setOriginBlockPos(DhApiVec3d pos)
+ {
+ this.originBlockPos.x = pos.x;
+ this.originBlockPos.y = pos.y;
+ this.originBlockPos.z = pos.z;
+ }
+
+ @Override
+ public DhApiVec3d getOriginBlockPos() { return new DhApiVec3d(this.originBlockPos.x, this.originBlockPos.y, this.originBlockPos.z); }
+
+
+ @Override
+ public void setSkyLight(int skyLight)
+ {
+ if (skyLight < LodUtil.MIN_MC_LIGHT || skyLight > LodUtil.MAX_MC_LIGHT)
+ {
+ throw new IllegalArgumentException("Sky light ["+skyLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive).");
+ }
+ this.skyLight = skyLight;
+ }
+ @Override
+ public int getSkyLight() { return this.skyLight; }
+
+ @Override
+ public void setBlockLight(int blockLight)
+ {
+ if (blockLight < LodUtil.MIN_MC_LIGHT || blockLight > LodUtil.MAX_MC_LIGHT)
+ {
+ throw new IllegalArgumentException("Block light ["+blockLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive).");
+ }
+ this.blockLight = blockLight;
+ }
+ @Override
+ public int getBlockLight() { return this.blockLight; }
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public RenderableBoxGroup(
+ String resourceLocation,
+ DhApiVec3d originBlockPos, List boxList,
+ boolean positionBoxesRelativeToGroupOrigin) throws IllegalArgumentException
+ {
+ String[] splitResourceLocation = resourceLocation.split(":");
+ if (splitResourceLocation.length != 2)
+ {
+ throw new IllegalArgumentException("Resource Location must be a string that's separated by a single colon, for example: [DistantHorizons:Beacons], your namespace ["+resourceLocation+"], contains ["+(splitResourceLocation.length-1)+"] colons.");
+ }
+
+ this.resourceLocationNamespace = splitResourceLocation[0];
+ this.resourceLocationPath = splitResourceLocation[1];
+
+ this.id = NEXT_ID_ATOMIC_INT.getAndIncrement();
+ this.boxList = new ArrayList<>(boxList);
+
+ this.originBlockPos = originBlockPos;
+ this.positionBoxesRelativeToGroupOrigin = positionBoxesRelativeToGroupOrigin;
+ }
+
+
+
+ // methods //
+
+ @Override
+ public boolean add(DhApiRenderableBox box) { return this.boxList.add(box); }
+
+ @Override
+ public void setPreRenderFunc(Consumer func) { this.beforeRenderFunc = func; }
+
+ @Override
+ public void setPostRenderFunc(Consumer func) { this.afterRenderFunc = func; }
+
+ @Override
+ public void triggerBoxChange() { this.vertexDataDirty = true; }
+
+ @Override
+ public void setActive(boolean active) { this.active = active; }
+ @Override
+ public boolean isActive() { return this.active; }
+
+ @Override
+ public void setSsaoEnabled(boolean ssaoEnabled) { this.ssaoEnabled = ssaoEnabled; }
+ @Override
+ public boolean isSsaoEnabled() { return this.ssaoEnabled; }
+
+ /**
+ * This is called before every frame, even if {@link this#isActive()} returns false.
+ * {@link this#isActive()} can be changed at this point before the object is rendered to the frame.
+ */
+ public void preRender(DhApiRenderParam renderEventParam)
+ {
+ if (this.beforeRenderFunc != null)
+ {
+ this.beforeRenderFunc.accept(renderEventParam);
+ }
+ }
+ /**
+ * Called after rendering is completed.
+ * Can be used to handle any necessary cleanup.
+ */
+ public void postRender(DhApiRenderParam renderEventParam)
+ {
+ if (this.afterRenderFunc != null)
+ {
+ this.afterRenderFunc.accept(renderEventParam);
+ }
+ }
+
+ @Override
+ public void setShading(DhApiRenderableBoxGroupShading shading) { this.shading = shading; }
+ @Override
+ public DhApiRenderableBoxGroupShading getShading() { return this.shading; }
+
+
+
+ //================//
+ // List Overrides //
+ //================//
+
+ @Override
+ public DhApiRenderableBox get(int index) { return this.boxList.get(index); }
+ @Override
+ public int size() { return this.boxList.size(); }
+ @Override
+ public boolean removeIf(Predicate super DhApiRenderableBox> filter) { return this.boxList.removeIf(filter); }
+ @Override
+ public void replaceAll(UnaryOperator operator) { this.boxList.replaceAll(operator); }
+ @Override
+ public void sort(Comparator super DhApiRenderableBox> c) { this.boxList.sort(c); }
+ @Override
+ public void forEach(Consumer super DhApiRenderableBox> action) { this.boxList.forEach(action); }
+ @Override
+ public Spliterator spliterator() { return this.boxList.spliterator(); }
+ @Override
+ public Stream stream() { return this.boxList.stream(); }
+ @Override
+ public Stream parallelStream() { return this.boxList.parallelStream(); }
+
+
+
+ //===================//
+ // vertex attributes //
+ //===================//
+
+ /** Does nothing if the vertex data is already up-to-date */
+ public void updateVertexAttributeData()
+ {
+ if (!this.vertexDataDirty)
+ {
+ return;
+ }
+ this.vertexDataDirty = false;
+
+ if (this.instanceChunkPosVbo == 0)
+ {
+ this.instanceChunkPosVbo = GL32.glGenBuffers();
+ this.instanceSubChunkPosVbo = GL32.glGenBuffers();
+ this.instanceScaleVbo = GL32.glGenBuffers();
+ this.instanceColorVbo = GL32.glGenBuffers();
+ this.instanceMaterialVbo = GL32.glGenBuffers();
+ }
+
+ int boxCount = this.size();
+ this.uploadedBoxCount = boxCount;
+
+
+
+ // transformation / scaling //
+ int[] chunkPosData = new int[boxCount * 3];
+ float[] subChunkPosData = new float[boxCount * 3];
+ float[] scalingData = new float[boxCount * 3];
+ for (int i = 0; i < boxCount; i++)
+ {
+ DhApiRenderableBox box = this.get(i);
+
+ int dataIndex = i * 3;
+
+ chunkPosData[dataIndex] = LodUtil.getChunkPosFromDouble(box.minPos.x);
+ chunkPosData[dataIndex + 1] = LodUtil.getChunkPosFromDouble(box.minPos.y);
+ chunkPosData[dataIndex + 2] = LodUtil.getChunkPosFromDouble(box.minPos.z);
+
+ subChunkPosData[dataIndex] = LodUtil.getSubChunkPosFromDouble(box.minPos.x);
+ subChunkPosData[dataIndex + 1] = LodUtil.getSubChunkPosFromDouble(box.minPos.y);
+ subChunkPosData[dataIndex + 2] = LodUtil.getSubChunkPosFromDouble(box.minPos.z);
+
+ scalingData[dataIndex] = (float) (box.maxPos.x - box.minPos.x);
+ scalingData[dataIndex + 1] = (float) (box.maxPos.y - box.minPos.y);
+ scalingData[dataIndex + 2] = (float) (box.maxPos.z - box.minPos.z);
+
+ }
+
+
+ // colors/materials //
+ float[] colorData = new float[boxCount * 4];
+ byte[] materialData = new byte[boxCount];
+ for (int i = 0; i < boxCount; i++)
+ {
+ DhApiRenderableBox box = this.get(i);
+ Color color = box.color;
+ int colorIndex = i * 4;
+ colorData[colorIndex] = color.getRed() / 255.0f;
+ colorData[colorIndex + 1] = color.getGreen() / 255.0f;
+ colorData[colorIndex + 2] = color.getBlue() / 255.0f;
+ colorData[colorIndex + 3] = color.getAlpha() / 255.0f;
+
+ materialData[i] = box.material;
+ }
+
+
+ // Upload transformation matrices
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceChunkPosVbo);
+ GL32.glBufferData(GL32.GL_ARRAY_BUFFER, chunkPosData ,GL32.GL_DYNAMIC_DRAW);
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceSubChunkPosVbo);
+ GL32.glBufferData(GL32.GL_ARRAY_BUFFER, subChunkPosData ,GL32.GL_DYNAMIC_DRAW);
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceScaleVbo);
+ GL32.glBufferData(GL32.GL_ARRAY_BUFFER, scalingData, GL32.GL_DYNAMIC_DRAW);
+
+ // Upload colors
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceColorVbo);
+ GL32.glBufferData(GL32.GL_ARRAY_BUFFER, colorData, GL32.GL_DYNAMIC_DRAW);
+
+ // Upload materials
+ GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceMaterialVbo);
+ GL32.glBufferData(GL32.GL_ARRAY_BUFFER, colorData, GL32.GL_DYNAMIC_DRAW);
+ }
+
+
+
+ //================//
+ // base overrides //
+ //================//
+
+ @Override
+ public String toString() { return "ID:["+this.id+"], pos:["+this.originBlockPos.x+","+this.originBlockPos.y+","+this.originBlockPos.z+"], size:["+this.size()+"], active:["+this.active+"]"; }
+
+ @Override
+ public void close()
+ {
+ GLProxy.getInstance().queueRunningOnRenderThread(() ->
+ {
+ if (this.instanceChunkPosVbo != 0)
+ {
+ GL32.glDeleteBuffers(this.instanceChunkPosVbo);
+ this.instanceChunkPosVbo = 0;
+ }
+
+ if (this.instanceSubChunkPosVbo != 0)
+ {
+ GL32.glDeleteBuffers(this.instanceSubChunkPosVbo);
+ this.instanceSubChunkPosVbo = 0;
+ }
+
+ if (this.instanceScaleVbo != 0)
+ {
+ GL32.glDeleteBuffers(this.instanceScaleVbo);
+ this.instanceScaleVbo = 0;
+ }
+
+ if (this.instanceColorVbo != 0)
+ {
+ GL32.glDeleteBuffers(this.instanceColorVbo);
+ this.instanceColorVbo = 0;
+ }
+
+ if (this.instanceMaterialVbo != 0)
+ {
+ GL32.glDeleteBuffers(this.instanceMaterialVbo);
+ this.instanceMaterialVbo = 0;
+ }
+ });
+ }
+
+ }
+
\ No newline at end of file
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java
new file mode 100644
index 000000000..4906b75b2
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java
@@ -0,0 +1,104 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.core.render.renderer.shaders;
+
+import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
+import com.seibel.distanthorizons.core.render.renderer.FogRenderer;
+import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
+import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
+import org.lwjgl.opengl.GL32;
+
+/**
+ * Draws the Fog texture onto DH's FrameBuffer.
+ *
+ * See Also:
+ * {@link FogRenderer} - Parent to this shader.
+ * {@link FogShader} - draws the Fog texture.
+ */
+public class FogApplyShader extends AbstractShaderRenderer
+{
+ public static FogApplyShader INSTANCE = new FogApplyShader();
+
+ public int fogTexture;
+
+ // uniforms
+ public int colorTextureUniform;
+ public int depthTextureUniform;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ @Override
+ public void onInit()
+ {
+ this.shader = new ShaderProgram(
+ "shaders/normal.vert",
+ "shaders/fog/apply.frag",
+ "fragColor",
+ new String[]{ "vPosition" });
+
+ // uniform setup
+ this.colorTextureUniform = this.shader.getUniformLocation("uColorTexture");
+ this.depthTextureUniform = this.shader.getUniformLocation("uDepthTexture");
+
+ }
+
+
+
+ //=============//
+ // render prep //
+ //=============//
+
+ @Override
+ protected void onApplyUniforms(float partialTicks)
+ {
+ GL32.glActiveTexture(GL32.GL_TEXTURE0);
+ GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.fogTexture);
+ GL32.glUniform1i(this.colorTextureUniform, 0);
+
+ GL32.glActiveTexture(GL32.GL_TEXTURE1);
+ GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId());
+ GL32.glUniform1i(this.depthTextureUniform, 1);
+ }
+
+
+
+ //========//
+ // render //
+ //========//
+
+ @Override
+ protected void onRender()
+ {
+ GL32.glEnable(GL32.GL_BLEND);
+ GL32.glBlendEquation(GL32.GL_FUNC_ADD);
+ GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA);
+
+ // apply the rendered Fog to DH's framebuffer
+ GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, FogShader.INSTANCE.frameBuffer);
+ GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.getActiveFramebufferId());
+
+ ScreenQuad.INSTANCE.render();
+ }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java
index e5a1fc255..5987af82e 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java
@@ -31,7 +31,7 @@ import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.lwjgl.opengl.GL32;
import java.awt.*;
@@ -44,24 +44,31 @@ public class FogShader extends AbstractShaderRenderer
private static final IVersionConstants VERSION_CONSTANTS = SingletonInjector.INSTANCE.get(IVersionConstants.class);
+ public int frameBuffer;
+
private final LodFogConfig fogConfig;
private Mat4f inverseMvmProjMatrix;
- public int gInvertedModelViewProjectionUniform;
- public int gDepthMapUniform;
-
- // Fog Uniforms
- public int fogColorUniform;
- public int fogScaleUniform;
- public int fogVerticalScaleUniform;
- public int nearFogStartUniform;
- public int nearFogLengthUniform;
- public int fullFogModeUniform;
- public FogShader(LodFogConfig fogConfig)
- {
- this.fogConfig = fogConfig;
- }
+ // Uniforms
+ public int uFogColor;
+ public int uFogScale;
+ public int uFogVerticalScale;
+ public int uNearFogStart;
+ public int uNearFogLength;
+ public int uFullFogMode;
+
+ /** Inverted Model View Projection matrix */
+ public int uInvMvmProj;
+ public int uDepthMap;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public FogShader(LodFogConfig fogConfig) { this.fogConfig = fogConfig; }
@Override
public void onInit()
@@ -76,49 +83,47 @@ public class FogShader extends AbstractShaderRenderer
// all uniforms should be tryGet...
// because disabling fog can cause the GLSL to optimize out most (if not all) uniforms
- this.gInvertedModelViewProjectionUniform = this.shader.tryGetUniformLocation("gInvMvmProj");
- this.gDepthMapUniform = this.shader.tryGetUniformLocation("gDepthMap");
+ this.uDepthMap = this.shader.getUniformLocation("uDepthMap");
+ this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj");
// Fog uniforms
- this.fogColorUniform = this.shader.tryGetUniformLocation("fogColor");
- this.fullFogModeUniform = this.shader.tryGetUniformLocation("fullFogMode");
- this.fogScaleUniform = this.shader.tryGetUniformLocation("fogScale");
- this.fogVerticalScaleUniform = this.shader.tryGetUniformLocation("fogVerticalScale");
+ this.uFogScale = this.shader.tryGetUniformLocation("uFogScale");
+ this.uFogVerticalScale = this.shader.tryGetUniformLocation("uFogVerticalScale");
+ this.uFogColor = this.shader.tryGetUniformLocation("uFogColor");
+ this.uFullFogMode = this.shader.tryGetUniformLocation("uFullFogMode");
// near fog
- this.nearFogStartUniform = this.shader.tryGetUniformLocation("nearFogStart");
- this.nearFogLengthUniform = this.shader.tryGetUniformLocation("nearFogLength");
+ this.uNearFogStart = this.shader.tryGetUniformLocation("uNearFogStart");
+ this.uNearFogLength = this.shader.tryGetUniformLocation("uNearFogLength");
+
}
+
+
+ //=============//
+ // render prep //
+ //=============//
+
@Override
protected void onApplyUniforms(float partialTicks)
{
- this.shader.setUniform(this.gInvertedModelViewProjectionUniform, this.inverseMvmProjMatrix);
-
- int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH;
- int vanillaDrawDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
- vanillaDrawDistance += LodUtil.CHUNK_WIDTH * 2; // Give it a 2 chunk boundary for near fog.
-
- // bind the depth buffer
- if (this.gDepthMapUniform != -1)
+ if (this.inverseMvmProjMatrix != null)
{
- GL32.glActiveTexture(GL32.GL_TEXTURE1);
- GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId());
- GL32.glUniform1i(this.gDepthMapUniform, 1);
+ this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix);
}
- // Fog
- if (this.fullFogModeUniform != -1) this.shader.setUniform(this.fullFogModeUniform, MC_RENDER.isFogStateSpecial() ? 1 : 0);
- if (this.fogColorUniform != -1) this.shader.setUniform(this.fogColorUniform, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks));
+ int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH;
- float nearFogLen = vanillaDrawDistance * 0.2f / lodDrawDistance;
- float nearFogStart = vanillaDrawDistance * (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance;
- if (this.nearFogStartUniform != -1) this.shader.setUniform(this.nearFogStartUniform, nearFogStart);
- if (this.nearFogLengthUniform != -1) this.shader.setUniform(this.nearFogLengthUniform, nearFogLen);
- if (this.fogScaleUniform != -1) this.shader.setUniform(this.fogScaleUniform, 1.f / lodDrawDistance);
- if (this.fogVerticalScaleUniform != -1) this.shader.setUniform(this.fogVerticalScaleUniform, 1.f / MC.getWrappedClientLevel().getHeight());
+ // Fog
+ if (this.uFullFogMode != -1) this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0);
+ if (this.uFogColor != -1) this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks));
+
+ float nearFogStart = (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance;
+ if (this.uNearFogStart != -1) this.shader.setUniform(this.uNearFogStart, nearFogStart);
+ if (this.uNearFogLength != -1) this.shader.setUniform(this.uNearFogLength, 0.0f);
+ if (this.uFogScale != -1) this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance);
+ if (this.uFogVerticalScale != -1) this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight());
}
-
private Color getFogColor(float partialTicks)
{
Color fogColor;
@@ -134,29 +139,34 @@ public class FogShader extends AbstractShaderRenderer
return fogColor;
}
-
private Color getSpecialFogColor(float partialTicks) { return MC_RENDER.getSpecialFogColor(partialTicks); }
- public void setModelViewProjectionMatrix(Mat4f combinedModelViewProjectionMatrix)
+ public void setProjectionMatrix(Mat4f projectionMatrix)
{
- this.inverseMvmProjMatrix = new Mat4f(combinedModelViewProjectionMatrix);
+ this.inverseMvmProjMatrix = new Mat4f(projectionMatrix);
this.inverseMvmProjMatrix.invert();
}
+
+
+ //========//
+ // render //
+ //========//
+
@Override
protected void onRender()
{
GLState state = new GLState();
- GL32.glDisable(GL32.GL_DEPTH_TEST);
+ GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer);
GL32.glDisable(GL32.GL_SCISSOR_TEST);
-
- GL32.glEnable(GL32.GL_BLEND);
- GL32.glBlendEquation(GL32.GL_FUNC_ADD);
- GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE);
+ GL32.glDisable(GL32.GL_DEPTH_TEST);
+ GL32.glDisable(GL32.GL_BLEND);
GL32.glActiveTexture(GL32.GL_TEXTURE0);
- GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveColorTextureId());
+ GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId());
+ GL32.glUniform1i(this.uDepthMap, 0);
+
ScreenQuad.INSTANCE.render();
state.restore();
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java
index eb871c92b..43da8521f 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java
@@ -50,6 +50,11 @@ public class SSAOApplyShader extends AbstractShaderRenderer
public int gFarUniform;
+
+ //=============//
+ // constructor //
+ //=============//
+
@Override
public void onInit()
{
@@ -68,6 +73,12 @@ public class SSAOApplyShader extends AbstractShaderRenderer
this.gFarUniform = this.shader.tryGetUniformLocation("gFar");
}
+
+
+ //=============//
+ // render prep //
+ //=============//
+
@Override
protected void onApplyUniforms(float partialTicks)
{
@@ -102,6 +113,7 @@ public class SSAOApplyShader extends AbstractShaderRenderer
}
+
//========//
// render //
//========//
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java
index 867f891e0..05834e159 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.render.renderer.SSAORenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
-import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
+import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.lwjgl.opengl.GL32;
/**
@@ -38,11 +38,13 @@ public class SSAOShader extends AbstractShaderRenderer
{
public static SSAOShader INSTANCE = new SSAOShader();
+
public int frameBuffer;
private Mat4f projection;
private Mat4f invertedProjection;
+
// uniforms
public int gProjUniform;
public int gInvProjUniform;
@@ -54,11 +56,17 @@ public class SSAOShader extends AbstractShaderRenderer
public int gDepthMapUniform;
+
+ //=============//
+ // constructor //
+ //=============//
+
@Override
public void onInit()
{
this.shader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag",
- "fragColor", new String[]{"vPosition"});
+ "fragColor", new String[]{ "vPosition" }
+ );
// uniform setup
this.gProjUniform = this.shader.getUniformLocation("gProj");
@@ -71,6 +79,12 @@ public class SSAOShader extends AbstractShaderRenderer
this.gDepthMapUniform = this.shader.getUniformLocation("gDepthMap");
}
+
+
+ //=============//
+ // render prep //
+ //=============//
+
public void setProjectionMatrix(Mat4f projectionMatrix)
{
this.projection = projectionMatrix;
@@ -108,6 +122,7 @@ public class SSAOShader extends AbstractShaderRenderer
}
+
//========//
// render //
//========//
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java
new file mode 100644
index 000000000..452af3873
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java
@@ -0,0 +1,55 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.core.sql.dto;
+
+import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
+import com.seibel.distanthorizons.core.pos.DhBlockPos;
+import com.seibel.distanthorizons.core.pos.DhBlockPos;
+
+import java.awt.*;
+
+/** handles storing {@link FullDataSourceV2}'s in the database. */
+public class BeaconBeamDTO implements IBaseDTO
+{
+ public DhBlockPos pos;
+ public Color color;
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public BeaconBeamDTO(DhBlockPos pos, Color color)
+ {
+ this.pos = pos;
+ this.color = color;
+ }
+
+
+
+ //===========//
+ // overrides //
+ //===========//
+
+ @Override
+ public DhBlockPos getKey() { return this.pos; }
+
+}
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java
index 34de259dc..de29b49bb 100644
--- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java
+++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java
@@ -426,6 +426,36 @@ public abstract class AbstractDhRepo> implemen
}
}
+ /** can be used to make sure everything is closed when the world closes */
+ public static void closeAllConnections()
+ {
+ LOGGER.info("Closing all ["+ACTIVE_CONNECTION_STRINGS_BY_REPO.size()+"] database connections...");
+ for (String connectionString : ACTIVE_CONNECTION_STRINGS_BY_REPO.values())
+ {
+ try
+ {
+ Connection connection = CONNECTIONS_BY_CONNECTION_STRING.remove(connectionString);
+ if (connection != null)
+ {
+ if (!connection.isClosed())
+ {
+ LOGGER.info("Closing database connection: [" + connectionString + "]");
+ connection.close();
+ }
+ else
+ {
+ LOGGER.warn("Attempting to close already closed database connection: [" + connectionString + "]");
+ }
+ }
+ }
+ catch(SQLException e)
+ {
+ // connection close failed.
+ LOGGER.error("Unable to close the connection ["+connectionString+"], error: ["+e.getMessage()+"]");
+ }
+ }
+ }
+
@Override
public void close()
{
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java
new file mode 100644
index 000000000..9616a79b2
--- /dev/null
+++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java
@@ -0,0 +1,196 @@
+/*
+ * 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 .
+ */
+
+package com.seibel.distanthorizons.core.sql.repo;
+
+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.sql.dto.BeaconBeamDTO;
+import com.seibel.distanthorizons.core.util.LodUtil;
+import org.apache.logging.log4j.Logger;
+
+import java.awt.*;
+import java.io.File;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class BeaconBeamRepo extends AbstractDhRepo
+{
+ private static final Logger LOGGER = DhLoggerBuilder.getLogger();
+
+
+
+ //=============//
+ // constructor //
+ //=============//
+
+ public BeaconBeamRepo(String databaseType, File databaseFile) throws SQLException
+ {
+ super(databaseType, databaseFile, BeaconBeamDTO.class);
+ }
+
+
+
+ //===========//
+ // overrides //
+ //===========//
+
+ @Override
+ public String getTableName() { return "BeaconBeam"; }
+
+ @Override
+ public String createWhereStatement(DhBlockPos pos) { return "BlockPosX = "+pos.x+" AND BlockPosY = "+pos.y+" AND BlockPosZ = "+pos.z; }
+
+
+
+ //=======================//
+ // repo required methods //
+ //=======================//
+
+ @Override
+ public BeaconBeamDTO convertDictionaryToDto(Map objectMap) throws ClassCastException
+ {
+ int posX = (Integer) objectMap.get("BlockPosX");
+ int posY = (Integer) objectMap.get("BlockPosY");
+ int posZ = (Integer) objectMap.get("BlockPosZ");
+
+ int red = (Integer) objectMap.get("ColorR");
+ int green = (Integer) objectMap.get("ColorG");
+ int blue = (Integer) objectMap.get("ColorB");
+
+
+ BeaconBeamDTO dto = new BeaconBeamDTO(new DhBlockPos(posX, posY, posZ), new Color(red, green, blue));
+ return dto;
+ }
+
+ @Override
+ public PreparedStatement createInsertStatement(BeaconBeamDTO dto) throws SQLException
+ {
+ String sql =
+ "INSERT INTO "+this.getTableName() + " (\n" +
+ " BlockPosX, BlockPosY, BlockPosZ, \n" +
+ " ColorR, ColorG, ColorB, \n" +
+ " LastModifiedUnixDateTime, CreatedUnixDateTime) \n" +
+ "VALUES( \n" +
+ " ?, ?, ?, \n" +
+ " ?, ?, ?, \n" +
+ " ?, ? \n" +
+ ");";
+ PreparedStatement statement = this.createPreparedStatement(sql);
+
+ int i = 1;
+ statement.setObject(i++, dto.pos.x);
+ statement.setObject(i++, dto.pos.y);
+ statement.setObject(i++, dto.pos.z);
+
+ statement.setObject(i++, dto.color.getRed());
+ statement.setObject(i++, dto.color.getGreen());
+ statement.setObject(i++, dto.color.getBlue());
+
+ statement.setObject(i++, System.currentTimeMillis()); // last modified unix time
+ statement.setObject(i++, System.currentTimeMillis()); // created unix time
+
+ return statement;
+ }
+
+ @Override
+ public PreparedStatement createUpdateStatement(BeaconBeamDTO dto) throws SQLException
+ {
+ String sql =
+ "UPDATE "+this.getTableName()+" \n" +
+ "SET \n" +
+ " ColorR = ?, ColorG = ?, ColorB = ?, \n" +
+ " LastModifiedUnixDateTime = ? \n" +
+ "WHERE BlockPosX = ? AND BlockPosY = ? AND BlockPosZ = ?";
+ PreparedStatement statement = this.createPreparedStatement(sql);
+
+ int i = 1;
+ statement.setObject(i++, dto.color.getRed());
+ statement.setObject(i++, dto.color.getGreen());
+ statement.setObject(i++, dto.color.getBlue());
+
+ statement.setObject(i++, System.currentTimeMillis()); // last modified unix time
+
+ statement.setObject(i++, dto.pos.x);
+ statement.setObject(i++, dto.pos.y);
+ statement.setObject(i++, dto.pos.z);
+
+ return statement;
+ }
+
+
+
+ //====================//
+ // additional methods //
+ //====================//
+
+ public List getAllBeamsForPos(DhChunkPos chunkPos)
+ {
+ int minBlockX = chunkPos.getMinBlockX();
+ int minBlockZ = chunkPos.getMinBlockZ();
+ int maxBlockX = minBlockX + LodUtil.CHUNK_WIDTH;
+ int maxBlockZ = minBlockZ + LodUtil.CHUNK_WIDTH;
+
+ return this.getAllBeamsInBlockPosRange(
+ minBlockX, minBlockZ,
+ maxBlockX, maxBlockZ
+ );
+ }
+
+ public List getAllBeamsForPos(long pos)
+ {
+ int minBlockX = DhSectionPos.getMinCornerBlockX(pos);
+ int minBlockZ = DhSectionPos.getMinCornerBlockZ(pos);
+ int maxBlockX = minBlockX + DhSectionPos.getBlockWidth(pos);
+ int maxBlockZ = minBlockZ + DhSectionPos.getBlockWidth(pos);
+
+ return this.getAllBeamsInBlockPosRange(
+ minBlockX, minBlockZ,
+ maxBlockX, maxBlockZ
+ );
+ }
+
+ public List getAllBeamsInBlockPosRange(
+ int minBlockX, int minBlockZ,
+ int maxBlockX, int maxBlockZ
+ )
+ {
+ List