Buffers: Basically redid the buffer management
Note: Backface Culling currently disabled
This commit is contained in:
@@ -131,13 +131,14 @@ public class CubicLodTemplate
|
||||
//if(vertexOptimizer.isCulled(lodDirection))
|
||||
// continue;
|
||||
// culling
|
||||
|
||||
// FIXME: Reimpl backface culling
|
||||
/*
|
||||
if (lodDirection == LodDirection.NORTH && vertexOptimizer.getZ(lodDirection, 0) < -cullingRangeZ
|
||||
|| lodDirection == LodDirection.EAST && vertexOptimizer.getX(lodDirection, 0) > cullingRangeX
|
||||
|| lodDirection == LodDirection.SOUTH && vertexOptimizer.getZ(lodDirection, 0) > cullingRangeZ
|
||||
|| lodDirection == LodDirection.WEST && vertexOptimizer.getX(lodDirection, 0) < -cullingRangeX)
|
||||
continue;
|
||||
|
||||
*/
|
||||
|
||||
int verticalFaceIndex = 0;
|
||||
while (vertexOptimizer.shouldRenderFace(lodDirection, verticalFaceIndex))
|
||||
|
||||
+540
-694
File diff suppressed because it is too large
Load Diff
@@ -78,9 +78,9 @@ public class LodDimension
|
||||
/** stores if the region at the given x and z index needs to be saved to disk */
|
||||
private volatile boolean[][] isRegionDirty;
|
||||
/** stores if the region at the given x and z index needs to be regenerated */
|
||||
private volatile boolean[][] regenRegionBuffer;
|
||||
/** stores if the buffer size at the given x and z index needs to be changed */
|
||||
private volatile boolean[][] recreateRegionBuffer;
|
||||
// Use int because I need Tri state:
|
||||
// 0: both buffer good. 1: the displaying buffer good. 2: both buffer bad.
|
||||
private volatile int[][] regenRegionBuffer;
|
||||
|
||||
/**
|
||||
* if true that means there are regions in this dimension
|
||||
@@ -143,13 +143,14 @@ public class LodDimension
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
regenRegionBuffer = new boolean[width][width];
|
||||
recreateRegionBuffer = new boolean[width][width];
|
||||
regenRegionBuffer = new int[width][width];
|
||||
|
||||
center = new RegionPos(0, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//FIXME: Race condition on this move and other reading regions!
|
||||
/**
|
||||
* Move the center of this LodDimension and move all owned
|
||||
* regions over by the given x and z offset. <br><br>
|
||||
@@ -167,9 +168,10 @@ public class LodDimension
|
||||
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
for (int z = 0; z < width; z++)
|
||||
for (int z = 0; z < width; z++) {
|
||||
regions[x][z] = null;
|
||||
|
||||
regenRegionBuffer[x][z] = 0;
|
||||
}
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
@@ -186,10 +188,14 @@ public class LodDimension
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (x + xOffset < width)
|
||||
if (x + xOffset < width) {
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regenRegionBuffer[x][z] = regenRegionBuffer[x + xOffset][z];
|
||||
}
|
||||
else {
|
||||
regions[x][z] = null;
|
||||
regenRegionBuffer[x][z] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,11 +206,14 @@ public class LodDimension
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (x + xOffset >= 0)
|
||||
if (x + xOffset >= 0) {
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regenRegionBuffer[x][z] = regenRegionBuffer[x + xOffset][z];
|
||||
}
|
||||
else {
|
||||
regions[x][z] = null;
|
||||
}
|
||||
regenRegionBuffer[x][z] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,10 +226,14 @@ public class LodDimension
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (z + zOffset < width)
|
||||
if (z + zOffset < width) {
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regenRegionBuffer[x][z] = regenRegionBuffer[x][z + zOffset];
|
||||
}
|
||||
else {
|
||||
regions[x][z] = null;
|
||||
regenRegionBuffer[x][z] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -231,10 +244,15 @@ public class LodDimension
|
||||
{
|
||||
for (int z = width - 1; z >= 0; z--)
|
||||
{
|
||||
if (z + zOffset >= 0)
|
||||
if (z + zOffset >= 0) {
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regenRegionBuffer[x][z] = regenRegionBuffer[x][z + zOffset];
|
||||
}
|
||||
else {
|
||||
regions[x][z] = null;
|
||||
regenRegionBuffer[x][z] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,7 +394,8 @@ public class LodDimension
|
||||
|
||||
if (regions[x][z].getMinDetailLevel() > minAllowedDetailLevel) {
|
||||
regions[x][z].cutTree(minAllowedDetailLevel);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
regenRegionBuffer[x][z] = 2;
|
||||
regenDimensionBuffers = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -430,16 +449,16 @@ public class LodDimension
|
||||
if (regions[x][z] == null)
|
||||
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality);
|
||||
|
||||
regenRegionBuffer[x][z] = true;
|
||||
regenRegionBuffer[x][z] = 2;
|
||||
regenDimensionBuffers = true;
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
} else if (region.getMinDetailLevel() > levelToGen) {
|
||||
// Second case, the region exists at a higher detail level.
|
||||
|
||||
// Expand the region by introducing the missing layer
|
||||
region.growTree(levelToGen);
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
regenRegionBuffer[x][z] = 2;
|
||||
regenDimensionBuffers = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -476,7 +495,7 @@ public class LodDimension
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
regenRegionBuffer[xIndex][zIndex] = true;
|
||||
regenRegionBuffer[xIndex][zIndex] = 2;
|
||||
regenDimensionBuffers = true;
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e)
|
||||
@@ -518,7 +537,7 @@ public class LodDimension
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
regenRegionBuffer[xIndex][zIndex] = true;
|
||||
regenRegionBuffer[xIndex][zIndex] = 2;
|
||||
regenDimensionBuffers = true;
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e)
|
||||
@@ -538,7 +557,7 @@ public class LodDimension
|
||||
{
|
||||
int xIndex = (xRegion - center.x) + halfWidth;
|
||||
int zIndex = (zRegion - center.z) + halfWidth;
|
||||
regenRegionBuffer[xIndex][zIndex] = true;
|
||||
regenRegionBuffer[xIndex][zIndex] = 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -760,31 +779,35 @@ public class LodDimension
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
int xRegion = LevelPosUtil.getRegion(detailLevel, posX);
|
||||
int zRegion = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||
LodRegion region = getRegion(xRegion, zRegion);
|
||||
if (region == null)
|
||||
return;
|
||||
|
||||
markRegionBufferToRegen(xRegion, zRegion);
|
||||
region.clear(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the buffer at the given array index needs
|
||||
* to have its buffer regenerated.
|
||||
* to have its buffer regenerated. Also decrease the state by 1
|
||||
*/
|
||||
public boolean doesRegionNeedBufferRegen(int xIndex, int zIndex)
|
||||
public boolean getAndClearRegionNeedBufferRegen(int regionX, int regionZ)
|
||||
{
|
||||
return regenRegionBuffer[xIndex][zIndex] || recreateRegionBuffer[xIndex][zIndex];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets if the buffer at the given array index needs
|
||||
* to have its buffer regenerated.
|
||||
*/
|
||||
public void setRegenRegionBufferByArrayIndex(int xIndex, int zIndex, boolean newRegen)
|
||||
{
|
||||
regenRegionBuffer[xIndex][zIndex] = newRegen;
|
||||
//FIXME: Use actual atomics on regenRegionBuffer
|
||||
//FIXME: Race condition on lodDim move/resize!
|
||||
int xIndex = (regionX - center.x) + halfWidth;
|
||||
int zIndex = (regionZ - center.z) + halfWidth;
|
||||
|
||||
if (xIndex < 0 || xIndex >= width || zIndex < 0 || zIndex >= width)
|
||||
return false;
|
||||
int i = regenRegionBuffer[xIndex][zIndex];
|
||||
if (i > 0) {
|
||||
regenRegionBuffer[xIndex][zIndex]--;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -798,10 +821,13 @@ public class LodDimension
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
int xRegion = LevelPosUtil.getRegion(detailLevel, posX);
|
||||
int zRegion = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||
LodRegion region = getRegion(xRegion, zRegion);
|
||||
if (region == null)
|
||||
return;
|
||||
markRegionBufferToRegen(xRegion, zRegion);
|
||||
|
||||
region.updateArea(detailLevel, posX, posZ);
|
||||
}
|
||||
@@ -882,8 +908,7 @@ public class LodDimension
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
regenRegionBuffer = new boolean[width][width];
|
||||
recreateRegionBuffer = new boolean[width][width];
|
||||
regenRegionBuffer = new int[width][width];
|
||||
|
||||
// populate isRegionDirty
|
||||
for (int i = 0; i < width; i++)
|
||||
|
||||
@@ -35,13 +35,15 @@ public class LodVertexBuffer implements AutoCloseable
|
||||
{
|
||||
public int id;
|
||||
public int vertexCount;
|
||||
public final boolean isBufferStorage;
|
||||
public long size = 0;
|
||||
|
||||
public LodVertexBuffer()
|
||||
public LodVertexBuffer(boolean isBufferStorage)
|
||||
{
|
||||
if (GLProxy.getInstance().getGlContext() == GLProxyContext.NONE)
|
||||
throw new IllegalStateException("Thread [" +Thread.currentThread().getName() + "] tried to create a [" + LodVertexBuffer.class.getSimpleName() + "] outside a OpenGL contex.");
|
||||
|
||||
this.id = GL32.glGenBuffers();
|
||||
this.isBufferStorage = isBufferStorage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,15 +20,14 @@
|
||||
package com.seibel.lod.core.render;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.HashSet;
|
||||
import java.time.Duration;
|
||||
import java.util.Set;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.VertexBuffersAndOffset;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogColorMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
@@ -40,9 +39,11 @@ import com.seibel.lod.core.objects.math.Vec3f;
|
||||
import com.seibel.lod.core.objects.opengl.LodVertexBuffer;
|
||||
import com.seibel.lod.core.render.objects.LightmapTexture;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.GridList;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.MovableGridList;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
@@ -58,11 +59,40 @@ import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
*/
|
||||
public class LodRenderer
|
||||
{
|
||||
public static class VanillaRenderedChunksList extends GridList<Boolean> {
|
||||
private static final long serialVersionUID = -5448501880911391315L;
|
||||
|
||||
public final int centerX;
|
||||
public final int centerZ;
|
||||
|
||||
public VanillaRenderedChunksList(int range, int centerX, int centerZ) {
|
||||
super(range);
|
||||
this.centerX = centerX;
|
||||
this.centerZ = centerZ;
|
||||
for (int i=0; i<gridSize*gridSize; i++) {
|
||||
add(i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
public static class LagSpikeCatcher {
|
||||
|
||||
long timer = System.nanoTime();
|
||||
public LagSpikeCatcher() {}
|
||||
public void end(String source) {
|
||||
timer = System.nanoTime() - timer;
|
||||
if (timer> 16000000) { //16 ms
|
||||
ClientApi.LOGGER.info("NOTE: "+source+" took "+Duration.ofNanos(timer)+"!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IReflectionHandler REFLECTION_HANDLER = SingletonHandler.get(IReflectionHandler.class);
|
||||
|
||||
|
||||
public static final int VANILLA_REFRESH_TIMEOUT = 60;
|
||||
/**
|
||||
* If true the LODs colors will be replaced with
|
||||
* a checkerboard, this can be used for debugging.
|
||||
@@ -75,23 +105,11 @@ public class LodRenderer
|
||||
/** This is used to generate the buildable buffers */
|
||||
private final LodBufferBuilderFactory lodBufferBuilderFactory;
|
||||
|
||||
/** Each VertexBuffer represents 1 region */
|
||||
private LodVertexBuffer[][][] vbos;
|
||||
/**
|
||||
* the OpenGL IDs for the vbos of the same indices.
|
||||
* These have to be separate because we can't override the
|
||||
* buffers in the VBOs (and we don't want to)
|
||||
*/
|
||||
private int[][][] storageBufferIds = null;
|
||||
|
||||
// The shader program
|
||||
LodRenderProgram shaderProgram = null;
|
||||
|
||||
private int vbosCenterX = 0;
|
||||
private int vbosCenterZ = 0;
|
||||
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int[] previousPos = new int[] { 0, 0, 0 };
|
||||
private AbstractBlockPosWrapper previousPos = null;
|
||||
|
||||
// these variables are used to determine if the buffers should be rebuilt
|
||||
private int prevRenderDistance = 0;
|
||||
@@ -115,9 +133,10 @@ public class LodRenderer
|
||||
* This HashSet contains every chunk that Vanilla Minecraft
|
||||
* is going to render
|
||||
*/
|
||||
public boolean[][] vanillaRenderedChunks;
|
||||
public boolean vanillaRenderedChunksChanged;
|
||||
public boolean vanillaRenderedChunksEmptySkip = false;
|
||||
public VanillaRenderedChunksList vanillaRenderedChunks;
|
||||
public int vanillaRenderedChunksCenterX;
|
||||
public int vanillaRenderedChunksCenterZ;
|
||||
public int vanillaRenderedChunksRefreshTimer;
|
||||
|
||||
private boolean canVanillaFogBeDisabled = true;
|
||||
|
||||
@@ -149,8 +168,6 @@ public class LodRenderer
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (MC_RENDER.playerHasBlindnessEffect())
|
||||
{
|
||||
// if the player is blind, don't render LODs,
|
||||
@@ -175,8 +192,17 @@ public class LodRenderer
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client api instead)
|
||||
// starting here...
|
||||
determineIfLodsShouldRegenerate(lodDim, partialTicks);
|
||||
LagSpikeCatcher updateStatue = new LagSpikeCatcher();
|
||||
updateRegenStatus(lodDim, partialTicks);
|
||||
updateStatue.end("LodDrawSetup:UpdateStatus");
|
||||
|
||||
|
||||
// FIXME: Currently, we check for last Lod Dimension so that we can trigger a cleanup() if dimension has changed
|
||||
// The better thing to do is to call cleanup() on leaving dimensions in the EventApi, but only for client-side.
|
||||
if (markToCleanup) {
|
||||
markToCleanup = false;
|
||||
cleanup(); // This will unset the isSetupComplete, causing a setup() call.
|
||||
}
|
||||
|
||||
//=================//
|
||||
// create the LODs //
|
||||
@@ -187,44 +213,32 @@ public class LodRenderer
|
||||
// 2. we aren't already regenerating the LODs
|
||||
// 3. we aren't waiting for the build and draw buffers to swap
|
||||
// (this is to prevent thread conflicts)
|
||||
if ((partialRegen || fullRegen) && !lodBufferBuilderFactory.generatingBuffers && !lodBufferBuilderFactory.newBuffersAvailable())
|
||||
{
|
||||
// generate the LODs on a separate thread to prevent stuttering or freezing
|
||||
lodBufferBuilderFactory.generateLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getY(), MC.getPlayerBlockPos().getZ(), fullRegen);
|
||||
|
||||
if (lodBufferBuilderFactory.updateAndSwapLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos().getX(),
|
||||
MC.getPlayerBlockPos().getY(), MC.getPlayerBlockPos().getZ(), partialRegen, fullRegen)) {
|
||||
// the regen process has been started,
|
||||
// it will be done when lodBufferBuilder.newBuffersAvailable()
|
||||
// is true
|
||||
// it will be done when lodBufferBuilder.newBuffersAvailable() is true
|
||||
fullRegen = false;
|
||||
partialRegen = false;
|
||||
}
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client api instead)
|
||||
// ...ending here
|
||||
|
||||
if (lodBufferBuilderFactory.newBuffersAvailable())
|
||||
{
|
||||
swapBuffers();
|
||||
}
|
||||
// Get the front buffers to draw
|
||||
MovableGridList<LodVertexBuffer[]> vbos = lodBufferBuilderFactory.getFrontBuffers();
|
||||
int vbosCenterX = lodBufferBuilderFactory.getFrontBuffersCenterX();
|
||||
int vbosCenterZ = lodBufferBuilderFactory.getFrontBuffersCenterZ();
|
||||
|
||||
// @Unused
|
||||
if (vbos == null) {
|
||||
// There is still no vbos, which means nothing needs to be drawn. So no rendering needed
|
||||
// (Vbos should be setup by now)
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: Currently, we check for last Lod Dimension so that we can trigger a cleanup() if dimension has changed
|
||||
// The better thing to do is to call cleanup() on leaving dimensions in the EventApi, but only for client-side.
|
||||
if (markToCleanup) {
|
||||
markToCleanup = false;
|
||||
cleanup(); // This will unset the isSetupComplete, causing a setup() call.
|
||||
}
|
||||
|
||||
//===================//
|
||||
// draw params setup //
|
||||
//===================//
|
||||
|
||||
profiler.push("LOD draw setup");
|
||||
LagSpikeCatcher drawSetup = new LagSpikeCatcher();
|
||||
|
||||
/*---------Set GL State--------*/
|
||||
// Make sure to unbind current VBO so we don't mess up vanilla settings
|
||||
@@ -256,7 +270,7 @@ public class LodRenderer
|
||||
|
||||
/*---------Get required data--------*/
|
||||
// Get the matrixs for rendering
|
||||
Mat4f modelViewMatrix = translateModelViewMatrix(mcModelViewMatrix, partialTicks);
|
||||
Mat4f modelViewMatrix = translateModelViewMatrix(mcModelViewMatrix, partialTicks, vbosCenterX, vbosCenterZ);
|
||||
int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
|
||||
int farPlaneBlockDistance;
|
||||
// required for setupFog and setupProjectionMatrix
|
||||
@@ -269,7 +283,7 @@ public class LodRenderer
|
||||
|
||||
/*---------Fill uniform data--------*/
|
||||
// Fill the uniform data. Note: GL33.GL_TEXTURE0 == texture bindpoint 0
|
||||
shaderProgram.fillUniformData(modelViewMatrix, projectionMatrix, getTranslatedCameraPos(),
|
||||
shaderProgram.fillUniformData(modelViewMatrix, projectionMatrix, getTranslatedCameraPos(vbosCenterX, vbosCenterZ),
|
||||
MC_RENDER.isFogStateInUnderWater() ? getUnderWaterFogColor(partialTicks) : getFogColor(partialTicks), (int) (MC.getSkyDarken(partialTicks) * 15), 0);
|
||||
// Previous guy said fog setting may be different from region to region, but the fogSettings never changed... soooooo...
|
||||
shaderProgram.fillUniformDataForFog(fogSettings, MC_RENDER.isFogStateInUnderWater());
|
||||
@@ -279,45 +293,44 @@ public class LodRenderer
|
||||
//===========//
|
||||
// rendering //
|
||||
//===========//
|
||||
|
||||
drawSetup.end("LodDrawSetup");
|
||||
profiler.popPush("LOD draw");
|
||||
LagSpikeCatcher draw = new LagSpikeCatcher();
|
||||
|
||||
boolean cullingDisabled = CONFIG.client().graphics().advancedGraphics().getDisableDirectionalCulling();
|
||||
boolean usingBufferStorage = glProxy.getGpuUploadMethod() == GpuUploadMethod.BUFFER_STORAGE;
|
||||
|
||||
// where the center of the buffers is (needed when culling regions)
|
||||
// render each of the buffers
|
||||
for (int x = 0; x < vbos.length; x++)
|
||||
{
|
||||
for (int z = 0; z < vbos.length; z++)
|
||||
{
|
||||
//int tempX = LodUtil.convertLevelPos(x + vbosCenterX - (lodDim.getWidth() / 2), LodUtil.REGION_DETAIL_LEVEL , LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
//int tempY = LodUtil.convertLevelPos(z + vbosCenterZ - (lodDim.getWidth() / 2), LodUtil.REGION_DETAIL_LEVEL, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
int lowRegionX = vbos.getCenterX() - vbos.gridCentreToEdge;
|
||||
int lowRegionZ = vbos.getCenterY() - vbos.gridCentreToEdge;
|
||||
int drawCall = 0;
|
||||
for (int regionX=lowRegionX; regionX<vbos.gridSize; regionX++) {
|
||||
for (int regionZ=lowRegionZ; regionZ<vbos.gridSize; regionZ++) {
|
||||
if (vbos.get(regionX, regionZ) == null) continue;
|
||||
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(),
|
||||
MC_RENDER.getLookAtVector(),
|
||||
vbosCenterX + LodUtil.convertLevelPos(x - (lodDim.getWidth() / 2), LodUtil.REGION_DETAIL_LEVEL , LodUtil.BLOCK_DETAIL_LEVEL),
|
||||
vbosCenterZ + LodUtil.convertLevelPos(z - (lodDim.getWidth() / 2), LodUtil.REGION_DETAIL_LEVEL , LodUtil.BLOCK_DETAIL_LEVEL)))
|
||||
{
|
||||
|
||||
// actual rendering
|
||||
int bufferId = 0;
|
||||
for (int i = 0; i < vbos[x][z].length; i++)
|
||||
{
|
||||
bufferId = (storageBufferIds != null && usingBufferStorage) ? storageBufferIds[x][z][i] : vbos[x][z][i].id;
|
||||
if (bufferId==0) continue;
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, bufferId);
|
||||
shaderProgram.bindVertexBuffer(bufferId);
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, vbos[x][z][i].vertexCount);
|
||||
MC_RENDER.getLookAtVector(), regionX, regionZ)) {
|
||||
for (LodVertexBuffer vbo : vbos.get(regionX, regionZ)) {
|
||||
if (vbo == null) continue;
|
||||
if (vbo.vertexCount == 0) continue;
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, vbo.id);
|
||||
shaderProgram.bindVertexBuffer(vbo.id);
|
||||
drawCall++;
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, vbo.vertexCount);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//if (drawCall!=0)
|
||||
// ClientApi.LOGGER.info("DrawCall Count: "+drawCall);
|
||||
|
||||
//================//
|
||||
// render cleanup //
|
||||
//================//
|
||||
|
||||
draw.end("LodDraw");
|
||||
profiler.popPush("LOD cleanup");
|
||||
LagSpikeCatcher drawCleanup = new LagSpikeCatcher();
|
||||
|
||||
GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
@@ -340,7 +353,7 @@ public class LodRenderer
|
||||
|
||||
// clear the depth buffer so everything is drawn over the LODs
|
||||
GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
drawCleanup.end("LodDrawCleanup");
|
||||
// end of internal LOD profiling
|
||||
profiler.pop();
|
||||
}
|
||||
@@ -367,7 +380,7 @@ public class LodRenderer
|
||||
/** Create all buffers that will be used. */
|
||||
public void setupBuffers(LodDimension lodDim)
|
||||
{
|
||||
lodBufferBuilderFactory.setupBuffers(lodDim);
|
||||
lodBufferBuilderFactory.allBuffersRequireReset = true;
|
||||
}
|
||||
|
||||
private Color getFogColor(float partialTicks)
|
||||
@@ -393,7 +406,7 @@ public class LodRenderer
|
||||
* (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
|
||||
* accuracy vs the model view matrix, which only uses floats)
|
||||
*/
|
||||
private Mat4f translateModelViewMatrix(Mat4f mcModelViewMatrix, float partialTicks)
|
||||
private Mat4f translateModelViewMatrix(Mat4f mcModelViewMatrix, float partialTicks, int vbosCenterX, int vbosCenterZ)
|
||||
{
|
||||
// get all relevant camera info
|
||||
Vec3d projectedView = MC_RENDER.getCameraExactPosition();
|
||||
@@ -416,7 +429,7 @@ public class LodRenderer
|
||||
* Similar to translateModelViewMatrix (above),
|
||||
* but for the camera position
|
||||
*/
|
||||
private Vec3f getTranslatedCameraPos()
|
||||
private Vec3f getTranslatedCameraPos(int vbosCenterX, int vbosCenterZ)
|
||||
{
|
||||
//int worldCenterX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, vbosCenterX, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
//int worldCenterZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, vbosCenterZ, LodUtil.BLOCK_DETAIL_LEVEL);
|
||||
@@ -484,138 +497,127 @@ public class LodRenderer
|
||||
fullRegen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the current Vertex Buffers with the newly
|
||||
* created buffers from the lodBufferBuilder. <br><br>
|
||||
* <p>
|
||||
* For some reason this has to be called after the frame has been rendered,
|
||||
* otherwise visual stuttering/rubber banding may happen. I'm not sure why...
|
||||
*/
|
||||
private void swapBuffers()
|
||||
{
|
||||
// replace the drawable buffers with
|
||||
// the newly created buffers from the lodBufferBuilder
|
||||
VertexBuffersAndOffset result = lodBufferBuilderFactory.getVertexBuffers();
|
||||
vbos = result.vbos;
|
||||
storageBufferIds = result.storageBufferIds;
|
||||
vbosCenterX = result.drawableCenterBlockPosX;
|
||||
vbosCenterZ = result.drawableCenterBlockPosZ;
|
||||
// returns whether anything changed
|
||||
private boolean updateVanillaRenderedChunks(LodDimension lodDim, boolean recreateChunks) {
|
||||
short chunkRenderDistance = (short) MC_RENDER.getRenderDistance();
|
||||
int chunkX = Math.floorDiv(previousPos.getX(), 16);
|
||||
int chunkZ = Math.floorDiv(previousPos.getZ(), 16);
|
||||
// if the player is high enough, draw all LODs
|
||||
if (previousPos.getY() > 256) {
|
||||
vanillaRenderedChunks = new VanillaRenderedChunksList(
|
||||
chunkRenderDistance, chunkX, chunkZ);
|
||||
return true;
|
||||
}
|
||||
VanillaRenderedChunksList chunkList;
|
||||
|
||||
if (recreateChunks) {
|
||||
vanillaRenderedChunks = new VanillaRenderedChunksList(chunkRenderDistance, chunkX, chunkZ);
|
||||
return true;
|
||||
} else {
|
||||
chunkList = vanillaRenderedChunks;
|
||||
chunkX = chunkList.centerX;
|
||||
chunkZ = chunkList.centerZ;
|
||||
chunkRenderDistance = (short) vanillaRenderedChunks.gridCentreToEdge;
|
||||
}
|
||||
|
||||
boolean anyChanged = false;
|
||||
LagSpikeCatcher getChunks = new LagSpikeCatcher();
|
||||
Set<AbstractChunkPosWrapper> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, previousPos);
|
||||
getChunks.end("LodDrawSetup:UpdateStatus:UpdateVanillaChunks:getChunks");
|
||||
for (AbstractChunkPosWrapper pos : chunkPosToSkip)
|
||||
{
|
||||
int xIndex = (pos.getX() - chunkX) + (chunkRenderDistance + 1);
|
||||
int zIndex = (pos.getZ() - chunkZ) + (chunkRenderDistance + 1);
|
||||
|
||||
// sometimes we are given chunks that are outside the render distance,
|
||||
// This prevents index out of bounds exceptions
|
||||
if (xIndex >= 0 && zIndex >= 0
|
||||
&& xIndex < vanillaRenderedChunks.gridSize
|
||||
&& zIndex < vanillaRenderedChunks.gridSize)
|
||||
{
|
||||
if (!chunkList.get(chunkList.calculateOffset(xIndex, zIndex)))
|
||||
{
|
||||
chunkList.set(chunkList.calculateOffset(xIndex, zIndex), true);
|
||||
anyChanged = true;
|
||||
lodDim.markRegionBufferToRegen(pos.getRegionX(), pos.getRegionZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
vanillaRenderedChunks = chunkList;
|
||||
return anyChanged;
|
||||
}
|
||||
|
||||
/** Determines if the LODs should have a fullRegen or partialRegen */
|
||||
private void determineIfLodsShouldRegenerate(LodDimension lodDim, float partialTicks)
|
||||
{
|
||||
private void updateRegenStatus(LodDimension lodDim, float partialTicks) {
|
||||
short chunkRenderDistance = (short) MC_RENDER.getRenderDistance();
|
||||
int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2;
|
||||
|
||||
//=============//
|
||||
// full regens //
|
||||
//=============//
|
||||
|
||||
// check if the view distance changed
|
||||
long newTime = System.currentTimeMillis();
|
||||
AbstractBlockPosWrapper newPos = MC.getPlayerBlockPos();
|
||||
boolean shouldUpdateChunks = false;
|
||||
boolean posUpdated = false;
|
||||
boolean tryPartialGen = false;
|
||||
boolean tryFullGen = false;
|
||||
|
||||
// check if the view distance or config changed
|
||||
if (ApiShared.previousLodRenderDistance != CONFIG.client().graphics().quality().getLodChunkRenderDistance()
|
||||
|| chunkRenderDistance != prevRenderDistance
|
||||
|| prevFogDistance != CONFIG.client().graphics().fogQuality().getFogDistance())
|
||||
{
|
||||
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
DetailDistanceUtil.updateSettings();
|
||||
fullRegen = true;
|
||||
previousPos = LevelPosUtil.createLevelPos((byte) 4, MC.getPlayerChunkPos().getZ(), MC.getPlayerChunkPos().getZ());
|
||||
DetailDistanceUtil.updateSettings(); // FIXME: This should NOT be here!
|
||||
prevFogDistance = CONFIG.client().graphics().fogQuality().getFogDistance();
|
||||
prevRenderDistance = chunkRenderDistance;
|
||||
}
|
||||
|
||||
// did the user change the debug setting?
|
||||
if (CONFIG.client().advanced().debugging().getDebugMode() != previousDebugMode)
|
||||
{
|
||||
tryFullGen = true;
|
||||
} else if (CONFIG.client().advanced().debugging().getDebugMode() != previousDebugMode)
|
||||
{ // did the user change the debug setting?
|
||||
previousDebugMode = CONFIG.client().advanced().debugging().getDebugMode();
|
||||
fullRegen = true;
|
||||
tryFullGen = true;
|
||||
}
|
||||
|
||||
|
||||
long newTime = System.currentTimeMillis();
|
||||
|
||||
// check if the player has moved
|
||||
if (newTime - prevPlayerPosTime > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveTimeout)
|
||||
{
|
||||
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|
||||
|| Math.abs(MC.getPlayerChunkPos().getX() - LevelPosUtil.getPosX(previousPos)) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance
|
||||
|| Math.abs(MC.getPlayerChunkPos().getZ() - LevelPosUtil.getPosZ(previousPos)) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance)
|
||||
if (newTime - prevPlayerPosTime > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveTimeout) {
|
||||
if (previousPos == null
|
||||
|| Math.abs(newPos.getX() - previousPos.getX()) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance*16
|
||||
|| Math.abs(newPos.getZ() - previousPos.getZ()) > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveDistance*16)
|
||||
{
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
fullRegen = true;
|
||||
previousPos = LevelPosUtil.createLevelPos((byte) 4, MC.getPlayerChunkPos().getX(), MC.getPlayerChunkPos().getZ());
|
||||
tryPartialGen = true;
|
||||
previousPos = newPos;
|
||||
posUpdated = true;
|
||||
}
|
||||
prevPlayerPosTime = newTime;
|
||||
}
|
||||
|
||||
//================//
|
||||
// partial regens //
|
||||
//================//
|
||||
|
||||
|
||||
// check if the vanilla rendered chunks changed
|
||||
if (newTime - prevVanillaChunkTime > CONFIG.client().advanced().buffers().getRebuildTimes().renderedChunkTimeout)
|
||||
{
|
||||
if (vanillaRenderedChunksChanged)
|
||||
{
|
||||
partialRegen = true;
|
||||
vanillaRenderedChunksChanged = false;
|
||||
}
|
||||
shouldUpdateChunks = true;
|
||||
prevVanillaChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
// check if there is any newly generated terrain to show
|
||||
if (newTime - prevChunkTime > CONFIG.client().advanced().buffers().getRebuildTimes().chunkChangeTimeout)
|
||||
{
|
||||
tryPartialGen = true;
|
||||
prevChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
if (tryFullGen && !posUpdated) {
|
||||
previousPos = newPos;
|
||||
posUpdated = true;
|
||||
}
|
||||
shouldUpdateChunks |= posUpdated;
|
||||
if (shouldUpdateChunks) {
|
||||
tryPartialGen |= updateVanillaRenderedChunks(lodDim, posUpdated);
|
||||
}
|
||||
|
||||
if (tryFullGen) {
|
||||
fullRegen = true;
|
||||
lodDim.regenDimensionBuffers = false;
|
||||
} else if (tryPartialGen) {
|
||||
if (lodDim.regenDimensionBuffers)
|
||||
{
|
||||
partialRegen = true;
|
||||
lodDim.regenDimensionBuffers = false;
|
||||
}
|
||||
prevChunkTime = newTime;
|
||||
}
|
||||
|
||||
//==============//
|
||||
// LOD skipping //
|
||||
//==============//
|
||||
|
||||
// determine which LODs should not be rendered close to the player
|
||||
HashSet<AbstractChunkPosWrapper> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, MC.getPlayerBlockPos());
|
||||
int xIndex;
|
||||
int zIndex;
|
||||
for (AbstractChunkPosWrapper pos : chunkPosToSkip)
|
||||
{
|
||||
vanillaRenderedChunksEmptySkip = false;
|
||||
|
||||
xIndex = (pos.getX() - MC.getPlayerChunkPos().getX()) + (chunkRenderDistance + 1);
|
||||
zIndex = (pos.getZ() - MC.getPlayerChunkPos().getZ()) + (chunkRenderDistance + 1);
|
||||
|
||||
// sometimes we are given chunks that are outside the render distance,
|
||||
// This prevents index out of bounds exceptions
|
||||
if (xIndex >= 0 && zIndex >= 0
|
||||
&& xIndex < vanillaRenderedChunks.length
|
||||
&& zIndex < vanillaRenderedChunks.length)
|
||||
{
|
||||
if (!vanillaRenderedChunks[xIndex][zIndex])
|
||||
{
|
||||
vanillaRenderedChunks[xIndex][zIndex] = true;
|
||||
vanillaRenderedChunksChanged = true;
|
||||
lodDim.markRegionBufferToRegen(pos.getRegionX(), pos.getRegionZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if the player is high enough, draw all LODs
|
||||
if (chunkPosToSkip.isEmpty() && MC.getPlayerBlockPos().getY() > 256 && !vanillaRenderedChunksEmptySkip)
|
||||
{
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
vanillaRenderedChunksChanged = true;
|
||||
vanillaRenderedChunksEmptySkip = true;
|
||||
}
|
||||
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -85,23 +85,20 @@ public class RenderUtil
|
||||
* Returns true if one of the region's 4 corners is in front
|
||||
* of the camera.
|
||||
*/
|
||||
public static boolean isRegionInViewFrustum(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, int vboCenterPosX, int vboCenterPosZ)
|
||||
public static boolean isRegionInViewFrustum(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, int vboRegionX, int vboRegionZ)
|
||||
{
|
||||
// convert the vbo position into a direction vector
|
||||
// starting from the player's position
|
||||
Vec3f vboVec = new Vec3f(vboCenterPosX, 0, vboCenterPosZ);
|
||||
Vec3f vboVec = new Vec3f(vboRegionX * LodUtil.REGION_WIDTH, 0, vboRegionZ * LodUtil.REGION_WIDTH);
|
||||
Vec3f playerVec = new Vec3f(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ());
|
||||
|
||||
vboVec.subtract(playerVec);
|
||||
|
||||
|
||||
int halfRegionWidth = LodUtil.REGION_WIDTH / 2;
|
||||
|
||||
// calculate the 4 corners
|
||||
Vec3f vboSeVec = new Vec3f(vboVec.x + halfRegionWidth, vboVec.y, vboVec.z + halfRegionWidth);
|
||||
Vec3f vboSwVec = new Vec3f(vboVec.x - halfRegionWidth, vboVec.y, vboVec.z + halfRegionWidth);
|
||||
Vec3f vboNwVec = new Vec3f(vboVec.x - halfRegionWidth, vboVec.y, vboVec.z - halfRegionWidth);
|
||||
Vec3f vboNeVec = new Vec3f(vboVec.x + halfRegionWidth, vboVec.y, vboVec.z - halfRegionWidth);
|
||||
Vec3f vboSeVec = new Vec3f(vboVec.x + LodUtil.REGION_WIDTH, vboVec.y, vboVec.z + LodUtil.REGION_WIDTH);
|
||||
Vec3f vboSwVec = new Vec3f(vboVec.x , vboVec.y, vboVec.z + LodUtil.REGION_WIDTH);
|
||||
Vec3f vboNwVec = new Vec3f(vboVec.x , vboVec.y, vboVec.z);
|
||||
Vec3f vboNeVec = new Vec3f(vboVec.x + LodUtil.REGION_WIDTH, vboVec.y, vboVec.z);
|
||||
|
||||
// if any corner is visible, this region should be rendered
|
||||
return isNormalizedVectorInViewFrustum(vboSeVec, cameraDir) ||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.seibel.lod.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GridList<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
public static class Pos {
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public Pos(int xx, int yy) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
}
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1585978374811888116L;
|
||||
public final int gridCentreToEdge;
|
||||
public final int gridSize;
|
||||
|
||||
public GridList(int gridCentreToEdge) {
|
||||
super((gridCentreToEdge * 2 + 1) * (gridCentreToEdge * 2 + 1));
|
||||
gridSize = gridCentreToEdge * 2 + 1;
|
||||
this.gridCentreToEdge = gridCentreToEdge;
|
||||
}
|
||||
|
||||
public final T getOffsetOf(int index, int x, int y) {
|
||||
return get(index + x + y * gridSize);
|
||||
}
|
||||
|
||||
public final int offsetOf(int index, int x, int y) {
|
||||
return index + x + y * gridSize;
|
||||
}
|
||||
|
||||
public final Pos posOf(int index) {
|
||||
return new Pos(index % gridSize, index / gridSize);
|
||||
}
|
||||
|
||||
public final int calculateOffset(int x, int y) {
|
||||
return x + y * gridSize;
|
||||
}
|
||||
|
||||
public final GridList<T> subGrid(int gridCentreToEdge) {
|
||||
int centreIndex = size() / 2;
|
||||
GridList<T> subGrid = new GridList<T>(gridCentreToEdge);
|
||||
for (int oy = -gridCentreToEdge; oy <= gridCentreToEdge; oy++) {
|
||||
int begin = offsetOf(centreIndex, -gridCentreToEdge, oy);
|
||||
int end = offsetOf(centreIndex, gridCentreToEdge, oy);
|
||||
subGrid.addAll(this.subList(begin, end + 1));
|
||||
}
|
||||
// System.out.println("========================================\n"+
|
||||
// this.toDetailString() + "\nTOOOOOOOOOOOOO\n"+subGrid.toDetailString()+
|
||||
// "==========================================\n");
|
||||
return subGrid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GridList " + gridSize + "*" + gridSize + "[" + size() + "]";
|
||||
}
|
||||
|
||||
public String toDetailString() {
|
||||
StringBuilder str = new StringBuilder("\n");
|
||||
int i = 0;
|
||||
for (T t : this) {
|
||||
str.append(t.toString());
|
||||
str.append(", ");
|
||||
i++;
|
||||
if (i % gridSize == 0) {
|
||||
str.append("\n");
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
@@ -181,12 +181,6 @@ public class LodUtil
|
||||
return new RegionPos(relativePosX, relativePosZ);
|
||||
}
|
||||
|
||||
/** Convert a 2D absolute position into a quad tree relative position. */
|
||||
public static int convertLevelPos(int pos, int currentDetailLevel, int targetDetailLevel)
|
||||
{
|
||||
return pos / (1 << (targetDetailLevel - currentDetailLevel));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If on single player this will return the name of the user's
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.seibel.lod.core.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/*Layout:
|
||||
* 0,1,2,
|
||||
* 3,4,5,
|
||||
* 6,7,8
|
||||
*/
|
||||
|
||||
public class MovableGridList<T> extends ArrayList<T> implements List<T> {
|
||||
|
||||
private static final long serialVersionUID = 5366261085254591277L;
|
||||
|
||||
public static class Pos {
|
||||
public int x;
|
||||
public int y;
|
||||
|
||||
public Pos(int xx, int yy) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
}
|
||||
}
|
||||
|
||||
private int centerX;
|
||||
private int centerY;
|
||||
|
||||
public final int gridCentreToEdge;
|
||||
public final int gridSize;
|
||||
|
||||
public MovableGridList(int gridCentreToEdge, int centerX, int centerY) {
|
||||
super((gridCentreToEdge * 2 + 1) * (gridCentreToEdge * 2 + 1));
|
||||
gridSize = gridCentreToEdge * 2 + 1;
|
||||
this.gridCentreToEdge = gridCentreToEdge;
|
||||
this.centerX = centerX;
|
||||
this.centerY = centerY;
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
super.clear();
|
||||
super.ensureCapacity(gridSize*gridSize);
|
||||
for (int i=0; i<gridSize*gridSize; i++) {
|
||||
super.add(null);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCenterX() {return centerX;}
|
||||
public int getCenterY() {return centerY;}
|
||||
|
||||
// return null if x,y is outside of the grid
|
||||
public T get(int x, int y) {
|
||||
x = x-centerX+gridCentreToEdge;
|
||||
y = y-centerY+gridCentreToEdge;
|
||||
return _getDirect(x,y);
|
||||
}
|
||||
|
||||
// return null if x,y is outside of the grid
|
||||
public T setAndGet(int x, int y, T t) {
|
||||
x = x-centerX+gridCentreToEdge;
|
||||
y = y-centerY+gridCentreToEdge;
|
||||
return _setDirect(x,y, t) ? t : null;
|
||||
}
|
||||
|
||||
private final T _getDirect(int x, int y) {
|
||||
if (x<0 || x>=gridSize || y<0 || y>=gridSize) return null;
|
||||
return super.get(x + y * gridSize);
|
||||
}
|
||||
private final boolean _setDirect(int x, int y, T t) {
|
||||
if (x<0 || x>=gridSize || y<0 || y>=gridSize) return false;
|
||||
super.set(x + y * gridSize, t);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void move(int newCenterX, int newCenterY) {
|
||||
if (centerX == newCenterX && centerY == newCenterY) return;
|
||||
int deltaX = newCenterX - centerX;
|
||||
int deltaY = newCenterY - centerY;
|
||||
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total width, just delete the current data
|
||||
// and update the centerX and/or centerZ
|
||||
if (Math.abs(deltaX) >= gridSize || Math.abs(deltaY) >= gridSize)
|
||||
{
|
||||
clear();
|
||||
// update the new center
|
||||
centerX = newCenterX;
|
||||
centerY = newCenterY;
|
||||
return;
|
||||
}
|
||||
|
||||
// X
|
||||
if (deltaX >= 0 && deltaY >= 0)
|
||||
{
|
||||
// move everything over to the left-up (as the center moves to the right-down)
|
||||
for (int x = 0; x < gridSize; x++)
|
||||
{
|
||||
for (int y = 0; y < gridSize; y++)
|
||||
{
|
||||
_setDirect(x, y, _getDirect(x+deltaX, y+deltaY));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (deltaX < 0 && deltaY >= 0)
|
||||
{
|
||||
// move everything over to the right-up (as the center moves to the left-down)
|
||||
for (int x = gridSize - 1; x >= 0; x--)
|
||||
{
|
||||
for (int y = 0; y < gridSize; y++)
|
||||
{
|
||||
_setDirect(x, y, _getDirect(x+deltaX, y+deltaY));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (deltaX >= 0 && deltaY < 0)
|
||||
{
|
||||
// move everything over to the left-down (as the center moves to the right-up)
|
||||
for (int x = 0; x < gridSize; x++)
|
||||
{
|
||||
for (int y = gridSize - 1; y >= 0; y--)
|
||||
{
|
||||
_setDirect(x, y, _getDirect(x+deltaX, y+deltaY));
|
||||
}
|
||||
}
|
||||
}
|
||||
else //if (deltaX < 0 && deltaY < 0)
|
||||
{
|
||||
// move everything over to the right-down (as the center moves to the left-up)
|
||||
for (int x = gridSize - 1; x >= 0; x--)
|
||||
{
|
||||
for (int y = gridSize - 1; y >= 0; y--)
|
||||
{
|
||||
_setDirect(x, y, _getDirect(x+deltaX, y+deltaY));
|
||||
}
|
||||
}
|
||||
}
|
||||
centerX = newCenterX;
|
||||
centerY = newCenterY;
|
||||
}
|
||||
|
||||
|
||||
// TODO: This is unused but may be useful later on.
|
||||
/*
|
||||
public final MovableGridList<T> subGrid(int gridCentreToEdge, int newCenterX, int newCenterY) {
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MovableGridList[" + centerX + "," + centerY + "] " + gridSize + "*" + gridSize + "[" + size() + "]";
|
||||
}
|
||||
|
||||
public String toDetailString() {
|
||||
StringBuilder str = new StringBuilder("\n");
|
||||
int i = 0;
|
||||
for (T t : this) {
|
||||
|
||||
str.append(t!=null ? t.toString() : "NULL");
|
||||
str.append(", ");
|
||||
i++;
|
||||
if (i % gridSize == 0) {
|
||||
str.append("\n");
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user