diff --git a/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java b/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java
index a45cf80ea..724a5cabb 100644
--- a/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java
+++ b/src/main/java/com/backsun/lod/builders/LodBufferBuilder.java
@@ -1,52 +1,287 @@
package com.backsun.lod.builders;
import java.awt.Color;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import org.lwjgl.opengl.GL11;
-import com.backsun.lod.enums.FogDistance;
+import com.backsun.lod.enums.ColorDirection;
+import com.backsun.lod.enums.LodCorner;
+import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.NearFarBuffer;
import com.backsun.lod.renderer.LodRenderer;
+import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.math.ChunkPos;
+import net.minecraftforge.common.WorldWorkerManager;
/**
* This object is used to create NearFarBuffer objects.
*
* @author James Seibel
- * @version 02-27-2021
+ * @version 03-25-2021
*/
public class LodBufferBuilder
{
- public BufferBuilder nearBuffer;
- public BufferBuilder farBuffer;
- public FogDistance distanceMode;
- public AxisAlignedBB[][] lods;
- public Color[][] colors;
+ private Minecraft mc;
+
+ /** This holds the thread used to generate new LODs off the main thread. */
+ private ExecutorService genThread = Executors.newSingleThreadExecutor();
+
+ private LodBuilder lodBuilder;
+
+ /** The buffers that are used to create LODs using near fog */
+ public volatile BufferBuilder buildableNearBuffer;
+ /** The buffers that are used to create LODs using far fog */
+ public volatile BufferBuilder buildableFarBuffer;
+
+ /** if this is true the LOD buffers are currently being
+ * regenerated. */
+ public volatile boolean generatingBuffers = false;
+
+ /** if this is true new LOD buffers have been generated
+ * and are waiting to be swapped with the drawable buffers*/
+ private volatile boolean switchBuffers = false;
+
+ /** If this is greater than 0 no new chunk generation requests will be made
+ * this is to prevent chunks from being generated for a long time in an area
+ * the player is no longer in. */
+ public int numChunksWaitingToGen = 0;
+
+ /** how many chunks to generate outside of the player's
+ * view distance at one time. (or more specifically how
+ * many requests to make at one time) */
+ public int maxChunkGenRequests = 8;
-
- public LodBufferBuilder()
+ public LodBufferBuilder(LodBuilder newLodBuilder)
{
-
+ mc = Minecraft.getInstance();
+ lodBuilder = newLodBuilder;
}
- public NearFarBuffer createBuffers(
- BufferBuilder newNearBufferBuilder, BufferBuilder newFarBufferBuilder,
- FogDistance newDistanceMode,
- AxisAlignedBB[][] newLods, Color[][] newColors)
+
+
+ /**
+ * Create a thread to asynchronously generate LOD buffers
+ * centered around the given camera X and Z.
+ *
+ * This method will write to the drawableNearBuffers and drawableFarBuffers.
+ *
+ * After the buildable buffers have been generated they must be
+ * swapped with the drawable buffers in the LodRenderer to be drawn.
+ */
+ public void generateLodBuffersAsync(LodRenderer renderer,
+ double playerX, double playerZ, int numbChunksWide)
{
- nearBuffer = newNearBufferBuilder;
- farBuffer = newFarBufferBuilder;
- distanceMode = newDistanceMode;
- lods = newLods;
- colors = newColors;
+ // only allow one generation process to happen at a time
+ if (generatingBuffers)
+ return;
+
+ if (buildableNearBuffer == null || buildableFarBuffer == null)
+ throw new IllegalStateException("generateLodBuffersAsync was called before the buildableNearBuffer and buildableFarBuffer were created.");
- nearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
- farBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
+
+ generatingBuffers = true;
+
+ // this is where we store the points for each LOD object
+ AxisAlignedBB lodArray[][] = new AxisAlignedBB[numbChunksWide][numbChunksWide];
+ // this is where we store the color for each LOD object
+ Color colorArray[][] = new Color[numbChunksWide][numbChunksWide];
+
+ int alpha = 255; // 0 - 255
+ Color red = new Color(255, 0, 0, alpha);
+ Color black = new Color(0, 0, 0, alpha);
+ Color white = new Color(255, 255, 255, alpha);
+
+ // this seemingly useless math is required,
+ // just using (int) playerX/Z doesn't work
+ int playerXChunkOffset = ((int) playerX / LodChunk.WIDTH) * LodChunk.WIDTH;
+ int playerZChunkOffset = ((int) playerZ / LodChunk.WIDTH) * LodChunk.WIDTH;
+ // this is where we will start drawing squares
+ // (exactly half the total width)
+ int startX = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerXChunkOffset;
+ int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset;
+
+
+
+ Thread t = new Thread(()->
+ {
+ // index of the chunk currently being added to the
+ // generation list
+ int chunkGenIndex = 0;
+
+ ChunkPos[] chunksToGen = new ChunkPos[maxChunkGenRequests];
+ int minChunkDist = Integer.MAX_VALUE;
+ ChunkPos playerChunkPos = new ChunkPos((int)playerX / LodChunk.WIDTH, (int)playerZ / LodChunk.WIDTH);
+
+
+
+ // x axis
+ for (int i = 0; i < numbChunksWide; i++)
+ {
+ // z axis
+ for (int j = 0; j < numbChunksWide; j++)
+ {
+ // skip the middle
+ // (As the player moves some chunks will overlap or be missing,
+ // this is just how chunk loading/unloading works. This can hopefully
+ // be hidden with careful use of fog)
+ int middle = (numbChunksWide / 2);
+ if (isCoordInCenterArea(i, j, middle))
+ {
+ continue;
+ }
+
+
+ // set where this square will be drawn in the world
+ double xOffset = (LodChunk.WIDTH * i) + // offset by the number of LOD blocks
+ startX; // offset so the center LOD block is centered underneath the player
+ double yOffset = 0;
+ double zOffset = (LodChunk.WIDTH * j) + startZ;
+
+ int chunkX = i + (startX / LodChunk.WIDTH);
+ int chunkZ = j + (startZ / LodChunk.WIDTH);
+
+ LodChunk lod = renderer.lodDimension.getLodFromCoordinates(chunkX, chunkZ);
+ if (lod == null || lod.isLodEmpty())
+ {
+ // note: for some reason if any color or lod objects are set here
+ // it causes the game to use 100% gpu;
+ // undefined in the debug menu
+ // and drop to ~6 fps.
+ colorArray[i][j] = null;
+ lodArray[i][j] = null;
+
+
+ // only generate a new chunk if no chunk currently exists
+ // and we aren't waiting on any other chunks to generate
+ if (lod == null && numChunksWaitingToGen == 0)
+ {
+ ChunkPos pos = new ChunkPos(chunkX, chunkZ);
+
+ // determine if this position is closer to the player
+ // than the previous
+ int newDistance = playerChunkPos.getChessboardDistance(pos);
+
+ if (newDistance < minChunkDist)
+ {
+ // this chunk is closer, clear any previous
+ // positions and update the new minimum distance
+ minChunkDist = newDistance;
+
+ chunkGenIndex = 0;
+ chunksToGen = new ChunkPos[maxChunkGenRequests];
+ chunksToGen[chunkGenIndex] = pos;
+ chunkGenIndex++;
+ }
+ else if (newDistance <= minChunkDist)
+ {
+ // this chunk position is as close or closers than the
+ // minimum distance
+ if(chunkGenIndex < maxChunkGenRequests)
+ {
+ // we are still under the number of chunks to generate
+ // add this pos to the list
+ chunksToGen[chunkGenIndex] = pos;
+ chunkGenIndex++;
+ }
+ }
+ }
+
+ continue;
+ }
+
+
+ Color c = new Color(
+ (lod.colors[ColorDirection.TOP.value].getRed()),
+ (lod.colors[ColorDirection.TOP.value].getGreen()),
+ (lod.colors[ColorDirection.TOP.value].getBlue()),
+ lod.colors[ColorDirection.TOP.value].getAlpha());
+
+ if (!renderer.debugging)
+ {
+ // add the color to the array
+ colorArray[i][j] = c;
+ }
+ else
+ {
+ // if debugging draw the squares as a black and white checker board
+ if ((chunkX + chunkZ) % 2 == 0)
+ c = white;
+ else
+ c = black;
+ // draw the first square as red
+ if (i == 0 && j == 0)
+ c = red;
+
+ colorArray[i][j] = c;
+ }
+
+
+ // add the new box to the array
+ int topPoint = getValidHeightPoint(lod.top);
+ int bottomPoint = getValidHeightPoint(lod.bottom);
+
+ // don't draw an LOD if it is empty
+ if (topPoint == -1 && bottomPoint == -1)
+ continue;
+
+ lodArray[i][j] = new AxisAlignedBB(0, bottomPoint, 0, LodChunk.WIDTH, topPoint, LodChunk.WIDTH).offset(xOffset, yOffset, zOffset);
+ }
+ }
+
+ // start chunk generation
+ for(ChunkPos chunkPos : chunksToGen)
+ {
+ if(chunkPos == null)
+ break;
+
+ // add a placeholder chunk to prevent this chunk from
+ // being generated again
+ LodChunk placeholder = new LodChunk();
+ placeholder.x = chunkPos.x;
+ placeholder.z = chunkPos.z;
+ renderer.lodDimension.addLod(placeholder);
+
+ numChunksWaitingToGen++;
+
+ LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, renderer, lodBuilder, this, renderer.lodDimension);
+ WorldWorkerManager.addWorker(genWorker);
+ }
+
+
+
+
+ // generate our new buildable buffers
+ buildBuffersFromAABB(lodArray, colorArray);
+
+ // mark that the buildable buffers as ready to swap
+ generatingBuffers = false;
+ switchBuffers = true;
+ });
+
+ genThread.execute(t);
+
+ return;
+ }
+
+
+ /**
+ * Build the buildable near and far buffers.
+ *
+ * @param lods
+ * @param colors
+ */
+ private void buildBuffersFromAABB(AxisAlignedBB[][] lods, Color[][] colors)
+ {
+ buildableNearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
+ buildableFarBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
int numbChunksWide = lods.length;
@@ -77,9 +312,9 @@ public class LodBufferBuilder
if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2))
- currentBuffer = nearBuffer;
+ currentBuffer = buildableNearBuffer;
else
- currentBuffer = farBuffer;
+ currentBuffer = buildableFarBuffer;
if (bb.minY != bb.maxY)
@@ -158,10 +393,8 @@ public class LodBufferBuilder
} // z axis
} // x axis
- nearBuffer.finishDrawing();
- farBuffer.finishDrawing();
-
- return new NearFarBuffer(nearBuffer, farBuffer);
+ buildableNearBuffer.finishDrawing();
+ buildableFarBuffer.finishDrawing();
}
private void addPosAndColor(BufferBuilder buffer, double x, double y, double z, int red, int green, int blue, int alpha)
@@ -171,6 +404,42 @@ public class LodBufferBuilder
+
+
+
+ //====================//
+ // generation helpers //
+ //====================//
+
+ /**
+ * Returns if the given coordinate is in the loaded area of the world.
+ * @param centerCoordinate the center of the loaded world
+ */
+ private boolean isCoordInCenterArea(int i, int j, int centerCoordinate)
+ {
+ return (i >= centerCoordinate - mc.gameSettings.renderDistanceChunks
+ && i <= centerCoordinate + mc.gameSettings.renderDistanceChunks)
+ &&
+ (j >= centerCoordinate - mc.gameSettings.renderDistanceChunks
+ && j <= centerCoordinate + mc.gameSettings.renderDistanceChunks);
+ }
+
+
+ /**
+ * @Returns -1 if there are no valid points
+ */
+ private int getValidHeightPoint(short[] heightPoints)
+ {
+ if (heightPoints[LodCorner.NE.value] != -1)
+ return heightPoints[LodCorner.NE.value];
+ if (heightPoints[LodCorner.NW.value] != -1)
+ return heightPoints[LodCorner.NW.value];
+ if (heightPoints[LodCorner.SE.value] != -1)
+ return heightPoints[LodCorner.NE.value];
+ return heightPoints[LodCorner.NE.value];
+ }
+
+
/**
* Find the coordinates that are in the center half of the given
* 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius).
@@ -186,4 +455,62 @@ public class LodBufferBuilder
&& chunkZ <= lodRadius + halfRadius);
}
+
+
+
+
+ //===============================//
+ // BufferBuilder related methods //
+ //===============================//
+
+
+ /**
+ * Called from the LodRenderer to create the
+ * BufferBuilders at the right size.
+ *
+ * @param bufferMaxCapacity
+ */
+ public void setupBuffers(int bufferMaxCapacity)
+ {
+ buildableNearBuffer = new BufferBuilder(bufferMaxCapacity);
+ buildableFarBuffer = new BufferBuilder(bufferMaxCapacity);
+ }
+
+ /**
+ * Swap the drawable and buildable buffers and return
+ * the old drawable buffers.
+ * @param drawableNearBuffer
+ * @param drawableFarBuffer
+ */
+ public NearFarBuffer swapBuffers(BufferBuilder drawableNearBuffer, BufferBuilder drawableFarBuffer)
+ {
+ // swap the BufferBuilders
+ BufferBuilder tmp = buildableNearBuffer;
+ buildableNearBuffer = drawableNearBuffer;
+ drawableNearBuffer = tmp;
+
+ tmp = buildableFarBuffer;
+ buildableFarBuffer = drawableFarBuffer;
+ drawableFarBuffer = tmp;
+
+
+ // the buffers have been swapped
+ switchBuffers = false;
+
+ return new NearFarBuffer(drawableNearBuffer, drawableFarBuffer);
+ }
+
+ /**
+ * If this is true the buildable near and far
+ * buffers have been generated and are ready to be
+ * sent to the LodRenderer.
+ */
+ public boolean newBuffersAvaliable()
+ {
+ return switchBuffers;
+ }
+
+
+
+
}
\ No newline at end of file
diff --git a/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java b/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java
index c21017df1..ffc20cdeb 100644
--- a/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java
+++ b/src/main/java/com/backsun/lod/builders/LodChunkGenWorker.java
@@ -34,7 +34,7 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker;
* This is used to generate a LodChunk at a given ChunkPos.
*
* @author James Seibel
- * @version 03-24-2021
+ * @version 03-25-2021
*/
public class LodChunkGenWorker implements IWorker
{
@@ -42,14 +42,16 @@ public class LodChunkGenWorker implements IWorker
private ChunkPos pos;
private LodDimension lodDim;
private LodBuilder lodBuilder;
+ private LodBufferBuilder lodBufferBuilder;
private LodRenderer lodRenderer;
- public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer, LodBuilder newLodBuilder, LodDimension newLodDimension)
+ public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer, LodBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder, LodDimension newLodDimension)
{
serverWorld = LodUtils.getServerWorldFromDimension(newLodDimension.dimension);
pos = newPos;
lodDim = newLodDimension;
lodBuilder = newLodBuilder;
+ lodBufferBuilder = newLodBufferBuilder;
lodRenderer = newLodRenderer;
}
@@ -91,6 +93,8 @@ public class LodChunkGenWorker implements IWorker
// System.out.println("Out of range " + x + " " + z);
//}
+ lodBufferBuilder.numChunksWaitingToGen--;
+
pos = null;
}
diff --git a/src/main/java/com/backsun/lod/objects/NearFarBuffer.java b/src/main/java/com/backsun/lod/objects/NearFarBuffer.java
index 060a53bb5..ab1773ad3 100644
--- a/src/main/java/com/backsun/lod/objects/NearFarBuffer.java
+++ b/src/main/java/com/backsun/lod/objects/NearFarBuffer.java
@@ -8,7 +8,7 @@ import net.minecraft.client.renderer.BufferBuilder;
* and BuildBufferThread.
*
* @author James Seibel
- * @version 02-21-2021
+ * @version 03-25-2021
*/
public class NearFarBuffer
{
@@ -16,7 +16,10 @@ public class NearFarBuffer
public BufferBuilder farBuffer;
-
+ /**
+ * @param newNearBuffer
+ * @param newFarBuffer
+ */
public NearFarBuffer(BufferBuilder newNearBuffer, BufferBuilder newFarBuffer)
{
nearBuffer = newNearBuffer;
diff --git a/src/main/java/com/backsun/lod/renderer/LodRenderer.java b/src/main/java/com/backsun/lod/renderer/LodRenderer.java
index 4395f6e2c..49f72970b 100644
--- a/src/main/java/com/backsun/lod/renderer/LodRenderer.java
+++ b/src/main/java/com/backsun/lod/renderer/LodRenderer.java
@@ -1,21 +1,15 @@
package com.backsun.lod.renderer;
-import java.awt.Color;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
import org.lwjgl.opengl.GL11;
import com.backsun.lod.builders.LodBufferBuilder;
import com.backsun.lod.builders.LodBuilder;
-import com.backsun.lod.builders.LodChunkGenWorker;
-import com.backsun.lod.enums.ColorDirection;
import com.backsun.lod.enums.FogDistance;
import com.backsun.lod.enums.FogQuality;
-import com.backsun.lod.enums.LodCorner;
import com.backsun.lod.handlers.ReflectionHandler;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
@@ -37,13 +31,10 @@ import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.potion.Effects;
import net.minecraft.profiler.IProfiler;
-import net.minecraft.util.math.AxisAlignedBB;
-import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
-import net.minecraftforge.common.WorldWorkerManager;
/**
@@ -51,7 +42,7 @@ import net.minecraftforge.common.WorldWorkerManager;
* This is where LODs are draw to the world.
*
* @author James Seibel
- * @version 03-19-2021
+ * @version 03-25-2021
*/
public class LodRenderer
{
@@ -72,31 +63,23 @@ public class LodRenderer
private ReflectionHandler reflectionHandler;
- public LodDimension lodDimension = null;
+ public LodDimension lodDimension;
/** This is used to generate the buildable buffers */
- private LodBufferBuilder lodBufferBuilder = null;
+ private LodBufferBuilder lodBufferBuilder;
/** The buffers that are used to draw LODs using near fog */
- private volatile BufferBuilder drawableNearBuffer = null;
+ private volatile BufferBuilder drawableNearBuffer;
/** The buffers that are used to draw LODs using far fog */
- private volatile BufferBuilder drawableFarBuffer = null;
-
- /** The buffers that are used to create LODs using near fog */
- private volatile BufferBuilder buildableNearBuffer = null;
- /** The buffers that are used to create LODs using far fog */
- private volatile BufferBuilder buildableFarBuffer = null;
+ private volatile BufferBuilder drawableFarBuffer;
/** This is the VertexBuffer used to draw any LODs that use near fog */
- private volatile VertexBuffer nearVbo = null;
+ private volatile VertexBuffer nearVbo;
/** This is the VertexBuffer used to draw any LODs that use far fog */
- private volatile VertexBuffer farVbo = null;
+ private volatile VertexBuffer farVbo;
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
- /** This holds the thread used to generate new LODs off the main thread. */
- private ExecutorService genThread = Executors.newSingleThreadExecutor();
-
/** This is used to determine if the LODs should be regenerated */
private int previousChunkRenderDistance = 0;
/** This is used to determine if the LODs should be regenerated */
@@ -109,14 +92,7 @@ public class LodRenderer
/** if this is true the LOD buffers should be regenerated,
* provided they aren't already being regenerated. */
private boolean regen = false;
- /** if this is true the LOD buffers are currently being
- * regenerated. */
- private volatile boolean regenerating = false;
- /** if this is true new LOD buffers have been generated
- * and are waiting to be swapped with the drawable buffers*/
- private volatile boolean switchBuffers = false;
- private LodBuilder lodBuilder;
@@ -127,7 +103,7 @@ public class LodRenderer
gameRender = mc.gameRenderer;
reflectionHandler = new ReflectionHandler();
- lodBuilder = newLodBuilder;
+ lodBufferBuilder = new LodBufferBuilder(newLodBuilder);
}
@@ -230,32 +206,28 @@ 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 (regen && !regenerating && !switchBuffers)
+ if (regen && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvaliable())
{
- regenerating = true;
-
- if (lodBufferBuilder == null)
- lodBufferBuilder = new LodBufferBuilder();
-
// this will mainly happen when the view distance is changed
if (drawableNearBuffer == null || drawableFarBuffer == null ||
previousChunkRenderDistance != mc.gameSettings.renderDistanceChunks)
setupBuffers(numbChunksWide);
// generate the LODs on a separate thread to prevent stuttering or freezing
- generateLodBuffersAsync(player.getPosX(), player.getPosZ(), numbChunksWide);
+ lodBufferBuilder.generateLodBuffersAsync(this, player.getPosX(), player.getPosZ(), numbChunksWide);
- // the regen process has been started
+ // the regen process has been started,
+ // it will be done when lodBufferBuilder.newBuffersAvaliable
+ // is true
regen = false;
}
// replace the buffers used to draw and build,
// this is only done when the createLodBufferGenerationThread
// has finished executing on a parallel thread.
- if (switchBuffers)
+ if (lodBufferBuilder.newBuffersAvaliable())
{
swapBuffers();
- switchBuffers = false;
}
@@ -325,28 +297,6 @@ public class LodRenderer
}
- /**
- * Create the model view matrix to move the LODs
- * from object space into world space.
- */
- private Matrix4f generateModelViewMatrix(float partialTicks)
- {
- // get all relevant camera info
- ActiveRenderInfo renderInfo = mc.gameRenderer.getActiveRenderInfo();
- Vector3d projectedView = renderInfo.getProjectedView();
-
-
- // generate the model view matrix
- MatrixStack matrixStack = new MatrixStack();
- matrixStack.push();
- // translate and rotate to the current camera location
- matrixStack.rotate(Vector3f.XP.rotationDegrees(renderInfo.getPitch()));
- matrixStack.rotate(Vector3f.YP.rotationDegrees(renderInfo.getYaw() + 180));
- matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z);
-
- return matrixStack.getLast().getMatrix();
- }
-
/**
* This is where the actual drawing happens.
@@ -440,6 +390,29 @@ public class LodRenderer
}
+ /**
+ * Create the model view matrix to move the LODs
+ * from object space into world space.
+ */
+ private Matrix4f generateModelViewMatrix(float partialTicks)
+ {
+ // get all relevant camera info
+ ActiveRenderInfo renderInfo = mc.gameRenderer.getActiveRenderInfo();
+ Vector3d projectedView = renderInfo.getProjectedView();
+
+
+ // generate the model view matrix
+ MatrixStack matrixStack = new MatrixStack();
+ matrixStack.push();
+ // translate and rotate to the current camera location
+ matrixStack.rotate(Vector3f.XP.rotationDegrees(renderInfo.getPitch()));
+ matrixStack.rotate(Vector3f.YP.rotationDegrees(renderInfo.getYaw() + 180));
+ matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z);
+
+ return matrixStack.getLast().getMatrix();
+ }
+
+
/**
* create a new projection matrix and send it over to the GPU
*
@@ -517,6 +490,7 @@ public class LodRenderer
RenderSystem.enableLighting();
}
+
/**
* Create all buffers that will be used.
*/
@@ -533,11 +507,11 @@ public class LodRenderer
drawableNearBuffer = new BufferBuilder(bufferMaxCapacity);
drawableFarBuffer = new BufferBuilder(bufferMaxCapacity);
- buildableNearBuffer = new BufferBuilder(bufferMaxCapacity);
- buildableFarBuffer = new BufferBuilder(bufferMaxCapacity);
+ lodBufferBuilder.setupBuffers(bufferMaxCapacity);
}
+
@@ -556,232 +530,18 @@ public class LodRenderer
regen = true;
}
- /**
- * @Returns -1 if there are no valid points
- */
- private int getValidHeightPoint(short[] heightPoints)
- {
- if (heightPoints[LodCorner.NE.value] != -1)
- return heightPoints[LodCorner.NE.value];
- if (heightPoints[LodCorner.NW.value] != -1)
- return heightPoints[LodCorner.NW.value];
- if (heightPoints[LodCorner.SE.value] != -1)
- return heightPoints[LodCorner.NE.value];
- return heightPoints[LodCorner.NE.value];
- }
-
/**
- * Create a thread to asynchronously generate LOD buffers
- * centered around the given camera X and Z.
- *
- * This thread will write to the drawableNearBuffers and drawableFarBuffers.
- *
- * After the buildable buffers have been generated they must be
- * swapped with the drawable buffers to be drawn.
- */
- private void generateLodBuffersAsync(double playerX, double playerZ,
- int numbChunksWide)
- {
- // this is where we store the points for each LOD object
- AxisAlignedBB lodArray[][] = new AxisAlignedBB[numbChunksWide][numbChunksWide];
- // this is where we store the color for each LOD object
- Color colorArray[][] = new Color[numbChunksWide][numbChunksWide];
-
- int alpha = 255; // 0 - 255
- Color red = new Color(255, 0, 0, alpha);
- Color black = new Color(0, 0, 0, alpha);
- Color white = new Color(255, 255, 255, alpha);
-
- // this seemingly useless math is required,
- // just using (int) playerX/Z doesn't work
- int playerXChunkOffset = ((int) playerX / LodChunk.WIDTH) * LodChunk.WIDTH;
- int playerZChunkOffset = ((int) playerZ / LodChunk.WIDTH) * LodChunk.WIDTH;
- // this is where we will start drawing squares
- // (exactly half the total width)
- int startX = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerXChunkOffset;
- int startZ = (-LodChunk.WIDTH * (numbChunksWide / 2)) + playerZChunkOffset;
-
- Thread t = new Thread(()->
- {
- // how many chunks to generate outside of the player's
- // view distance
- int maxNumbToGen = 8;
- int chunkGenIndex = 0;
-
- ChunkPos[] chunksToGen = new ChunkPos[maxNumbToGen];
- int minChunkDist = Integer.MAX_VALUE;
- ChunkPos playerChunkPos = new ChunkPos((int)playerX / LodChunk.WIDTH, (int)playerZ / LodChunk.WIDTH);
-
-
-
- // x axis
- for (int i = 0; i < numbChunksWide; i++)
- {
- // z axis
- for (int j = 0; j < numbChunksWide; j++)
- {
- // skip the middle
- // (As the player moves some chunks will overlap or be missing,
- // this is just how chunk loading/unloading works. This can hopefully
- // be hidden with careful use of fog)
- int middle = (numbChunksWide / 2);
- if (isCoordInCenterArea(i, j, middle))
- {
- continue;
- }
-
-
- // set where this square will be drawn in the world
- double xOffset = (LodChunk.WIDTH * i) + // offset by the number of LOD blocks
- startX; // offset so the center LOD block is centered underneath the player
- double yOffset = 0;
- double zOffset = (LodChunk.WIDTH * j) + startZ;
-
- int chunkX = i + (startX / LodChunk.WIDTH);
- int chunkZ = j + (startZ / LodChunk.WIDTH);
-
- LodChunk lod = lodDimension.getLodFromCoordinates(chunkX, chunkZ);
- if (lod == null || lod.isLodEmpty())
- {
- // note: for some reason if any color or lod objects are set here
- // it causes the game to use 100% gpu;
- // undefined in the debug menu
- // and drop to ~6 fps.
- colorArray[i][j] = null;
- lodArray[i][j] = null;
-
-
- if (lod == null)
- {
- ChunkPos pos = new ChunkPos(chunkX, chunkZ);
-
- // determine if this position is closer to the player
- // than the previous
- int newDistance = playerChunkPos.getChessboardDistance(pos);
-
- if (newDistance < minChunkDist)
- {
- // this chunk is closer, clear any previous
- // positions and update the new minimum distance
- minChunkDist = newDistance;
-
- chunkGenIndex = 0;
- chunksToGen = new ChunkPos[maxNumbToGen];
- chunksToGen[chunkGenIndex] = pos;
- chunkGenIndex++;
- }
- else if (newDistance <= minChunkDist)
- {
- // this chunk position is as close or closers than the
- // minimum distance
- if(chunkGenIndex < maxNumbToGen)
- {
- // we are still under the number of chunks to generate
- // add this pos to the list
- chunksToGen[chunkGenIndex] = pos;
- chunkGenIndex++;
- }
- }
- }
-
- continue;
- }
-
-
- Color c = new Color(
- (lod.colors[ColorDirection.TOP.value].getRed()),
- (lod.colors[ColorDirection.TOP.value].getGreen()),
- (lod.colors[ColorDirection.TOP.value].getBlue()),
- lod.colors[ColorDirection.TOP.value].getAlpha());
-
- if (!debugging)
- {
- // add the color to the array
- colorArray[i][j] = c;
- }
- else
- {
- // if debugging draw the squares as a black and white checker board
- if ((chunkX + chunkZ) % 2 == 0)
- c = white;
- else
- c = black;
- // draw the first square as red
- if (i == 0 && j == 0)
- c = red;
-
- colorArray[i][j] = c;
- }
-
-
- // add the new box to the array
- int topPoint = getValidHeightPoint(lod.top);
- int bottomPoint = getValidHeightPoint(lod.bottom);
-
- // don't draw an LOD if it is empty
- if (topPoint == -1 && bottomPoint == -1)
- continue;
-
- lodArray[i][j] = new AxisAlignedBB(0, bottomPoint, 0, LodChunk.WIDTH, topPoint, LodChunk.WIDTH).offset(xOffset, yOffset, zOffset);
- }
- }
-
- // start chunk generation
- for(ChunkPos chunkPos : chunksToGen)
- {
- if(chunkPos == null)
- break;
-
- // add a placeholder chunk to prevent this chunk from
- // being generated again
- LodChunk placeholder = new LodChunk();
- placeholder.x = chunkPos.x;
- placeholder.z = chunkPos.z;
- lodDimension.addLod(placeholder);
-
- LodChunkGenWorker genWorker = new LodChunkGenWorker(chunkPos, this, lodBuilder, lodDimension);
- WorldWorkerManager.addWorker(genWorker);
- }
-
-
-
-
-
- // generate our new buildable buffers
- NearFarBuffer nearFarBuffers = lodBufferBuilder.createBuffers(
- buildableNearBuffer, buildableFarBuffer,
- LodConfig.CLIENT.fogDistance.get(), lodArray, colorArray);
-
- // update our buffers
- buildableNearBuffer = nearFarBuffers.nearBuffer;
- buildableFarBuffer = nearFarBuffers.farBuffer;
-
- // mark that the buildable buffers as ready to swap
- regenerating = false;
- switchBuffers = true;
- });
-
- genThread.execute(t);
-
- return;
- }
-
-
-
- /**
- * Swap buildable and drawable buffers.
+ * Replace the current drawable buffers with the newly
+ * created buffers from the lodBufferBuilder.
*/
private void swapBuffers()
{
- // swap the BufferBuilders
- BufferBuilder tmp = buildableNearBuffer;
- buildableNearBuffer = drawableNearBuffer;
- drawableNearBuffer = tmp;
-
- tmp = buildableFarBuffer;
- buildableFarBuffer = drawableFarBuffer;
- drawableFarBuffer = tmp;
+ // replace the drawable buffers with
+ // the newly created buffers from the lodBufferBuilder
+ NearFarBuffer newBuffers = lodBufferBuilder.swapBuffers(drawableNearBuffer, drawableFarBuffer);
+ drawableNearBuffer = newBuffers.nearBuffer;
+ drawableFarBuffer = newBuffers.farBuffer;
// bind the buffers with their respective VBOs
@@ -800,21 +560,7 @@ public class LodRenderer
}
- /**
- * Returns if the given coordinate is in the loaded area of the world.
- * @param centerCoordinate the center of the loaded world
- */
- private boolean isCoordInCenterArea(int i, int j, int centerCoordinate)
- {
- return (i >= centerCoordinate - mc.gameSettings.renderDistanceChunks
- && i <= centerCoordinate + mc.gameSettings.renderDistanceChunks)
- &&
- (j >= centerCoordinate - mc.gameSettings.renderDistanceChunks
- && j <= centerCoordinate + mc.gameSettings.renderDistanceChunks);
- }
-
-
- public double getFov(float partialTicks, boolean useFovSetting)
+ private double getFov(float partialTicks, boolean useFovSetting)
{
return mc.gameRenderer.getFOVModifier(mc.gameRenderer.getActiveRenderInfo(), partialTicks, useFovSetting);
}