The lodQuadTree is now correctly converted to use LodDataPoint and DistanceGenerationMode
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
package com.seibel.lod;
|
||||
|
||||
import com.seibel.lod.render.LodNodeRenderer;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
@@ -42,9 +42,9 @@ public class LodMain
|
||||
|
||||
Thread setFancyFog = new Thread(() ->
|
||||
{
|
||||
LodRenderer.fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
|
||||
LodNodeRenderer.fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
|
||||
|
||||
if (!LodRenderer.fancyFogAvailable)
|
||||
if (!LodNodeRenderer.fancyFogAvailable)
|
||||
{
|
||||
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that fancy fog options will not be available.");
|
||||
}
|
||||
|
||||
+3
@@ -205,9 +205,12 @@ public class LodBufferBuilder
|
||||
|
||||
// get the desired LodTemplate and
|
||||
// add this LOD to the buffer
|
||||
/*
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
template.addLodToBuffer(currentBuffer, lodDim, lod,
|
||||
xOffset, yOffset, zOffset, renderer.debugging);
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package com.seibel.lod.builders;
|
||||
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.NearFarBuffer;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.*;
|
||||
import com.seibel.lod.render.LodNodeRenderer;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.biome.BiomeContainer;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.common.WorldWorkerManager;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -125,35 +126,117 @@ public class LodNodeBufferBuilder
|
||||
|
||||
|
||||
// generate our new buildable buffers
|
||||
buildableNearBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
buildableFarBuffer.begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
buildableNearBuffer.begin(GL11.GL_QUADS, LodNodeRenderer.LOD_VERTEX_FORMAT);
|
||||
buildableFarBuffer.begin(GL11.GL_QUADS, LodNodeRenderer.LOD_VERTEX_FORMAT);
|
||||
|
||||
// x axis
|
||||
for (int i = 0; i < numbChunksWide; i++)
|
||||
{
|
||||
// z axis
|
||||
for (int j = 0; j < numbChunksWide; j++)
|
||||
{
|
||||
int chunkX = i + (startX / LodChunk.WIDTH);
|
||||
int chunkZ = j + (startZ / LodChunk.WIDTH);
|
||||
|
||||
// skip any chunks that Minecraft is going to render
|
||||
if(isCoordInCenterArea(i, j, (numbChunksWide / 2))
|
||||
&& renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
List<LodQuadTreeNode> lodToRender = new ArrayList<>();
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int) playerX,(int)playerZ,(byte) 0, LodQuadTreeDimension.FULL_COMPLEXITY_MASK, 100000,0));
|
||||
/*
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int) playerX,(int)playerZ,(byte) 9, 100000,8000));
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int)playerX,(int)playerZ,(byte) 8, 8000,4000));
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int)playerX,(int)playerZ,(byte) 7, 4000,2000));
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int)playerX,(int)playerZ,(byte) 6, 2000,1000));
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int)playerX,(int)playerZ,(byte) 5, 1000,500));
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int)playerX,(int)playerZ,(byte) 4, 500,250));
|
||||
lodToRender.addAll(lodDim.getNodeToRender((int)playerX,(int)playerZ,(byte) 3, 250,0));
|
||||
*/
|
||||
for(LodQuadTreeNode data : lodToRender){
|
||||
BufferBuilder currentBuffer = null;
|
||||
/*
|
||||
if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2))
|
||||
currentBuffer = buildableNearBuffer;
|
||||
else
|
||||
currentBuffer = buildableFarBuffer;
|
||||
*/
|
||||
currentBuffer = buildableFarBuffer;
|
||||
// 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;
|
||||
|
||||
// get the desired LodTemplate and
|
||||
// add this LOD to the buffer
|
||||
LodQuadTreeNode lod = lodDim.getLodFromCoordinates(chunkX, chunkZ, (byte) 4);
|
||||
|
||||
if (lod == null || lod.getComplexity() == DistanceGenerationMode.NONE)
|
||||
{
|
||||
// generate a new chunk if no chunk currently exists
|
||||
// and we aren't waiting on any other chunks to generate
|
||||
if (lod == null && numberOfChunksWaitingToGenerate < maxChunkGenRequests)
|
||||
{
|
||||
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;
|
||||
chunksToGenReserve = chunksToGen;
|
||||
|
||||
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 position to the list
|
||||
chunksToGen[chunkGenIndex] = pos;
|
||||
chunkGenIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// don't render this null chunk
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
BufferBuilder currentBuffer = null;
|
||||
if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2))
|
||||
currentBuffer = buildableNearBuffer;
|
||||
else
|
||||
currentBuffer = buildableFarBuffer;
|
||||
|
||||
// get the desired LodTemplate and
|
||||
// add this LOD to the buffer
|
||||
LodConfig.CLIENT.lodTemplate.get().
|
||||
template.addLodToBuffer(currentBuffer, lodDim, lod,
|
||||
xOffset, yOffset, zOffset, renderer.debugging);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO add a way for a server side mod to generate chunks requested here
|
||||
if(mc.hasSingleplayerServer())
|
||||
{
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
// make sure we have as many chunks to generate as we are allowed
|
||||
if (chunkGenIndex < maxChunkGenRequests)
|
||||
{
|
||||
for(int i = chunkGenIndex, j = 0; i < maxChunkGenRequests; i++, j++)
|
||||
{
|
||||
chunksToGen[i] = chunksToGenReserve[j];
|
||||
}
|
||||
}
|
||||
|
||||
// start chunk generation
|
||||
for(ChunkPos chunkPos : chunksToGen)
|
||||
{
|
||||
if(chunkPos == null)
|
||||
break;
|
||||
|
||||
numberOfChunksWaitingToGenerate++;
|
||||
|
||||
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, renderer, lodChunkBuilder, this, lodDim, serverWorld, biomeContainer);
|
||||
WorldWorkerManager.addWorker(genWorker);
|
||||
}
|
||||
}
|
||||
|
||||
// x axis
|
||||
// finish the buffer building
|
||||
|
||||
@@ -63,8 +63,10 @@ public class LodNodeBuilder {
|
||||
*/
|
||||
|
||||
public void generateLodNodeAsync(IChunk chunk, LodQuadTreeWorld lodWorld, IWorld world) {
|
||||
if (lodWorld == null || !lodWorld.getIsWorldLoaded())
|
||||
if (lodWorld == null || !lodWorld.getIsWorldLoaded()) {
|
||||
System.out.println("This case?");
|
||||
return;
|
||||
}
|
||||
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
@@ -82,20 +84,24 @@ public class LodNodeBuilder {
|
||||
LodQuadTreeDimension lodDim;
|
||||
|
||||
if (lodWorld.getLodDimension(dim) == null) {
|
||||
System.out.println("Adding");
|
||||
lodDim = new LodQuadTreeDimension(dim, lodWorld, regionWidth);
|
||||
lodWorld.addLodDimension(lodDim);
|
||||
} else {
|
||||
System.out.println("Not adding");
|
||||
lodDim = lodWorld.getLodDimension(dim);
|
||||
}
|
||||
|
||||
lodDim.addNode(node);
|
||||
} catch (IllegalArgumentException | NullPointerException e) {
|
||||
e.printStackTrace();
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
}
|
||||
});
|
||||
lodGenThreadPool.execute(thread);
|
||||
System.out.println("Is this ENDING?");
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -266,6 +272,8 @@ public class LodNodeBuilder {
|
||||
* otherwise use the
|
||||
*/
|
||||
private Color generateLodColorForArea(IChunk chunk, LodBuilderConfig config, int startX, int startZ, int endX, int endZ) {
|
||||
return Color.BLUE;
|
||||
/*
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
int numbOfBlocks = 0;
|
||||
@@ -345,6 +353,8 @@ public class LodNodeBuilder {
|
||||
blue /= numbOfBlocks;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
@@ -15,10 +17,10 @@ import net.minecraft.client.renderer.BufferBuilder;
|
||||
*/
|
||||
public abstract class AbstractLodTemplate
|
||||
{
|
||||
public abstract void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging);
|
||||
public abstract void addLodToBuffer(BufferBuilder buffer,
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging);
|
||||
|
||||
/** add the given position and color to the buffer */
|
||||
protected void addPosAndColor(BufferBuilder buffer,
|
||||
|
||||
@@ -8,6 +8,8 @@ import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
|
||||
@@ -28,9 +30,9 @@ public class CubicLodTemplate extends AbstractLodTemplate
|
||||
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk centerLod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
{
|
||||
AxisAlignedBB bbox;
|
||||
|
||||
@@ -39,28 +41,19 @@ public class CubicLodTemplate extends AbstractLodTemplate
|
||||
LodDetail detail = LodConfig.CLIENT.lodDetail.get();
|
||||
|
||||
int halfWidth = detail.dataPointWidth / 2;
|
||||
|
||||
for(int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||
{
|
||||
int startX = detail.startX[i];
|
||||
int startZ = detail.startZ[i];
|
||||
int endX = detail.endX[i];
|
||||
int endZ = detail.endZ[i];
|
||||
|
||||
// returns null if the lod is empty at the given location
|
||||
bbox = generateBoundingBox(
|
||||
centerLod.getAverageHeightOverArea(startX, startZ, endX, endZ),
|
||||
centerLod.getAverageDepthOverArea(startX, startZ, endX, endZ),
|
||||
centerLod.lodDataPoint.height,
|
||||
centerLod.lodDataPoint.depth,
|
||||
detail.dataPointWidth,
|
||||
xOffset - (halfWidth / 2) + detail.startX[i],
|
||||
xOffset - (centerLod.width / 2),
|
||||
yOffset,
|
||||
zOffset - (halfWidth / 2) + detail.startZ[i]);
|
||||
zOffset - (centerLod.width / 2));
|
||||
|
||||
if (bbox != null)
|
||||
{
|
||||
addBoundingBoxToBuffer(buffer, bbox, centerLod.getAverageColorOverArea(startX, startZ, endX, endZ, debugging));
|
||||
addBoundingBoxToBuffer(buffer, bbox, centerLod.lodDataPoint.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
@@ -19,9 +21,9 @@ public class DynamicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
|
||||
import com.seibel.lod.objects.LodQuadTreeDimension;
|
||||
import com.seibel.lod.objects.LodQuadTreeNode;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
|
||||
/**
|
||||
@@ -17,9 +19,9 @@ public class TriangularLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer,
|
||||
LodDimension lodDim, LodChunk lod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
LodQuadTreeDimension lodDim, LodQuadTreeNode centerLod,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
boolean debugging)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
}
|
||||
|
||||
@@ -1,581 +0,0 @@
|
||||
package com.seibel.lod.builders.worldGeneration;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodBuilderConfig;
|
||||
import com.seibel.lod.builders.LodChunkBuilder;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.WeightedList.Entry;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.palette.UpgradeData;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeContainer;
|
||||
import net.minecraft.world.chunk.ChunkPrimer;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.gen.ChunkGenerator;
|
||||
import net.minecraft.world.gen.Heightmap;
|
||||
import net.minecraft.world.gen.blockstateprovider.WeightedBlockStateProvider;
|
||||
import net.minecraft.world.gen.feature.BlockClusterFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.ConfiguredFeature;
|
||||
import net.minecraft.world.gen.feature.DecoratedFeatureConfig;
|
||||
import net.minecraft.world.gen.feature.FeatureSpread;
|
||||
import net.minecraft.world.gen.feature.FeatureSpreadConfig;
|
||||
import net.minecraft.world.gen.feature.IceAndSnowFeature;
|
||||
import net.minecraft.world.gen.feature.NoFeatureConfig;
|
||||
import net.minecraft.world.gen.placement.ConfiguredPlacement;
|
||||
import net.minecraft.world.gen.placement.DecoratedPlacementConfig;
|
||||
import net.minecraft.world.gen.placement.IPlacementConfig;
|
||||
import net.minecraft.world.gen.placement.NoiseDependant;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraft.world.server.ServerWorldLightManager;
|
||||
import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-4-2021
|
||||
*/
|
||||
public class LodChunkGenWorker implements IWorker
|
||||
{
|
||||
public static final ExecutorService genThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
|
||||
|
||||
private boolean threadStarted = false;
|
||||
private LodChunkGenThread thread;
|
||||
|
||||
/** If a configured feature fails for whatever reason,
|
||||
* add it to this list, this is to hopefully remove any
|
||||
* features that could cause issues down the line. */
|
||||
private static ConcurrentHashMap<Integer, ConfiguredFeature<?, ?>> configuredFeaturesToAvoid = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
public LodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer,
|
||||
LodChunkBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld,
|
||||
BiomeContainer newBiomeContainer)
|
||||
{
|
||||
if (newServerWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ServerWorld");
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newLodRenderer,
|
||||
newLodBuilder, newLodBufferBuilder,
|
||||
newLodDimension, newServerWorld);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
if (!threadStarted)
|
||||
{
|
||||
// make sure we don't generate this chunk again
|
||||
thread.lodDim.addLod(new LodChunk(thread.pos));
|
||||
|
||||
thread.lodBufferBuilder.numberOfChunksWaitingToGenerate--;
|
||||
|
||||
if (LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.SERVER)
|
||||
{
|
||||
// if we are using SERVER generation that has to be done
|
||||
// synchronously to prevent crashing and harmful
|
||||
// interactions with the normal world generator
|
||||
thread.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
threadStarted = true;
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWork()
|
||||
{
|
||||
return !threadStarted;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private class LodChunkGenThread implements Runnable
|
||||
{
|
||||
public final ServerWorld serverWorld;
|
||||
public final LodDimension lodDim;
|
||||
public final LodChunkBuilder lodChunkBuilder;
|
||||
public final LodRenderer lodRenderer;
|
||||
private LodBufferBuilder lodBufferBuilder;
|
||||
|
||||
private ChunkPos pos;
|
||||
|
||||
public LodChunkGenThread(ChunkPos newPos, LodRenderer newLodRenderer,
|
||||
LodChunkBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
pos = newPos;
|
||||
lodRenderer = newLodRenderer;
|
||||
lodChunkBuilder = newLodBuilder;
|
||||
lodBufferBuilder = newLodBufferBuilder;
|
||||
lodDim = newLodDimension;
|
||||
serverWorld = newServerWorld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
if (lodDim.regionIsInRange(pos.x / LodRegion.SIZE, pos.z / LodRegion.SIZE))
|
||||
{
|
||||
// long startTime = System.currentTimeMillis();
|
||||
|
||||
switch(LodConfig.CLIENT.distanceGenerationMode.get())
|
||||
{
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
generateUsingBiomesOnly();
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
generateUsingSurface();
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
generateUsingFeatures();
|
||||
break;
|
||||
case SERVER:
|
||||
// very slow
|
||||
generateWithServer();
|
||||
break;
|
||||
}
|
||||
|
||||
lodRenderer.regenerateLODsNextFrame();
|
||||
|
||||
|
||||
// if (lodDim.getLodFromCoordinates(pos.x, pos.z) != null)
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(endTime - startTime);
|
||||
|
||||
}// if in range
|
||||
|
||||
}// run
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* takes about 2-5 ms
|
||||
*/
|
||||
private void generateUsingBiomesOnly()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
|
||||
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
|
||||
ChunkGenerator chunkGen = chunkSource.generator;
|
||||
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
|
||||
|
||||
// generate fake height data for this LOD
|
||||
int seaLevel = serverWorld.getSeaLevel();
|
||||
|
||||
boolean simulateHeight = LodConfig.CLIENT.distanceGenerationMode.get() == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
boolean inTheEnd = false;
|
||||
|
||||
// add fake heightmap data so our LODs aren't at height 0
|
||||
Heightmap heightmap = new Heightmap(chunk, LodChunk.DEFAULT_HEIGHTMAP);
|
||||
for(int x = 0; x < LodChunk.WIDTH && !inTheEnd; x++)
|
||||
{
|
||||
for(int z = 0; z < LodChunk.WIDTH && !inTheEnd; z++)
|
||||
{
|
||||
if (simulateHeight)
|
||||
{
|
||||
// TODO use the biomes around each block to smooth out the transition
|
||||
|
||||
// these heights are of course aren't super accurate,
|
||||
// they are just to simulate height data where there isn't any
|
||||
switch(chunk.getBiomes().getNoiseBiome(x, seaLevel, z).getBiomeCategory())
|
||||
{
|
||||
case NETHER:
|
||||
heightmap.setHeight(x, z, serverWorld.getHeight() / 2);
|
||||
break;
|
||||
|
||||
case EXTREME_HILLS:
|
||||
heightmap.setHeight(x, z, seaLevel + 30);
|
||||
break;
|
||||
case MESA:
|
||||
heightmap.setHeight(x, z, seaLevel + 20);
|
||||
break;
|
||||
case JUNGLE:
|
||||
heightmap.setHeight(x, z, seaLevel + 20);
|
||||
break;
|
||||
case BEACH:
|
||||
heightmap.setHeight(x, z, seaLevel + 5);
|
||||
break;
|
||||
case NONE:
|
||||
heightmap.setHeight(x, z, 0);
|
||||
break;
|
||||
|
||||
case OCEAN:
|
||||
case RIVER:
|
||||
heightmap.setHeight(x, z, seaLevel);
|
||||
break;
|
||||
|
||||
case THEEND:
|
||||
inTheEnd = true;
|
||||
break;
|
||||
|
||||
// DESERT
|
||||
// FOREST
|
||||
// ICY
|
||||
// MUSHROOM
|
||||
// SAVANNA
|
||||
// SWAMP
|
||||
// TAIGA
|
||||
// PLAINS
|
||||
default:
|
||||
heightmap.setHeight(x, z, seaLevel + 10);
|
||||
break;
|
||||
}// heightmap switch
|
||||
}
|
||||
else
|
||||
{
|
||||
// we aren't simulating height
|
||||
// always use sea level
|
||||
heightmap.setHeight(x, z, seaLevel);
|
||||
}
|
||||
}// z
|
||||
}// x
|
||||
|
||||
chunk.setHeightmap(LodChunk.DEFAULT_HEIGHTMAP, heightmap.getRawData());
|
||||
|
||||
|
||||
LodChunk lod;
|
||||
if (!inTheEnd)
|
||||
{
|
||||
lod = lodChunkBuilder.generateLodFromChunk(chunk, new LodBuilderConfig(true, true, false));
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are in the end, don't generate any chunks.
|
||||
// Since we don't know where the islands are, everything
|
||||
// generates the same and it looks really bad.
|
||||
lod = new LodChunk(chunk.getPos().x, chunk.getPos().z);
|
||||
}
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* takes about 10 - 20 ms
|
||||
*/
|
||||
private void generateUsingSurface()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
|
||||
|
||||
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
|
||||
ChunkGenerator chunkGen = chunkSource.generator;
|
||||
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
|
||||
// this feature has proved to be thread safe
|
||||
// so we will add it
|
||||
IceAndSnowFeature snowFeature = new IceAndSnowFeature(NoFeatureConfig.CODEC);
|
||||
snowFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition(), null);
|
||||
|
||||
|
||||
LodChunk lod = lodChunkBuilder.generateLodFromChunk(chunk, new LodBuilderConfig(false, true, true));
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* takes about 15 - 20 ms
|
||||
*
|
||||
* Causes concurrentModification Exceptions,
|
||||
* which could cause instability or world generation bugs
|
||||
*/
|
||||
private void generateUsingFeatures()
|
||||
{
|
||||
List<IChunk> chunkList = new LinkedList<>();
|
||||
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
|
||||
chunkList.add(chunk);
|
||||
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
|
||||
|
||||
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
|
||||
ChunkGenerator chunkGen = chunkSource.generator;
|
||||
|
||||
|
||||
// generate the terrain (this is thread safe)
|
||||
ChunkStatus.EMPTY.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
// override the chunk status so we can run the next generator stage
|
||||
chunk.setStatus(ChunkStatus.STRUCTURE_REFERENCES);
|
||||
ChunkStatus.BIOMES.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.NOISE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
ChunkStatus.SURFACE.generate(serverWorld, chunkGen, serverWorld.getStructureManager(), (ServerWorldLightManager) serverWorld.getLightEngine(), null, chunkList);
|
||||
|
||||
|
||||
// get all the biomes in the chunk
|
||||
HashSet<Biome> biomes = new HashSet<>();
|
||||
for (int x = 0; x < LodChunk.WIDTH; x++)
|
||||
{
|
||||
for (int z = 0; z < LodChunk.WIDTH; z++)
|
||||
{
|
||||
Biome biome = chunk.getBiomes().getNoiseBiome(x, serverWorld.getSeaLevel(), z);
|
||||
|
||||
// Issue #35
|
||||
// For some reason Jungle biomes cause incredible lag
|
||||
// the features here must be interacting with each other
|
||||
// in unpredictable ways (specifically tree feature generation).
|
||||
// When generating Features my CPU usage generally hovers around 30 - 40%
|
||||
// when generating Jungles it spikes to 100%.
|
||||
if (biome.getBiomeCategory() != Biome.Category.JUNGLE)
|
||||
{
|
||||
// should probably use the heightmap here instead of seaLevel,
|
||||
// but this seems to get the job done well enough
|
||||
biomes.add(biome);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generate all the features related to this chunk.
|
||||
// this may or may not be thread safe
|
||||
for (Biome biome : biomes)
|
||||
{
|
||||
List<List<Supplier<ConfiguredFeature<?, ?>>>> featuresForState = biome.generationSettings.features();
|
||||
|
||||
for(int featureStateToGenerate = 0; featureStateToGenerate < featuresForState.size(); featureStateToGenerate++)
|
||||
{
|
||||
for(Supplier<ConfiguredFeature<?, ?>> featureSupplier : featuresForState.get(featureStateToGenerate))
|
||||
{
|
||||
ConfiguredFeature<?, ?> configuredFeature = featureSupplier.get();
|
||||
|
||||
if (configuredFeaturesToAvoid.containsKey(configuredFeature.hashCode()))
|
||||
continue;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
configuredFeature.place(lodServerWorld, chunkGen, serverWorld.random, chunk.getPos().getWorldPosition());
|
||||
}
|
||||
catch(ConcurrentModificationException e)
|
||||
{
|
||||
// This will happen. I'm not sure what to do about it
|
||||
// except pray that it doesn't effect the normal world generation
|
||||
// in any harmful way
|
||||
|
||||
// Issue #35
|
||||
// I tried cloning the config for each feature, but that
|
||||
// path was blocked since I can't clone lambda methods.
|
||||
// I tried using a deep cloning library and discovered
|
||||
// the problem there.
|
||||
// ( https://github.com/kostaskougios/cloning )
|
||||
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
catch(UnsupportedOperationException e)
|
||||
{
|
||||
// This will happen when the LodServerWorld
|
||||
// isn't able to return something that a feature
|
||||
// generator needs
|
||||
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// I'm not sure what happened, print to the log
|
||||
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
e.printStackTrace();
|
||||
System.out.println();
|
||||
//ClientProxy.LOGGER.error("error class: \"" + configuredfeature.config.getClass() + "\"");
|
||||
System.out.println();
|
||||
|
||||
configuredFeaturesToAvoid.put(configuredFeature.hashCode(), configuredFeature);
|
||||
// ClientProxy.LOGGER.info(configuredFeaturesToAvoid.mappingCount());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generate a Lod like normal
|
||||
LodChunk lod = lodChunkBuilder.generateLodFromChunk(chunk);
|
||||
lodDim.addLod(lod);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* on pre generated chunks 0 - 1 ms
|
||||
* on un generated chunks 0 - 50 ms
|
||||
* with the median seeming to hover around 15 - 30 ms
|
||||
* and outliers in the 100 - 200 ms range
|
||||
*
|
||||
* Note this should not be multithreaded and does cause server/simulation lag
|
||||
* (Higher lag for generating than loading)
|
||||
*/
|
||||
private void generateWithServer()
|
||||
{
|
||||
lodChunkBuilder.generateLodChunkAsync(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES), ClientProxy.getLodWorld(), serverWorld);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// Unused methods //
|
||||
//================//
|
||||
|
||||
// Sadly I wasn't able to get these to work,
|
||||
// they are here for documentation purposes
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked", "unused" })
|
||||
private DecoratedFeatureConfig cloneDecoratedFeatureConfig(DecoratedFeatureConfig config)
|
||||
{
|
||||
IPlacementConfig placementConfig = null;
|
||||
|
||||
Class oldConfigClass = config.decorator.config().getClass();
|
||||
|
||||
if (oldConfigClass == FeatureSpreadConfig.class)
|
||||
{
|
||||
FeatureSpreadConfig oldPlacementConfig = (FeatureSpreadConfig) config.decorator.config();
|
||||
FeatureSpread oldSpread = oldPlacementConfig.count();
|
||||
|
||||
placementConfig = new FeatureSpreadConfig(oldSpread);
|
||||
}
|
||||
else if(oldConfigClass == DecoratedPlacementConfig.class)
|
||||
{
|
||||
DecoratedPlacementConfig oldPlacementConfig = (DecoratedPlacementConfig) config.decorator.config();
|
||||
placementConfig = new DecoratedPlacementConfig(oldPlacementConfig.inner(), oldPlacementConfig.outer());
|
||||
}
|
||||
else if(oldConfigClass == NoiseDependant.class)
|
||||
{
|
||||
NoiseDependant oldPlacementConfig = (NoiseDependant) config.decorator.config();
|
||||
placementConfig = new NoiseDependant(oldPlacementConfig.noiseLevel, oldPlacementConfig.belowNoise, oldPlacementConfig.aboveNoise);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ClientProxy.LOGGER.debug("unkown decorated placement config: \"" + config.decorator.config().getClass() + "\"");
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
ConfiguredPlacement<?> newPlacement = new ConfiguredPlacement(config.decorator.decorator, placementConfig);
|
||||
return new DecoratedFeatureConfig(config.feature, newPlacement);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config)
|
||||
{
|
||||
WeightedBlockStateProvider provider = new WeightedBlockStateProvider();
|
||||
for(Entry<BlockState> state : ((WeightedBlockStateProvider) config.stateProvider).weightedList.entries)
|
||||
provider.weightedList.entries.add(state);
|
||||
|
||||
HashSet<Block> whitelist = new HashSet<>();
|
||||
for(Block block : config.whitelist)
|
||||
whitelist.add(block);
|
||||
|
||||
HashSet<BlockState> blacklist = new HashSet<>();
|
||||
for(BlockState state : config.blacklist)
|
||||
blacklist.add(state);
|
||||
|
||||
|
||||
BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer);
|
||||
builder.whitelist(whitelist);
|
||||
builder.blacklist(blacklist);
|
||||
builder.xspread(config.xspread);
|
||||
builder.yspread(config.yspread);
|
||||
builder.zspread(config.zspread);
|
||||
if(config.canReplace) { builder.canReplace(); }
|
||||
if(config.needWater) { builder.needWater(); }
|
||||
if(config.project) { builder.noProjection(); }
|
||||
builder.tries(config.tries);
|
||||
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* performance/generation tests related to
|
||||
* serverWorld.getChunk(x, z, ChunkStatus. *** )
|
||||
|
||||
true/false is whether they generated blocks or not
|
||||
the time is how long it took to generate
|
||||
|
||||
ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P)
|
||||
ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks)
|
||||
ChunkStatus.BIOMES 1 - 10 ms false (no height)
|
||||
ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone)
|
||||
ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass)
|
||||
ChunkStatus.FEATURES 7 - 25 ms true
|
||||
ChunkStatus.HEIGHTMAPS 20 - 40 ms true
|
||||
ChunkStatus.LIGHT 20 - 40 ms true
|
||||
ChunkStatus.FULL 30 - 50 ms true
|
||||
ChunkStatus.SPAWN 50 - 80 ms true
|
||||
|
||||
|
||||
At this point I would suggest using FEATURES, as it generates snow and trees
|
||||
(and any other object that is needed to make biomes distinct)
|
||||
|
||||
Otherwise if snow/trees aren't necessary SURFACE is the next fastest (although not by much)
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
This are the file that you should use
|
||||
|
||||
DistanceGenerationMode (added NONE)
|
||||
LodQuadTreeDimensionFileHandler
|
||||
LodDataPoint (added hash and equal function)
|
||||
LodQuadTreeNode
|
||||
LodQuadTree
|
||||
LodQuadTreeDimension
|
||||
LodQuadTreeWorld (this is identical to LodWorld but uses LodQuadTreeDimension)
|
||||
|
||||
HOW IT WORK
|
||||
I've tried to make this classes as similar at yours. This way you could even do the same stuff that you are doing now
|
||||
like using all the Lod with the same quality. LodDetail is not used anywhere and is replaced by a level value in
|
||||
LodQuadTreeNode.
|
||||
|
||||
A LodQuadTree has a quad tree structure. So it has 4 children of the same type and a LodQuadTreeNode that contain all
|
||||
the information of the node such as position, level (the level is the depth of the quad tree) and the LodDataPoint.
|
||||
If in the future you want to add multiple LodDataPoint per position (maybe you want to show floating island) you could still
|
||||
do it by transforming the lodDataPoint variable in a LodDataPoint array.
|
||||
|
||||
The two most important factor of a Node is the level and the level position. At level 9 you find the region (of width 2^9=512)
|
||||
at level 4 you find the chunk (of width 2^4=16) and at level 0 you find the blocks (of width 2^0=1). The pos is like the
|
||||
region pos and the chunk pos but for every level.
|
||||
The complexity of a node indicate how the node was built, so i've used the DistanceGenerationMode enum. The complexity is
|
||||
ordered by the order in the enum (NONE -> BIOME_ONLY -> BIOME_ONLY_SIMULATE_HEIGHT -> SURFACE -> FEATURES -> SERVER). The idea
|
||||
is that you cannot override a node with a node that is less complex. This way you could use different type of generation based
|
||||
on the distance.
|
||||
|
||||
HOW TO USE
|
||||
@@ -1,16 +1,13 @@
|
||||
package com.seibel.lod.proxy;
|
||||
|
||||
import com.seibel.lod.builders.LodNodeBufferBuilder;
|
||||
import com.seibel.lod.builders.LodNodeBuilder;
|
||||
import com.seibel.lod.objects.*;
|
||||
import com.seibel.lod.render.LodNodeRenderer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodChunkBuilder;
|
||||
import com.seibel.lod.handlers.LodConfig;
|
||||
import com.seibel.lod.objects.LodChunk;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -31,10 +28,10 @@ public class ClientProxy
|
||||
{
|
||||
public static final Logger LOGGER = LogManager.getLogger("LOD");
|
||||
|
||||
private static LodWorld lodWorld = new LodWorld();
|
||||
private static LodChunkBuilder lodChunkBuilder = new LodChunkBuilder();
|
||||
private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(lodChunkBuilder);
|
||||
private static LodRenderer renderer = new LodRenderer(lodBufferBuilder);
|
||||
private static LodQuadTreeWorld lodWorld = new LodQuadTreeWorld();
|
||||
private static LodNodeBuilder lodChunkBuilder = new LodNodeBuilder();
|
||||
private static LodNodeBufferBuilder lodBufferBuilder = new LodNodeBufferBuilder(lodChunkBuilder);
|
||||
private static LodNodeRenderer renderer = new LodNodeRenderer(lodBufferBuilder);
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
@@ -59,41 +56,39 @@ public class ClientProxy
|
||||
{
|
||||
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
// update each regions' width to match the new render distance
|
||||
int newWidth = Math.max(4,
|
||||
// TODO is this logic good?
|
||||
(mc.options.renderDistance * LodChunk.WIDTH * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()) / LodRegion.SIZE
|
||||
);
|
||||
if (lodChunkBuilder.regionWidth != newWidth)
|
||||
{
|
||||
lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
lodChunkBuilder.regionWidth = newWidth;
|
||||
|
||||
// skip this frame, hopefully the lodWorld
|
||||
// should have everything set up by then
|
||||
return;
|
||||
}
|
||||
|
||||
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
|
||||
// offset the regions
|
||||
double playerX = mc.player.getX();
|
||||
double playerZ = mc.player.getZ();
|
||||
|
||||
int xOffset = ((int)playerX / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterX();
|
||||
int zOffset = ((int)playerZ / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterZ();
|
||||
|
||||
if (xOffset != 0 || zOffset != 0)
|
||||
{
|
||||
lodDim.move(xOffset, zOffset);
|
||||
}
|
||||
|
||||
|
||||
// TODO for testing
|
||||
try {
|
||||
// update each regions' width to match the new render distance
|
||||
int newWidth = Math.max(4,
|
||||
// TODO is this logic good?
|
||||
(mc.options.renderDistance * LodChunk.WIDTH * 2 * LodConfig.CLIENT.lodChunkRadiusMultiplier.get()) / LodRegion.SIZE
|
||||
);
|
||||
if (lodChunkBuilder.regionWidth != newWidth) {
|
||||
lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
lodChunkBuilder.regionWidth = newWidth;
|
||||
|
||||
// skip this frame, hopefully the lodWorld
|
||||
// should have everything set up by then
|
||||
return;
|
||||
}
|
||||
|
||||
LodQuadTreeDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
|
||||
// offset the regions
|
||||
double playerX = mc.player.getX();
|
||||
double playerZ = mc.player.getZ();
|
||||
|
||||
int xOffset = ((int) playerX / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterX();
|
||||
int zOffset = ((int) playerZ / (LodChunk.WIDTH * LodRegion.SIZE)) - lodDim.getCenterZ();
|
||||
|
||||
if (xOffset != 0 || zOffset != 0) {
|
||||
lodDim.move(xOffset, zOffset);
|
||||
}
|
||||
|
||||
|
||||
// TODO for testing
|
||||
// LodConfig.CLIENT.debugMode.set(false);
|
||||
// LodConfig.CLIENT.lodDetail.set(LodDetail.DOUBLE);
|
||||
// LodConfig.CLIENT.lodColorStyle.set(LodColorStyle.INDIVIDUAL_SIDES);
|
||||
@@ -101,19 +96,22 @@ public class ClientProxy
|
||||
// LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES);
|
||||
// LodConfig.CLIENT.fogDistance.set(FogDistance.FAR);
|
||||
// LodConfig.CLIENT.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
|
||||
|
||||
// Note to self:
|
||||
// if "unspecified" shows up in the pie chart, it is
|
||||
// possibly because the amount of time between sections
|
||||
// is too small for the profile to measure
|
||||
IProfiler profiler = mc.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
|
||||
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // restart terrain
|
||||
|
||||
// Note to self:
|
||||
// if "unspecified" shows up in the pie chart, it is
|
||||
// possibly because the amount of time between sections
|
||||
// is too small for the profile to measure
|
||||
IProfiler profiler = mc.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
|
||||
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // restart terrain
|
||||
}catch (Exception e){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,13 +124,15 @@ public class ClientProxy
|
||||
@SubscribeEvent
|
||||
public void chunkLoadEvent(ChunkEvent.Load event)
|
||||
{
|
||||
lodChunkBuilder.generateLodChunkAsync(event.getChunk(), lodWorld, event.getWorld());
|
||||
lodChunkBuilder.generateLodNodeAsync(event.getChunk(), lodWorld, event.getWorld());
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent
|
||||
public void worldLoadEvent(WorldEvent.Load event)
|
||||
{
|
||||
|
||||
System.out.println("Loading world");
|
||||
// the player just loaded a new world/dimension
|
||||
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
|
||||
// make sure the correct LODs are being rendered
|
||||
@@ -164,7 +164,7 @@ public class ClientProxy
|
||||
event.getClass() == BlockEvent.PortalSpawnEvent.class)
|
||||
{
|
||||
// recreate the LOD where the blocks were changed
|
||||
lodChunkBuilder.generateLodChunkAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
|
||||
lodChunkBuilder.generateLodNodeAsync(event.getWorld().getChunk(event.getPos()), lodWorld, event.getWorld());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,17 +175,17 @@ public class ClientProxy
|
||||
// public getters //
|
||||
//================//
|
||||
|
||||
public static LodWorld getLodWorld()
|
||||
public static LodQuadTreeWorld getLodWorld()
|
||||
{
|
||||
return lodWorld;
|
||||
}
|
||||
|
||||
public static LodChunkBuilder getLodBuilder()
|
||||
public static LodNodeBuilder getLodBuilder()
|
||||
{
|
||||
return lodChunkBuilder;
|
||||
}
|
||||
|
||||
public static LodRenderer getRenderer()
|
||||
public static LodNodeRenderer getRenderer()
|
||||
{
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public class RenderUtil
|
||||
*/
|
||||
public static int getMaxRadiusMultiplierWithAvaliableMemory(LodTemplate lodTemplate, LodDetail lodDetail)
|
||||
{
|
||||
int maxNumberOfLods = LodRenderer.MAX_ALOCATEABLE_DIRECT_MEMORY / lodTemplate.getBufferMemoryForSingleLod(lodDetail);
|
||||
int maxNumberOfLods = LodNodeRenderer.MAX_ALOCATEABLE_DIRECT_MEMORY / lodTemplate.getBufferMemoryForSingleLod(lodDetail);
|
||||
int numbLodsWide = (int) Math.sqrt(maxNumberOfLods);
|
||||
|
||||
return numbLodsWide / (2 * mc.options.renderDistance);
|
||||
|
||||
Reference in New Issue
Block a user