improve lod load time slightly
done by caching the ClientLevelWrapper used to determine block colors
This commit is contained in:
+1
-1
@@ -284,7 +284,7 @@ public class ColumnRenderBufferBuilder
|
||||
}// for z
|
||||
}// for x
|
||||
|
||||
quadBuilder.finalizeData();
|
||||
quadBuilder.mergeQuads();
|
||||
}
|
||||
private static void addLodToBuffer(
|
||||
IDhClientLevel clientLevel,
|
||||
|
||||
+3
-3
@@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial;
|
||||
@@ -210,9 +211,6 @@ public class LodQuadBuilder
|
||||
// data finalizing //
|
||||
//=================//
|
||||
|
||||
/** runs any final data cleanup, merging, etc. */
|
||||
public void finalizeData() { this.mergeQuads(); }
|
||||
|
||||
/** Uses Greedy meshing to merge this builder's Quads. */
|
||||
public void mergeQuads()
|
||||
{
|
||||
@@ -251,7 +249,9 @@ public class LodQuadBuilder
|
||||
private static long mergeQuadsInternal(ArrayList<BufferQuad>[] list, int directionIndex, BufferMergeDirectionEnum mergeDirection)
|
||||
{
|
||||
if (list[directionIndex].size() <= 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
list[directionIndex].sort((objOne, objTwo) -> objOne.compare(objTwo, mergeDirection));
|
||||
|
||||
|
||||
+23
-22
@@ -26,7 +26,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pooling.PhantomArrayListCheckout;
|
||||
import com.seibel.distanthorizons.core.pooling.PhantomArrayListPool;
|
||||
@@ -37,6 +36,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
@@ -66,13 +66,13 @@ public class FullDataToRenderDataTransformer
|
||||
//==============================//
|
||||
|
||||
@Nullable
|
||||
public static ColumnRenderSource transformFullDataToRenderSource(@Nullable FullDataSourceV2 fullDataSource, @Nullable IDhClientLevel level)
|
||||
public static ColumnRenderSource transformFullDataToRenderSource(@Nullable FullDataSourceV2 fullDataSource, @Nullable IClientLevelWrapper levelWrapper)
|
||||
{
|
||||
if (fullDataSource == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (level == null)
|
||||
else if (levelWrapper == null)
|
||||
{
|
||||
// if the client is no longer loaded in the world, render sources cannot be created
|
||||
return null;
|
||||
@@ -81,7 +81,7 @@ public class FullDataToRenderDataTransformer
|
||||
|
||||
try
|
||||
{
|
||||
return transformCompleteFullDataToColumnData(level, fullDataSource);
|
||||
return transformCompleteFullDataToColumnData(levelWrapper, fullDataSource);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
@@ -102,7 +102,7 @@ public class FullDataToRenderDataTransformer
|
||||
* @throws InterruptedException Can be caused by interrupting the thread upstream.
|
||||
* Generally thrown if the method is running after the client leaves the current world.
|
||||
*/
|
||||
private static ColumnRenderSource transformCompleteFullDataToColumnData(IDhClientLevel level, FullDataSourceV2 fullDataSource) throws InterruptedException
|
||||
private static ColumnRenderSource transformCompleteFullDataToColumnData(IClientLevelWrapper levelWrapper, FullDataSourceV2 fullDataSource) throws InterruptedException
|
||||
{
|
||||
final long pos = fullDataSource.getPos();
|
||||
final byte dataDetail = fullDataSource.getDataDetailLevel();
|
||||
@@ -111,7 +111,7 @@ public class FullDataToRenderDataTransformer
|
||||
|
||||
|
||||
|
||||
final ColumnRenderSource columnSource = ColumnRenderSource.createEmpty(pos, vertSize, level.getMinY());
|
||||
final ColumnRenderSource columnSource = ColumnRenderSource.createEmpty(pos, vertSize, levelWrapper.getMinHeight());
|
||||
if (fullDataSource.isEmpty)
|
||||
{
|
||||
return columnSource;
|
||||
@@ -121,9 +121,9 @@ public class FullDataToRenderDataTransformer
|
||||
int baseX = DhSectionPos.getMinCornerBlockX(pos);
|
||||
int baseZ = DhSectionPos.getMinCornerBlockZ(pos);
|
||||
|
||||
for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++)
|
||||
for (int x = 0; x < FullDataSourceV2.WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++)
|
||||
for (int z = 0; z < FullDataSourceV2.WIDTH; z++)
|
||||
{
|
||||
throwIfThreadInterrupted();
|
||||
|
||||
@@ -131,7 +131,7 @@ public class FullDataToRenderDataTransformer
|
||||
LongArrayList dataColumn = fullDataSource.get(x, z);
|
||||
|
||||
updateOrReplaceRenderDataViewColumnWithFullDataColumn(
|
||||
level, fullDataSource.mapping,
|
||||
levelWrapper, fullDataSource.mapping,
|
||||
// bitshift is to account for LODs with a detail level greater than 0 so the block pos is correct
|
||||
baseX + BitShiftUtil.pow(x,dataDetail), baseZ + BitShiftUtil.pow(z,dataDetail),
|
||||
columnArrayView, dataColumn);
|
||||
@@ -145,7 +145,7 @@ public class FullDataToRenderDataTransformer
|
||||
|
||||
/** Updates the given {@link ColumnArrayView} to match the incoming Full data {@link LongArrayList} */
|
||||
public static void updateOrReplaceRenderDataViewColumnWithFullDataColumn(
|
||||
IDhClientLevel level,
|
||||
IClientLevelWrapper levelWrapper,
|
||||
FullDataPointIdMap fullDataMapping, int blockX, int blockZ,
|
||||
ColumnArrayView columnArrayView,
|
||||
LongArrayList fullDataColumn)
|
||||
@@ -160,7 +160,7 @@ public class FullDataToRenderDataTransformer
|
||||
if (fullDataLength <= columnArrayView.verticalSize())
|
||||
{
|
||||
// Directly use the arrayView since it fits.
|
||||
setRenderColumnView(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn);
|
||||
setRenderColumnView(levelWrapper, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -171,7 +171,7 @@ public class FullDataToRenderDataTransformer
|
||||
{
|
||||
// expand the ColumnArrayView to fit the new larger max vertical size
|
||||
ColumnArrayView newColumnArrayView = new ColumnArrayView(dataArrayList, fullDataLength, 0, fullDataLength);
|
||||
setRenderColumnView(level, fullDataMapping, blockX, blockZ, newColumnArrayView, fullDataColumn);
|
||||
setRenderColumnView(levelWrapper, fullDataMapping, blockX, blockZ, newColumnArrayView, fullDataColumn);
|
||||
columnArrayView.changeVerticalSizeFrom(newColumnArrayView);
|
||||
}
|
||||
finally
|
||||
@@ -181,7 +181,7 @@ public class FullDataToRenderDataTransformer
|
||||
}
|
||||
}
|
||||
private static void setRenderColumnView(
|
||||
IDhClientLevel level, FullDataPointIdMap fullDataMapping,
|
||||
IClientLevelWrapper levelWrapper, FullDataPointIdMap fullDataMapping,
|
||||
int blockX, int blockZ,
|
||||
ColumnArrayView renderColumnData, LongArrayList fullColumnData)
|
||||
{
|
||||
@@ -192,18 +192,18 @@ public class FullDataToRenderDataTransformer
|
||||
boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING);
|
||||
boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get();
|
||||
|
||||
HashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper());
|
||||
HashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(level.getLevelWrapper());
|
||||
HashSet<IBlockStateWrapper> blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(levelWrapper);
|
||||
HashSet<IBlockStateWrapper> caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(levelWrapper);
|
||||
|
||||
int caveCullingMaxY = Config.Client.Advanced.Graphics.Culling.caveCullingHeight.get() - level.getMinY();
|
||||
int caveCullingMaxY = Config.Client.Advanced.Graphics.Culling.caveCullingHeight.get() - levelWrapper.getMinHeight();
|
||||
boolean caveCullingEnabled =
|
||||
Config.Client.Advanced.Graphics.Culling.enableCaveCulling.get()
|
||||
&& (
|
||||
// dimensions with a ceiling will be all caves so we don't want cave culling
|
||||
!level.getLevelWrapper().hasCeiling()
|
||||
!levelWrapper.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()
|
||||
&& !levelWrapper.getDimensionType().isTheEnd()
|
||||
);
|
||||
|
||||
boolean isColumnVoid = true;
|
||||
@@ -236,7 +236,7 @@ public class FullDataToRenderDataTransformer
|
||||
int blockLight = FullDataPointUtil.getBlockLight(fullData);
|
||||
int skyLight = FullDataPointUtil.getSkyLight(fullData);
|
||||
|
||||
mutableBlockPos.setY(bottomY + level.getMinY());
|
||||
mutableBlockPos.setY(bottomY + levelWrapper.getMinHeight());
|
||||
|
||||
IBiomeWrapper biome;
|
||||
IBlockStateWrapper block;
|
||||
@@ -250,7 +250,7 @@ public class FullDataToRenderDataTransformer
|
||||
if (!brokenPos.contains(fullDataMapping.getPos()))
|
||||
{
|
||||
brokenPos.add(fullDataMapping.getPos());
|
||||
String levelId = level.getLevelWrapper().getDhIdentifier();
|
||||
String levelId = levelWrapper.getDhIdentifier();
|
||||
LOGGER.warn("Unable to get data point with id ["+id+"] " +
|
||||
"(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " +
|
||||
"for pos ["+fullDataMapping.getPos()+"] in level ["+levelId+"]. " +
|
||||
@@ -324,7 +324,8 @@ public class FullDataToRenderDataTransformer
|
||||
{
|
||||
if (colorBelowWithAvoidedBlocks)
|
||||
{
|
||||
int tempColor = level.computeBaseColor(mutableBlockPos, biome, block);
|
||||
int tempColor = levelWrapper.getBlockColor(mutableBlockPos, biome, block);
|
||||
|
||||
// don't transfer the color when alpha is 0
|
||||
// this prevents issues if grass is transparent
|
||||
if (ColorUtil.getAlpha(tempColor) != 0)
|
||||
@@ -344,7 +345,7 @@ public class FullDataToRenderDataTransformer
|
||||
if (colorToApplyToNextBlock == -1)
|
||||
{
|
||||
// use this block's color
|
||||
color = level.computeBaseColor(mutableBlockPos, biome, block);
|
||||
color = levelWrapper.getBlockColor(mutableBlockPos, biome, block);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -36,17 +36,14 @@ import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue
|
||||
import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource;
|
||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.render.RenderBufferHandler;
|
||||
import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
@@ -297,9 +294,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
||||
// getters //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return this.levelWrapper.getBlockColor(pos, biome, block); }
|
||||
|
||||
@Override
|
||||
public IClientLevelWrapper getClientLevelWrapper() { return this.levelWrapper; }
|
||||
|
||||
|
||||
@@ -94,20 +94,6 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
|
||||
// level handling //
|
||||
//================//
|
||||
|
||||
@Override //FIXME this can fail if the clientLevel isn't available yet, maybe in that case we could return -1 and handle it upstream?
|
||||
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block)
|
||||
{
|
||||
IClientLevelWrapper clientLevel = this.getClientLevelWrapper();
|
||||
if (clientLevel == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return clientLevel.getBlockColor(pos, biome, block);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IClientLevelWrapper getClientLevelWrapper() { return MC_CLIENT.getWrappedClientLevel(); }
|
||||
|
||||
@@ -34,8 +34,6 @@ public interface IDhClientLevel extends IDhLevel
|
||||
void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler);
|
||||
void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler);
|
||||
|
||||
int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block);
|
||||
|
||||
@Nullable
|
||||
IClientLevelWrapper getClientLevelWrapper();
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ package com.seibel.distanthorizons.core.render;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.RemovalCause;
|
||||
import com.google.common.cache.RemovalNotification;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.CachedColumnRenderSource;
|
||||
import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource;
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.seibel.distanthorizons.core.util.KeyedLockContainer;
|
||||
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.util.MathUtil;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -78,6 +79,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
public final long pos;
|
||||
|
||||
private final IDhClientLevel level;
|
||||
private final IClientLevelWrapper levelWrapper;
|
||||
@WillNotClose
|
||||
private final FullDataSourceProviderV2 fullDataSourceProvider;
|
||||
private final LodQuadTree quadTree;
|
||||
@@ -156,6 +158,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
this.cachedRenderSourceByPos = cachedRenderSourceByPos;
|
||||
this.renderLoadLockContainer = renderLoadLockContainer;
|
||||
this.level = level;
|
||||
this.levelWrapper = level.getClientLevelWrapper();
|
||||
this.fullDataSourceProvider = fullDataSourceProvider;
|
||||
this.uploadTaskCountRef = uploadTaskCountRef;
|
||||
|
||||
@@ -309,6 +312,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
adjacentLoadFutures[1] = this.getRenderSourceForPosAsync(DhSectionPos.getAdjacentPos(this.pos, EDhDirection.SOUTH));
|
||||
adjacentLoadFutures[2] = this.getRenderSourceForPosAsync(DhSectionPos.getAdjacentPos(this.pos, EDhDirection.EAST));
|
||||
adjacentLoadFutures[3] = this.getRenderSourceForPosAsync(DhSectionPos.getAdjacentPos(this.pos, EDhDirection.WEST));
|
||||
//adjacentLoadFutures[0] = CompletableFuture.completedFuture(null);
|
||||
//adjacentLoadFutures[1] = CompletableFuture.completedFuture(null);
|
||||
//adjacentLoadFutures[2] = CompletableFuture.completedFuture(null);
|
||||
//adjacentLoadFutures[3] = CompletableFuture.completedFuture(null);
|
||||
return CompletableFuture.allOf(adjacentLoadFutures).thenRun(() ->
|
||||
{
|
||||
try (CachedColumnRenderSource northRenderSource = adjacentLoadFutures[0].get();
|
||||
@@ -387,7 +394,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
// generate new render source
|
||||
try (FullDataSourceV2 fullDataSource = this.fullDataSourceProvider.get(pos))
|
||||
{
|
||||
newCachedRenderSource.columnRenderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.level);
|
||||
newCachedRenderSource.columnRenderSource = FullDataToRenderDataTransformer.transformFullDataToRenderSource(fullDataSource, this.levelWrapper);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -33,14 +33,12 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.pos.DhLodPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.pos.Pos2D;
|
||||
import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
|
||||
import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector;
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
package com.seibel.distanthorizons.core.render.renderer;
|
||||
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.DhClientLevel;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLState;
|
||||
import com.seibel.distanthorizons.core.render.renderer.shaders.FadeApplyShader;
|
||||
import com.seibel.distanthorizons.core.render.renderer.shaders.FadeShader;
|
||||
|
||||
-1
@@ -28,7 +28,6 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
|
||||
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
|
||||
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.core.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
||||
|
||||
Reference in New Issue
Block a user