From bb61cdad70c38f5db3e45948d6dee49b3ca656e4 Mon Sep 17 00:00:00 2001 From: Leonardo Date: Tue, 10 Aug 2021 17:11:36 +0200 Subject: [PATCH] Added different LodDetail support --- .../lod/builders/LodNodeBufferBuilder.java | 86 ++++++++++--------- .../seibel/lod/builders/LodNodeBuilder.java | 50 +++++------ .../CubicLodNodeTemplate.java | 54 +++++++++--- .../com/seibel/lod/objects/LodQuadTree.java | 36 ++++++++ .../lod/objects/LodQuadTreeDimension.java | 46 +++++++++- .../seibel/lod/objects/LodQuadTreeNode.java | 4 +- .../com/seibel/lod/proxy/ClientProxy.java | 2 +- 7 files changed, 194 insertions(+), 84 deletions(-) diff --git a/src/main/java/com/seibel/lod/builders/LodNodeBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodNodeBufferBuilder.java index c030a51d8..c90ac910e 100644 --- a/src/main/java/com/seibel/lod/builders/LodNodeBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodNodeBufferBuilder.java @@ -21,6 +21,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; +import com.seibel.lod.enums.LodDetail; import org.lwjgl.opengl.GL11; import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker; @@ -158,15 +159,13 @@ public class LodNodeBufferBuilder for (int i = 0; i < numbChunksWide; i++) { // z axis - for (int j = 0; j < numbChunksWide; j++) - { - int chunkX = i + Math.floorDiv(startX , LodQuadTreeNode.CHUNK_WIDTH); - int chunkZ = j + Math.floorDiv(startZ , LodQuadTreeNode.CHUNK_WIDTH); + for (int j = 0; j < numbChunksWide; j++) { + int chunkX = i + Math.floorDiv(startX, LodQuadTreeNode.CHUNK_WIDTH); + int chunkZ = j + Math.floorDiv(startZ, LodQuadTreeNode.CHUNK_WIDTH); // skip any chunks that Minecraft is going to render - if(isCoordInCenterArea(i, j, (numbChunksWide / 2)) - && renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ))) - { + if (isCoordInCenterArea(i, j, (numbChunksWide / 2)) + && renderer.vanillaRenderedChunks.contains(new ChunkPos(chunkX, chunkZ))) { continue; } @@ -175,20 +174,18 @@ public class LodNodeBufferBuilder double xOffset = (LodQuadTreeNode.CHUNK_WIDTH * i) + // offset by the number of LOD blocks startX + // offset so the center LOD block is centered underneath the player 8; // I'm not sure what this is correcting exactly but with it the chunks line up. - // 8 works for LODs the size of chunks + // 8 works for LODs the size of chunks double yOffset = 0; double zOffset = (LodQuadTreeNode.CHUNK_WIDTH * j) + startZ + 8; - + LodQuadTreeNode lod = lodDim.getLodFromCoordinates(new ChunkPos(chunkX, chunkZ), LodQuadTreeNode.CHUNK_LEVEL); - if (lod == null || lod.complexity == DistanceGenerationMode.NONE) - { + if (lod == null || lod.complexity == 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.get() < maxChunkGenRequests) - { + if (lod == null && numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests) { ChunkPos pos = new ChunkPos(chunkX, chunkZ); - + // alternate determining logic that // can be used for debugging // if (chunksToGen == null) @@ -202,10 +199,8 @@ public class LodNodeBufferBuilder // chunksToGen[chunkGenIndex] = pos; // chunkGenIndex++; // } - - - - + + // determine if this position is closer to the player // than the previous int newDistance = playerChunkPos.getChessboardDistance(pos); @@ -216,69 +211,58 @@ public class LodNodeBufferBuilder // 100% CPU utilization, this code generally achieves 40 - 50% // after a certain point; and I'm sure there is a better data // structure for this. - if (newDistance < minChunkDist) - { + if (newDistance < minChunkDist) { // this chunk is closer, clear any previous // positions and update the new minimum distance minChunkDist = newDistance; - + // move all the old chunks into the reserve ChunkPos[] newReserve = new ChunkPos[maxChunkGenRequests]; int oldToGenIndex = 0; int oldReserveIndex = 0; - for(int tmpIndex = 0; tmpIndex < newReserve.length; tmpIndex++) - { + for (int tmpIndex = 0; tmpIndex < newReserve.length; tmpIndex++) { // we don't check if the boundaries are good since // the tmp array will always be the same length // as chunksToGen and chunksToGenReserve - - if (chunksToGen[oldToGenIndex] != null) - { + + if (chunksToGen[oldToGenIndex] != null) { // add all the closest chunks... newReserve[tmpIndex] = chunksToGen[oldToGenIndex]; oldToGenIndex++; - } - else if (chunksToGenReserve[oldReserveIndex] != null) - { + } else if (chunksToGenReserve[oldReserveIndex] != null) { // ...then add all the previous reserve chunks // (which are farther away) newReserve[tmpIndex] = chunksToGenReserve[oldToGenIndex]; oldReserveIndex++; - } - else - { + } else { // we have moved all the items from // the old chunksToGen and reserve break; } } chunksToGenReserve = newReserve; - - + chunkGenIndex = 0; chunksToGen = new ChunkPos[maxChunkGenRequests]; chunksToGen[chunkGenIndex] = pos; chunkGenIndex++; - } - else if (newDistance <= minChunkDist) - { + } else if (newDistance <= minChunkDist) { // this chunk position is as close or closers than the // minimum distance - if(chunkGenIndex < maxChunkGenRequests) - { + if (chunkGenIndex < maxChunkGenRequests) { // we are still under the number of chunks to generate // add this position to the list chunksToGen[chunkGenIndex] = pos; chunkGenIndex++; } } - + } // lod null and can generate more chunks // don't render this null/empty chunk continue; - + } // lod null or empty @@ -290,9 +274,27 @@ public class LodNodeBufferBuilder // 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); + xOffset , yOffset, zOffset, renderer.debugging); + /* + LodDetail detail = LodConfig.CLIENT.lodDetail.get(); + for(int x = 0; x < detail.dataPointLengthCount; x++){ + for(int z = 0; z < detail.dataPointLengthCount; z++) { + int posX = LodUtil.convertLevelPos(lod.startBlockPos.getX() + (x*detail.dataPointWidth), 0, detail.detailLevel); + int posZ = LodUtil.convertLevelPos(lod.startBlockPos.getZ() + (z*detail.dataPointWidth), 0, detail.detailLevel); + LodQuadTreeNode newLod = lodDim.getLodFromCoordinates(posX, posZ, detail.detailLevel); + System.out.print("printing "); + System.out.println(newLod); + if(newLod != null) { + LodConfig.CLIENT.lodTemplate.get(). + template.addLodToBuffer(currentBuffer, lodDim, newLod, + xOffset + (x*detail.dataPointWidth), yOffset, zOffset + (z*detail.dataPointWidth), renderer.debugging); + } + } + } + */ } } diff --git a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java index 1c1421c4a..1b7503125 100644 --- a/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodNodeBuilder.java @@ -88,7 +88,7 @@ public class LodNodeBuilder { try { DimensionType dim = world.dimensionType(); - List nodeList = generateLodNodeFromChunk(chunk); + List nodeList = generateLodNodeFromChunk(chunk); LodQuadTreeDimension lodDim; @@ -98,8 +98,9 @@ public class LodNodeBuilder { } else { lodDim = lodWorld.getLodDimension(dim); } - for(LodQuadTreeNode node : nodeList) { + for (LodQuadTreeNode node : nodeList) { lodDim.addNode(node); + } } catch (IllegalArgumentException | NullPointerException e) { e.printStackTrace(); @@ -130,37 +131,36 @@ public class LodNodeBuilder { * @throws IllegalArgumentException thrown if either the chunk or world is null. */ public List generateLodNodeFromChunk(IChunk chunk, LodBuilderConfig config) throws IllegalArgumentException { - LodDetail detail = LodDetail.QUAD; + LodDetail detail = LodDetail.HALF; List lodNodeList = new ArrayList<>(); if (chunk == null) throw new IllegalArgumentException("generateLodFromChunk given a null chunk"); - for (int x = 0; x < detail.dataPointLengthCount; x++) { - for (int z = 0; z < detail.dataPointLengthCount; z++) { - int startX = detail.startX[x]; - int startZ = detail.startZ[z]; - int endX = detail.endX[x]; - int endZ = detail.endZ[z]; + 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]; - // TODO startX/Z and endX/Z are relative coordinates - // getMin/Max appear to return world block coordinates + // TODO startX/Z and endX/Z are relative coordinates + // getMin/Max appear to return world block coordinates - Color color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ); + Color color = generateLodColorForArea(chunk, config, startX, startZ, endX, endZ); - short height; - short depth; + short height; + short depth; - if (!config.useHeightmap) { - height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ); - depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ); - } else { - height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ); - depth = 0; - } - lodNodeList.add(new LodQuadTreeNode((byte) detail.detailLevel, - LodUtil.convertLevelPos(chunk.getPos().getMinBlockX() + startX,0,detail.detailLevel), - LodUtil.convertLevelPos(chunk.getPos().getMinBlockZ() + startZ,0,detail.detailLevel) , - new LodDataPoint(height, depth, color), DistanceGenerationMode.SERVER)); + if (!config.useHeightmap) { + height = determineHeightPointForArea(chunk.getSections(), startX, startZ, endX, endZ); + depth = determineBottomPointForArea(chunk.getSections(), startX, startZ, endX, endZ); + } else { + height = determineHeightPoint(chunk.getOrCreateHeightmapUnprimed(LodChunk.DEFAULT_HEIGHTMAP), startX, startZ, endX, endZ); + depth = 0; } + lodNodeList.add(new LodQuadTreeNode((byte) detail.detailLevel, + LodUtil.convertLevelPos(chunk.getPos().getMinBlockX() + startX, 0, detail.detailLevel), + LodUtil.convertLevelPos(chunk.getPos().getMinBlockZ() + startZ, 0, detail.detailLevel), + new LodDataPoint(height, depth, color), DistanceGenerationMode.SERVER)); + } return lodNodeList; diff --git a/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java b/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java index 9cf91b5d3..87b55a289 100644 --- a/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java +++ b/src/main/java/com/seibel/lod/builders/lodNodeTemplates/CubicLodNodeTemplate.java @@ -18,11 +18,14 @@ package com.seibel.lod.builders.lodNodeTemplates; import java.awt.Color; +import com.seibel.lod.enums.LodDetail; import com.seibel.lod.enums.ShadingMode; import com.seibel.lod.handlers.LodConfig; +import com.seibel.lod.objects.LodQuadTree; import com.seibel.lod.objects.LodQuadTreeDimension; import com.seibel.lod.objects.LodQuadTreeNode; +import com.seibel.lod.util.LodUtil; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.util.math.AxisAlignedBB; @@ -48,20 +51,45 @@ public class CubicLodNodeTemplate extends AbstractLodNodeTemplate AxisAlignedBB bbox; // Add this LOD to the BufferBuilder - int halfWidth = lod.width / 2; - - // returns null if the lod is empty at the given location - bbox = generateBoundingBox( - lod.getLodDataPoint().height, - lod.getLodDataPoint().depth, - lod.width, - xOffset - halfWidth, - yOffset, - zOffset - halfWidth); + int halfWidth = LodQuadTreeNode.CHUNK_WIDTH / 2; + LodDetail detail = LodConfig.CLIENT.lodDetail.get(); +/* + bbox = generateBoundingBox( + lod.getLodDataPoint().height, + lod.getLodDataPoint().depth, + lod.width, + xOffset - halfWidth, + yOffset, + zOffset - halfWidth); + + if (bbox != null) { + addBoundingBoxToBuffer(buffer, bbox, lod.getLodDataPoint().color); + } + + */ + LodQuadTree chunkTree = lodDim.getLevelFromPos(lod.posX, lod.posZ, lod.detailLevel); + for(int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++) { + int startX = detail.startX[i]; + int startZ = detail.startZ[i]; + int posX = LodUtil.convertLevelPos((int) xOffset+startX,0, detail.detailLevel); + int posZ = LodUtil.convertLevelPos((int) zOffset+startZ,0, detail.detailLevel);; + //LodQuadTreeNode newLod = chunkTree.getNodeAtPos(posX ,posZ ,detail.detailLevel); + LodQuadTreeNode newLod = lodDim.getLodFromCoordinates(posX ,posZ ,detail.detailLevel); + if(newLod != null) { + bbox = generateBoundingBox( + newLod.getLodDataPoint().height, + newLod.getLodDataPoint().depth, + newLod.width, + xOffset + startX, + yOffset, + zOffset + startZ); + + if (bbox != null) { + addBoundingBoxToBuffer(buffer, bbox, newLod.getLodDataPoint().color); + } + } + } - if (bbox != null) { - addBoundingBoxToBuffer(buffer, bbox, lod.getLodDataPoint().color); - } } diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTree.java b/src/main/java/com/seibel/lod/objects/LodQuadTree.java index b1f4c4812..311f4d0b1 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTree.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTree.java @@ -263,6 +263,42 @@ public class LodQuadTree } + /** + * Gets the LodQuadTree at the given generic pos and detailLevel. + * Returns null if no such LodQuadTreeNode exists. + */ + public LodQuadTree getLevelAtPos(int posX, int posZ, int detailLevel) + { + if (detailLevel > LodQuadTreeNode.REGION_LEVEL) + throw new IllegalArgumentException("getNodeAtChunkPos given a level of \"" + detailLevel + "\" when \"" + LodQuadTreeNode.REGION_LEVEL + "\" is the max."); + + + byte currentDetailLevel = lodNode.detailLevel; + if (detailLevel == currentDetailLevel) + { + return this; + } + else if (detailLevel < currentDetailLevel) + { + // the detail level we need is lower, go down a layer + short widthRatio = (short) (lodNode.width / (2 * Math.pow(2, detailLevel))); + int WE = Math.abs(Math.floorDiv(posX , widthRatio) % 2); + int NS = Math.abs(Math.floorDiv(posZ , widthRatio) % 2); + if (getChild(NS, WE) == null) + { + return null; + } + LodQuadTree child = getChild(NS, WE); + return child.getLevelAtPos(posX, posZ, detailLevel); + } + else + { + // the detail level was higher than this region's + return null; + } + + } + /** * Put a child with the given data into the given position. * diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java b/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java index 6b964b56c..582b8fefd 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTreeDimension.java @@ -393,7 +393,51 @@ public class LodQuadTreeDimension return region.getNodeAtChunkPos(chunkPos); } - + + /** + * Get the LodNodeData at the given X and Z coordinates + * in this dimension. + *
+ * Returns null if the LodChunk doesn't exist or + * is outside the loaded area. + */ + public LodQuadTreeNode getLodFromCoordinates(int posX, int posZ, int detailLevel) + { + if (detailLevel > LodQuadTreeNode.REGION_LEVEL) + throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodQuadTreeNode.REGION_LEVEL + "\" is the max."); + + LodQuadTree region = getRegion(LodUtil.convertGenericPosToRegionPos(posX, posZ, detailLevel)); + + if(region == null) + { + return null; + } + + return region.getNodeAtPos(posX, posZ, detailLevel); + } + + /** + * Get the LodNodeData at the given X and Z coordinates + * in this dimension. + *
+ * Returns null if the LodChunk doesn't exist or + * is outside the loaded area. + */ + public LodQuadTree getLevelFromPos(int posX, int posZ, int detailLevel) + { + if (detailLevel > LodQuadTreeNode.REGION_LEVEL) + throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodQuadTreeNode.REGION_LEVEL + "\" is the max."); + + LodQuadTree region = getRegion(LodUtil.convertGenericPosToRegionPos(posX, posZ, detailLevel)); + + if(region == null) + { + return null; + } + + return region.getLevelAtPos(posX, posZ, detailLevel); + } + /** * return true if and only if the node at that position exist */ diff --git a/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java b/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java index eb8c0e327..55bf81e98 100644 --- a/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java +++ b/src/main/java/com/seibel/lod/objects/LodQuadTreeNode.java @@ -313,9 +313,9 @@ public class LodQuadTreeNode { // TODO would it be better to use the average height/depth? // get the lowest height from the all the given LodQuadTreeNodes - short height = (short) dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().height).min().getAsInt(); + short height = (short) dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().height).max().getAsInt(); // get the highest depth - short depth = (short) dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().depth).max().getAsInt(); + short depth = (short) dataList.stream().mapToInt(x -> (int) x.getLodDataPoint().depth).min().getAsInt(); // get the average color int red = dataList.stream().mapToInt(x -> x.getLodDataPoint().color.getRed()).sum() / dataList.size(); diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index bfffe3388..ac6bbc861 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -168,7 +168,7 @@ public class ClientProxy // LodConfig.CLIENT.brightnessMultiplier.set(1.0); // LodConfig.CLIENT.saturationMultiplier.set(1.0); - LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.SURFACE); + LodConfig.CLIENT.distanceGenerationMode.set(DistanceGenerationMode.FEATURES); LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); // LOGGER.info(lodBufferBuilder.numberOfChunksWaitingToGenerate.get());