Compare commits
220 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ff43118976 | |||
| 3b4e28d74a | |||
| 13abed4eb8 | |||
| ccb2c601a6 | |||
| f6816d9974 | |||
| 5156ae8e63 | |||
| 55c0c95339 | |||
| 0b8503f1fa | |||
| 79d9ac5d56 | |||
| 6cf4e663fb | |||
| bf9c3ce567 | |||
| 5bbdd22b58 | |||
| 6c1c04bb5f | |||
| 66728cc1eb | |||
| d4739b9bed | |||
| a93bb63c44 | |||
| a2e18de9f3 | |||
| 6183152d99 | |||
| 5d9f36bb5f | |||
| d330083d7b | |||
| 6d489b498e | |||
| d36d836bb3 | |||
| 29d2d1cff1 | |||
| 0a9ea1dfce | |||
| b9424cbe9f | |||
| 593a014dfc | |||
| 83ac04dca4 | |||
| c9aed389ae | |||
| 00da0e0520 | |||
| 5124739348 | |||
| d65bfd408e | |||
| da413f594e | |||
| 1fcef4be96 | |||
| d7cddd9b39 | |||
| fb480c2695 | |||
| 265abb64b3 | |||
| 1cf4852788 | |||
| 90d0fbe45b | |||
| ea9eac89ec | |||
| 017cc201b1 | |||
| bda963036d | |||
| 46bdf5763f | |||
| 8228a3b7a6 | |||
| e3df6c99da | |||
| a4e9e22d2c | |||
| 435c5ee73a | |||
| c6b063a380 | |||
| 1f5c3f5bd8 | |||
| 688cb3f89a | |||
| af80ff5267 | |||
| 2bc5c43b19 | |||
| e7c4827d08 | |||
| 482dfb918e | |||
| 5aaf6e0185 | |||
| ab014af15d | |||
| 772de1b869 | |||
| cb42683774 | |||
| a00698ccab | |||
| 29ed26f023 | |||
| af3c4ab801 | |||
| 1156b7dd28 | |||
| 25fd29b97e | |||
| a11ff5b493 | |||
| 7655ae03b0 | |||
| 075e29c617 | |||
| e482bf02a5 | |||
| 0dab4a2274 | |||
| 001335ab47 | |||
| faedb85f41 | |||
| 01b9a3f3b0 | |||
| cfd9bd903d | |||
| 3e634c082a | |||
| 6e86a808a5 | |||
| e615674246 | |||
| 86b5fc48a2 | |||
| 2fab33fa78 | |||
| b56b581bb6 | |||
| 2ee5289881 | |||
| 3da4e4818c | |||
| 686062f39c | |||
| ae857dfeae | |||
| 686592effb | |||
| 34b92d1053 | |||
| 01a850eb9d | |||
| 7d6f7f35ff | |||
| 28764bc16c | |||
| ee1657a798 | |||
| c7bf60b4e0 | |||
| 031af5f25c | |||
| d8200422b2 | |||
| c89cd54429 | |||
| ca940d5a36 | |||
| 2241492bc5 | |||
| 7b807bcea2 | |||
| ebe2c22a28 | |||
| cffb17aeb1 | |||
| c67a5a5e29 | |||
| 1d687fd1d4 | |||
| 783d4086c6 | |||
| d5243b7d16 | |||
| 43f3854068 | |||
| 0025f43a2e | |||
| 07d792979c | |||
| a939d29357 | |||
| ba1d096a48 | |||
| 1c9c72cfe3 | |||
| aa95361866 | |||
| ff5139d403 | |||
| 16fc4914fb | |||
| c110524c11 | |||
| 400b263059 | |||
| dadda4bdc9 | |||
| 1888ac7adc | |||
| 5c59ba7a80 | |||
| f3dd99e792 | |||
| 103ec128ac | |||
| b4226ec9ec | |||
| 7c28dd70d3 | |||
| d8c9a41314 | |||
| 5a7f77479f | |||
| d200a5ac24 | |||
| 5099f85da6 | |||
| 6e150fe378 | |||
| fd67d1ac66 | |||
| 7161bd52de | |||
| 82cf25c341 | |||
| 68d279807f | |||
| 3530158def | |||
| 62223480e2 | |||
| 50ab424497 | |||
| 900467cef2 | |||
| fa1d950ff9 | |||
| 83571951be | |||
| c31b1f4039 | |||
| 8fcd428194 | |||
| 87c275f768 | |||
| 8df6e972cb | |||
| d7d88d61ee | |||
| 9b1c0a1125 | |||
| b008b70255 | |||
| b6c350f667 | |||
| e23bbfcd91 | |||
| 6bc14fbb2d | |||
| d911017112 | |||
| fc4546538f | |||
| a90b3e9d37 | |||
| b2aca27615 | |||
| 6bf9e187e0 | |||
| b5f32705e8 | |||
| 8bc72af63f | |||
| 0e4cf8e882 | |||
| 5ede5fa202 | |||
| cdeba2616c | |||
| 6dc94b0cc2 | |||
| 7f9c7d8722 | |||
| aebbeb6ade | |||
| a945eb4579 | |||
| f9cf27a2c7 | |||
| e03e09a243 | |||
| fe02813d17 | |||
| 145479267d | |||
| 77ccd9eec3 | |||
| 9b216fedad | |||
| e9798ace13 | |||
| 75e78d9000 | |||
| 34776074fd | |||
| 8822e2d8a1 | |||
| 32de70b4f0 | |||
| 76a7baeb32 | |||
| 95d9c17e49 | |||
| a6544d3bb6 | |||
| c3115caa8f | |||
| 4e1e5b24ee | |||
| 7b73445f4d | |||
| 5f1f5f0948 | |||
| bdcc4c7755 | |||
| 1395e32a50 | |||
| 99f7d70613 | |||
| 59bfd739f4 | |||
| 3f1cf6c305 | |||
| 5c05df0361 | |||
| feebc3564a | |||
| a9aa630aff | |||
| 6ec146ba60 | |||
| 21140593e2 | |||
| 878714dae3 | |||
| e876d7bec6 | |||
| 4f06395557 | |||
| aa3dbe8f32 | |||
| b1bcdb24f0 | |||
| cbd7f4300e | |||
| 4d8e30121e | |||
| fbe39542fc | |||
| 6a1f448b41 | |||
| e13b23832c | |||
| 4c9e0edf32 | |||
| 8664a06d3b | |||
| 18b8359f0d | |||
| 7de4c7c72a | |||
| b4bbabae42 | |||
| 40321d6e21 | |||
| cf1702bd0c | |||
| a9b50cdb32 | |||
| 7eed472029 | |||
| 0262e452c7 | |||
| 30287d5a7d | |||
| 83346fed1f | |||
| e897c3edba | |||
| bd9e2acaf6 | |||
| 1e15d372c4 | |||
| 849d563425 | |||
| aa5a8aa3b8 | |||
| b096cc53aa | |||
| 94d994e761 | |||
| f76fa17e25 | |||
| 53a66268cb | |||
| 139867d4b8 | |||
| 81cf05ba19 | |||
| 13a1e7ed56 | |||
| 42bd0fbde9 |
Binary file not shown.
+5
-13
@@ -1,7 +1,6 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url = 'https://files.minecraftforge.net/maven' }
|
||||
jcenter()
|
||||
maven { url = 'https://files.minecraftforge.net' }
|
||||
mavenCentral()
|
||||
// potential replacement in case of problems:
|
||||
// https://dist.creeper.host/Sponge/maven
|
||||
@@ -19,20 +18,13 @@ apply plugin: 'org.spongepowered.mixin'
|
||||
// Only edit below this line, the above code adds and enables the necessary things for Forge to be setup.
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'maven-publish'
|
||||
apply plugin: 'java' // needed for compileJava
|
||||
|
||||
version = 'a1.4'
|
||||
version = 'a1.5.0-pre'
|
||||
group = 'com.seibel.lod'
|
||||
archivesBaseName = 'lod_1.16.5'
|
||||
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
|
||||
|
||||
compileJava {
|
||||
// release 8 is needed because otherwise FloatBuffer.flip() will crash
|
||||
// on some machines
|
||||
// example thread: https://github.com/eclipse/jetty.project/issues/3244
|
||||
options.compilerArgs.addAll(['--release', '8', '-Xlint:unchecked', '-Xlint:deprecation'])
|
||||
}
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
|
||||
|
||||
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
|
||||
minecraft {
|
||||
@@ -200,7 +192,7 @@ jar {
|
||||
"Specification-Title": "LOD",
|
||||
"Specification-Version": "1", // We are version 1 of ourselves
|
||||
"Implementation-Title": project.name,
|
||||
"Implementation-Version": "{version}",
|
||||
"Implementation-Version": "1",
|
||||
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
"MixinConfigs": "lod.mixins.json",
|
||||
])
|
||||
@@ -228,4 +220,4 @@ publishing {
|
||||
|
||||
mixin {
|
||||
add sourceSets.main, "lod.refmap.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.seibel.lod;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.builders.lodTemplates.Box;
|
||||
import com.seibel.lod.util.DataPointUtil;
|
||||
|
||||
import net.minecraft.util.Direction;
|
||||
|
||||
public class Main
|
||||
{
|
||||
public static void main(String[] args)
|
||||
{
|
||||
/*
|
||||
try
|
||||
{
|
||||
@SuppressWarnings("serial")
|
||||
Map<Direction, long[]> adjData = new HashMap<Direction, long[]>()
|
||||
{{
|
||||
put(Direction.EAST, new long[]{DataPointUtil.createDataPoint(60, 30, 0, 0, 0, 0), DataPointUtil.createDataPoint(25, 10, 0, 0, 0, 0)});
|
||||
put(Direction.WEST, new long[]{DataPointUtil.createDataPoint(60, 10, 0, 0, 0, 0)});
|
||||
put(Direction.NORTH, new long[]{DataPointUtil.createDataPoint(40, 20, 0, 0, 0, 0)});
|
||||
put(Direction.SOUTH, new long[]{DataPointUtil.createDataPoint(40, 20, 0, 0, 0, 0)});
|
||||
}};
|
||||
|
||||
Box box = new Box();
|
||||
int height = 60;
|
||||
int depth = 20;
|
||||
|
||||
box.set(10, height - depth, 10);
|
||||
box.move(0, depth, 0);
|
||||
box.setAdjData(adjData);
|
||||
|
||||
for(Direction direction : Box.DIRECTIONS)
|
||||
{
|
||||
int adjIndex = 0;
|
||||
while (box.shouldContinue(direction, adjIndex))
|
||||
{
|
||||
System.out.println(direction.toString());
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
System.out.println(box.getX(direction, i) + " " + box.getY(direction, i, adjIndex) + " " + box.getZ(direction, i));
|
||||
}
|
||||
adjIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -28,5 +28,5 @@ public final class ModInfo
|
||||
public static final String MODID = "lod";
|
||||
public static final String MODNAME = "LOD";
|
||||
public static final String MODAPI = "LodAPI";
|
||||
public static final String VERSION = "a1.4";
|
||||
public static final String VERSION = "a1.5.0-pre";
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package com.seibel.lod.builders;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
/**
|
||||
* @author Leonardo Amato
|
||||
* @version 22-08-2021
|
||||
*/
|
||||
public class GenerationRequest
|
||||
{
|
||||
public final LevelPos levelPos;
|
||||
public final DistanceGenerationMode generationMode;
|
||||
|
||||
public GenerationRequest(LevelPos levelPos, DistanceGenerationMode generationMode)
|
||||
{
|
||||
this.levelPos = levelPos;
|
||||
this.generationMode = generationMode;
|
||||
}
|
||||
|
||||
public ChunkPos getChunkPos()
|
||||
{
|
||||
LevelPos chunkLevelPos = levelPos.getConvertedLevelPos(LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
return new ChunkPos(chunkLevelPos.posX, chunkLevelPos.posZ);
|
||||
}
|
||||
}
|
||||
@@ -17,103 +17,123 @@
|
||||
*/
|
||||
package com.seibel.lod.builders;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL15C;
|
||||
|
||||
import com.seibel.lod.builders.lodTemplates.Box;
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.VerticalQuality;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.PosToRenderContainer;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.proxy.GlProxy;
|
||||
import com.seibel.lod.proxy.GlProxy.GlProxyContext;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.DataPointUtil;
|
||||
import com.seibel.lod.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.util.LevelPosUtil;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
|
||||
/**
|
||||
* This object is used to create NearFarBuffer objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-24-2021
|
||||
* @version 9-22-2021
|
||||
*/
|
||||
public class LodBufferBuilder
|
||||
{
|
||||
/**
|
||||
* This holds the thread used to generate new LODs off the main thread.
|
||||
*/
|
||||
private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main"));
|
||||
public static ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - main"));
|
||||
/**
|
||||
* This holds the threads used to generate buffers.
|
||||
*/
|
||||
private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfBufferBuilderThreads.get(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder"));
|
||||
|
||||
public static ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfBufferBuilderThreads.get(), new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - builder"));
|
||||
|
||||
/**
|
||||
* The buffers that are used to create LODs using far fog
|
||||
*/
|
||||
public volatile BufferBuilder[][] buildableBuffers;
|
||||
|
||||
|
||||
/**
|
||||
* Used when building new VBOs
|
||||
*/
|
||||
public volatile VertexBuffer[][] buildableVbos;
|
||||
|
||||
|
||||
/**
|
||||
* VBOs that are sent over to the LodNodeRenderer
|
||||
*/
|
||||
public volatile VertexBuffer[][] drawableVbos;
|
||||
|
||||
|
||||
/**
|
||||
* if this is true the LOD buffers are currently being
|
||||
* regenerated.
|
||||
*/
|
||||
public boolean generatingBuffers = false;
|
||||
|
||||
|
||||
/**
|
||||
* if this is true new LOD buffers have been generated
|
||||
* and are waiting to be swapped with the drawable buffers
|
||||
*/
|
||||
private boolean switchVbos = false;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Size of the buffer builders in bytes last time we created them
|
||||
*/
|
||||
public int previousBufferSize = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Width of the dimension in regions last time we created the buffers
|
||||
*/
|
||||
public int previousRegionWidth = 0;
|
||||
|
||||
|
||||
/**
|
||||
* this is used to prevent multiple threads creating, destroying, or using the buffers at the same time
|
||||
*/
|
||||
private ReentrantLock bufferLock = new ReentrantLock();
|
||||
|
||||
private volatile Object[][] setsToRender;
|
||||
|
||||
// private static final int NUMBER_OF_DIRECTION = 4;
|
||||
//in order -x, +x, -z, +z
|
||||
// private static final int[][] ADJ_VECTOR = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
|
||||
|
||||
private volatile Box[][] boxCache;
|
||||
private volatile PosToRenderContainer[][] setsToRender;
|
||||
private volatile RegionPos center;
|
||||
|
||||
|
||||
/**
|
||||
* This is the ChunkPos the player was at the last time the buffers were built.
|
||||
* IE the center of the buffers last time they were built
|
||||
*/
|
||||
private volatile ChunkPos drawableCenterChunkPos = new ChunkPos(0, 0);
|
||||
private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0, 0);
|
||||
private volatile boolean firstSetup = true;
|
||||
public LodBufferBuilder()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create a thread to asynchronously generate LOD buffers
|
||||
* centered around the given camera X and Z.
|
||||
@@ -124,193 +144,202 @@ public class LodBufferBuilder
|
||||
* swapped with the drawable buffers in the LodRenderer to be drawn.
|
||||
*/
|
||||
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
|
||||
BlockPos playerBlockPos, boolean fullRegen)
|
||||
BlockPos playerBlockPos, boolean fullRegen)
|
||||
{
|
||||
|
||||
// only allow one generation process to happen at a time
|
||||
if (generatingBuffers)
|
||||
return;
|
||||
|
||||
|
||||
if (buildableBuffers == null)
|
||||
// setupBuffers hasn't been called yet
|
||||
return;
|
||||
|
||||
|
||||
|
||||
generatingBuffers = true;
|
||||
|
||||
|
||||
|
||||
// round the player's block position down to the nearest chunk BlockPos
|
||||
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
|
||||
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
|
||||
|
||||
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
bufferLock.lock();
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
long treeStart = System.currentTimeMillis();
|
||||
long treeEnd = System.currentTimeMillis();
|
||||
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
|
||||
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length);
|
||||
|
||||
|
||||
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.getWidth() * lodDim.getWidth());
|
||||
|
||||
//setupBuffers(lodDim);
|
||||
startBuffers(fullRegen, lodDim);
|
||||
|
||||
|
||||
// =====================//
|
||||
// RENDERING PART //
|
||||
// =====================//
|
||||
|
||||
|
||||
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
|
||||
if (center == null)
|
||||
center = playerRegionPos;
|
||||
|
||||
|
||||
if (setsToRender == null)
|
||||
setsToRender = new Object[lodDim.regions.length][lodDim.regions.length];
|
||||
|
||||
if (setsToRender.length != lodDim.regions.length)
|
||||
setsToRender = new Object[lodDim.regions.length][lodDim.regions.length];
|
||||
|
||||
|
||||
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
|
||||
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
|
||||
setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()];
|
||||
|
||||
if (setsToRender.length != lodDim.getWidth())
|
||||
setsToRender = new PosToRenderContainer[lodDim.getWidth()][lodDim.getWidth()];
|
||||
|
||||
if (boxCache == null)
|
||||
boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()];
|
||||
|
||||
if (boxCache.length != lodDim.getWidth())
|
||||
boxCache = new Box[lodDim.getWidth()][lodDim.getWidth()];
|
||||
|
||||
// this will be the center of the VBOs once they have been built
|
||||
buildableCenterChunkPos = playerChunkPos;
|
||||
|
||||
for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++)
|
||||
{
|
||||
move(worldRegionOffset, Math.floorDiv(lodDim.getWidth(), 2));
|
||||
}
|
||||
|
||||
for (int xRegion = 0; xRegion < lodDim.regions.length; xRegion++)
|
||||
{
|
||||
for (int zRegion = 0; zRegion < lodDim.regions.length; zRegion++)
|
||||
for (int zRegion = 0; zRegion < lodDim.getWidth(); zRegion++)
|
||||
{
|
||||
if (lodDim.regen[xRegion][zRegion] || fullRegen)
|
||||
if (lodDim.isRegionToRegen(xRegion, zRegion) || fullRegen)
|
||||
{
|
||||
RegionPos regionPos = new RegionPos(
|
||||
xRegion + lodDim.getCenterX() - Math.floorDiv(lodDim.getWidth(), 2),
|
||||
zRegion + lodDim.getCenterZ() - Math.floorDiv(lodDim.getWidth(), 2));
|
||||
|
||||
|
||||
// local position in the vbo and bufferBuilder arrays
|
||||
BufferBuilder currentBuffer = buildableBuffers[xRegion][zRegion];
|
||||
|
||||
LodRegion region = lodDim.getRegion(regionPos.x, regionPos.z);
|
||||
|
||||
if (region == null)
|
||||
continue;
|
||||
|
||||
// make sure the buffers weren't
|
||||
// changed while we were running this method
|
||||
if (currentBuffer == null || (currentBuffer != null && !currentBuffer.building()))
|
||||
if (currentBuffer == null || !currentBuffer.building())
|
||||
return;
|
||||
|
||||
//previous setToRender chache
|
||||
if (setsToRender[xRegion][zRegion] == null)
|
||||
{
|
||||
setsToRender[xRegion][zRegion] = new ConcurrentHashMap<LevelPos, MutableBoolean>();
|
||||
}
|
||||
ConcurrentMap<LevelPos, MutableBoolean> nodeToRender = (ConcurrentMap<LevelPos, MutableBoolean>) setsToRender[xRegion][zRegion];
|
||||
|
||||
byte minDetail = region.getMinDetailLevel();
|
||||
|
||||
|
||||
final int xR = xRegion;
|
||||
final int zR = zRegion;
|
||||
Callable<Boolean> dataToRenderThread = () ->
|
||||
{
|
||||
Map<Direction, long[]> adjData = new HashMap<>();
|
||||
|
||||
// determine how many LODs we can stack vertically
|
||||
int maxVerticalData = 1;
|
||||
if (LodConfig.CLIENT.worldGenerator.lodQualityMode.get() == VerticalQuality.MULTI_LOD)
|
||||
{
|
||||
maxVerticalData = 256;
|
||||
}
|
||||
|
||||
// create adjData's arrays
|
||||
for (Direction direction : Box.ADJ_DIRECTIONS)
|
||||
{
|
||||
adjData.put(direction, new long[maxVerticalData]);
|
||||
}
|
||||
|
||||
//previous setToRender cache
|
||||
if (setsToRender[xR][zR] == null)
|
||||
{
|
||||
setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z);
|
||||
}
|
||||
|
||||
if (boxCache[xR][zR] == null)
|
||||
{
|
||||
boxCache[xR][zR] = new Box();
|
||||
}
|
||||
PosToRenderContainer posToRender = setsToRender[xR][zR];
|
||||
posToRender.clear(minDetail, regionPos.x, regionPos.z);
|
||||
|
||||
lodDim.getDataToRender(
|
||||
nodeToRender,
|
||||
posToRender,
|
||||
regionPos,
|
||||
playerBlockPosRounded.getX(),
|
||||
playerBlockPosRounded.getZ());
|
||||
|
||||
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
byte detailLevel;
|
||||
int xAdj;
|
||||
int zAdj;
|
||||
int chunkXdist;
|
||||
int chunkZdist;
|
||||
short gameChunkRenderDistance = (short) (renderer.vanillaRenderedChunks.length/2 - 1);
|
||||
for (LevelPos posToRender : nodeToRender.keySet())
|
||||
|
||||
// keep a local version so we don't have to worry about indexOutOfBounds Exceptions
|
||||
// if it changes in the LodRenderer while we are working here
|
||||
boolean[][] vanillaRenderedChunks = renderer.vanillaRenderedChunks;
|
||||
short gameChunkRenderDistance = (short) (vanillaRenderedChunks.length / 2 - 1);
|
||||
|
||||
for (int index = 0; index < posToRender.getNumberOfPos(); index++)
|
||||
{
|
||||
if (!nodeToRender.get(posToRender).booleanValue())
|
||||
detailLevel = posToRender.getNthDetailLevel(index);
|
||||
posX = posToRender.getNthPosX(index);
|
||||
posZ = posToRender.getNthPosZ(index);
|
||||
|
||||
// skip any chunks that Minecraft is going to render
|
||||
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x;
|
||||
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z;
|
||||
|
||||
if (gameChunkRenderDistance >= Math.abs(chunkXdist)
|
||||
&& gameChunkRenderDistance >= Math.abs(chunkZdist)
|
||||
&& detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL
|
||||
&& vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])
|
||||
{
|
||||
nodeToRender.remove(posToRender);
|
||||
continue;
|
||||
}
|
||||
nodeToRender.get(posToRender).setFalse();
|
||||
|
||||
// skip any chunks that Minecraft is going to render
|
||||
chunkXdist = posToRender.getChunkPosX() - playerChunkPos.x;
|
||||
chunkZdist = posToRender.getChunkPosZ() - playerChunkPos.z;
|
||||
if(gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist) && posToRender.detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL)
|
||||
for (Direction direction : Box.ADJ_DIRECTIONS)
|
||||
{
|
||||
if (renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])
|
||||
xAdj = posX + direction.getNormal().getX();
|
||||
zAdj = posZ + direction.getNormal().getZ();
|
||||
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.x;
|
||||
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.z;
|
||||
boolean performFaceCulling = true;
|
||||
if (performFaceCulling
|
||||
&& posToRender.contains(detailLevel, xAdj, zAdj)
|
||||
&& (gameChunkRenderDistance < Math.abs(chunkXdist)
|
||||
|| gameChunkRenderDistance < Math.abs(chunkZdist)
|
||||
|| !vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]))
|
||||
{
|
||||
continue;
|
||||
if (!adjData.containsKey(direction) || adjData.get(direction) == null)
|
||||
adjData.put(direction, new long[maxVerticalData]);
|
||||
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++)
|
||||
{
|
||||
long data = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex);
|
||||
adjData.get(direction)[verticalIndex] = data;
|
||||
}
|
||||
} else
|
||||
{
|
||||
adjData.put(direction, null);
|
||||
}
|
||||
}
|
||||
posX = posToRender.posX;
|
||||
posZ = posToRender.posZ;
|
||||
detailLevel = posToRender.detailLevel;
|
||||
// skip any chunks that Minecraft is going to render
|
||||
|
||||
|
||||
try
|
||||
|
||||
long data;
|
||||
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
|
||||
{
|
||||
boolean disableFix = false;
|
||||
if (lodDim.doesDataExist(posToRender.clone()))
|
||||
{
|
||||
short[] lodData = lodDim.getData(posToRender);
|
||||
short[][][] adjData = new short[2][2][];
|
||||
/**TODO The following two for are too complex*/
|
||||
for (int x : new int[]{0, 1})
|
||||
{
|
||||
posToRender.changeParameters(detailLevel, posX + x * 2 - 1, posZ);
|
||||
chunkXdist = posToRender.getChunkPosX() - playerChunkPos.x;
|
||||
chunkZdist = posToRender.getChunkPosZ() - playerChunkPos.z;
|
||||
if(gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist))
|
||||
{
|
||||
if (!renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1]
|
||||
&& (nodeToRender.containsKey(posToRender) || disableFix))
|
||||
{
|
||||
adjData[0][x] = lodDim.getData(posToRender);
|
||||
}
|
||||
}else{
|
||||
if (nodeToRender.containsKey(posToRender) || disableFix)
|
||||
{
|
||||
adjData[0][x] = lodDim.getData(posToRender);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int z : new int[]{0, 1})
|
||||
{
|
||||
posToRender.changeParameters(detailLevel, posX, posZ + z * 2 - 1);
|
||||
chunkXdist = posToRender.getChunkPosX() - playerChunkPos.x;
|
||||
chunkZdist = posToRender.getChunkPosZ() - playerChunkPos.z;
|
||||
if(gameChunkRenderDistance >= Math.abs(chunkXdist) && gameChunkRenderDistance >= Math.abs(chunkZdist))
|
||||
{
|
||||
if (!renderer.vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance+ 1]
|
||||
&& (nodeToRender.containsKey(posToRender) || disableFix))
|
||||
{
|
||||
adjData[1][z] = lodDim.getData(posToRender);
|
||||
}
|
||||
}else{
|
||||
if (nodeToRender.containsKey(posToRender) || disableFix)
|
||||
{
|
||||
adjData[1][z] = lodDim.getData(posToRender);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
posToRender.changeParameters(detailLevel, posX, posZ);
|
||||
|
||||
LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPos, lodData, adjData,
|
||||
posToRender, renderer.previousDebugMode);
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
return false;
|
||||
data = lodDim.getData(detailLevel, posX, posZ, verticalIndex);
|
||||
if (DataPointUtil.isItVoid(data) || !DataPointUtil.doesItExist(data))
|
||||
break;
|
||||
LodConfig.CLIENT.graphics.lodTemplate.get().template.addLodToBuffer(currentBuffer, playerBlockPosRounded, data, adjData,
|
||||
detailLevel, posX, posZ, boxCache[xR][zR], renderer.previousDebugMode, renderer.lightMap);
|
||||
}
|
||||
|
||||
}// for pos to in list to render
|
||||
|
||||
|
||||
|
||||
} // for pos to in list to render
|
||||
// the thread executed successfully
|
||||
return true;
|
||||
};
|
||||
nodeToRenderThreads.add(dataToRenderThread);
|
||||
}
|
||||
}// region z
|
||||
}// region z
|
||||
} // region z
|
||||
} // region z
|
||||
long renderStart = System.currentTimeMillis();
|
||||
// wait for all threads to finish
|
||||
List<Future<Boolean>> futuresBuffer = bufferBuilderThreads.invokeAll(nodeToRenderThreads);
|
||||
@@ -325,8 +354,7 @@ public class LodBufferBuilder
|
||||
}
|
||||
}
|
||||
long renderEnd = System.currentTimeMillis();
|
||||
|
||||
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
@SuppressWarnings("unused")
|
||||
long buildTime = endTime - startTime;
|
||||
@@ -334,11 +362,11 @@ public class LodBufferBuilder
|
||||
long treeTime = treeEnd - treeStart;
|
||||
@SuppressWarnings("unused")
|
||||
long renderingTime = renderEnd - renderStart;
|
||||
|
||||
// ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' +
|
||||
// "Tree cutting time: " + treeTime + " ms" + '\n' +
|
||||
// "Rendering time: " + renderingTime + " ms");
|
||||
|
||||
|
||||
// ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' +
|
||||
// "Tree cutting time: " + treeTime + " ms" + '\n' +
|
||||
// "Rendering time: " + renderingTime + " ms");
|
||||
|
||||
// mark that the buildable buffers as ready to swap
|
||||
switchVbos = true;
|
||||
} catch (Exception e)
|
||||
@@ -350,158 +378,64 @@ public class LodBufferBuilder
|
||||
// regardless of if we successfully created the buffers
|
||||
// we are done generating.
|
||||
generatingBuffers = false;
|
||||
|
||||
|
||||
// clean up any potentially open resources
|
||||
if (buildableBuffers != null)
|
||||
closeBuffers(fullRegen, lodDim);
|
||||
|
||||
|
||||
// upload the new buffers
|
||||
uploadBuffers(fullRegen, lodDim);
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
mainGenThread.execute(thread);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Move the center of this LodDimension and move all owned
|
||||
* regions over by the given x and z offset. <br><br>
|
||||
* <p>
|
||||
* Synchronized to prevent multiple moves happening on top of each other.
|
||||
*/
|
||||
public void move(RegionPos regionOffset, int width)
|
||||
{
|
||||
int xOffset = regionOffset.x;
|
||||
int zOffset = regionOffset.z;
|
||||
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total size, just delete the current data
|
||||
// and update the centerX and/or centerZ
|
||||
if (Math.abs(xOffset) >= width || Math.abs(zOffset) >= width)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
setsToRender[x][z] = null;
|
||||
}
|
||||
}
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// X
|
||||
if (xOffset > 0)
|
||||
{
|
||||
// move everything over to the left (as the center moves to the right)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (x + xOffset < width)
|
||||
setsToRender[x][z] = setsToRender[x + xOffset][z];
|
||||
else
|
||||
setsToRender[x][z] = null;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
// move everything over to the right (as the center moves to the left)
|
||||
for (int x = width - 1; x >= 0; x--)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (x + xOffset >= 0)
|
||||
setsToRender[x][z] = setsToRender[x + xOffset][z];
|
||||
else
|
||||
setsToRender[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Z
|
||||
if (zOffset > 0)
|
||||
{
|
||||
// move everything up (as the center moves down)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
if (z + zOffset < width)
|
||||
setsToRender[x][z] = setsToRender[x][z + zOffset];
|
||||
else
|
||||
setsToRender[x][z] = null;
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
// move everything down (as the center moves up)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = width - 1; z >= 0; z--)
|
||||
{
|
||||
if (z + zOffset >= 0)
|
||||
setsToRender[x][z] = setsToRender[x][z + zOffset];
|
||||
else
|
||||
setsToRender[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
}
|
||||
|
||||
|
||||
//===============================//
|
||||
// BufferBuilder related methods //
|
||||
//===============================//
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called from the LodRenderer to create the
|
||||
* BufferBuilders. <br><br>
|
||||
* <p>
|
||||
* May have to wait for the bufferLock to open.
|
||||
*/
|
||||
public void setupBuffers(int numbRegionsWide, int bufferMaxCapacity)
|
||||
public void setupBuffers(LodDimension lodDimension)
|
||||
{
|
||||
bufferLock.lock();
|
||||
|
||||
int numbRegionsWide = lodDimension.getWidth();
|
||||
int bufferMaxCapacity;
|
||||
|
||||
//if(previousRegionWidth != numbRegionsWide || firstSetup)
|
||||
//{
|
||||
firstSetup = false;
|
||||
previousRegionWidth = numbRegionsWide;
|
||||
previousBufferSize = bufferMaxCapacity;
|
||||
|
||||
|
||||
buildableBuffers = new BufferBuilder[numbRegionsWide][numbRegionsWide];
|
||||
|
||||
|
||||
buildableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
|
||||
drawableVbos = new VertexBuffer[numbRegionsWide][numbRegionsWide];
|
||||
|
||||
//}
|
||||
for (int x = 0; x < numbRegionsWide; x++)
|
||||
{
|
||||
for (int z = 0; z < numbRegionsWide; z++)
|
||||
{
|
||||
//if(lodDimension.isBufferToSetup(x,z))
|
||||
//{
|
||||
bufferMaxCapacity = lodDimension.getMemoryRequired(x, z, LodConfig.CLIENT.graphics.lodTemplate.get());
|
||||
buildableBuffers[x][z] = new BufferBuilder(bufferMaxCapacity);
|
||||
|
||||
buildableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
|
||||
drawableVbos[x][z] = new VertexBuffer(LodRenderer.LOD_VERTEX_FORMAT);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* sets the buffers and Vbos to null, forcing them to be recreated. <br><br>
|
||||
* <p>
|
||||
@@ -510,30 +444,31 @@ public class LodBufferBuilder
|
||||
public void destroyBuffers()
|
||||
{
|
||||
bufferLock.lock();
|
||||
|
||||
|
||||
buildableBuffers = null;
|
||||
buildableVbos = null;
|
||||
drawableVbos = null;
|
||||
|
||||
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Calls begin on each of the buildable BufferBuilders.
|
||||
*/
|
||||
private void startBuffers(boolean fullRegen, LodDimension lodDim)
|
||||
{
|
||||
for (int x = 0; x < buildableBuffers.length; x++)
|
||||
{
|
||||
for (int z = 0; z < buildableBuffers.length; z++)
|
||||
{
|
||||
if (fullRegen || lodDim.regen[x][z])
|
||||
if (fullRegen || lodDim.isRegionToRegen(x, z))
|
||||
{
|
||||
buildableBuffers[x][z].begin(GL11.GL_QUADS, LodRenderer.LOD_VERTEX_FORMAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls end on each of the buildable BufferBuilders.
|
||||
*/
|
||||
@@ -541,54 +476,118 @@ public class LodBufferBuilder
|
||||
{
|
||||
for (int x = 0; x < buildableBuffers.length; x++)
|
||||
for (int z = 0; z < buildableBuffers.length; z++)
|
||||
|
||||
if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building() && (fullRegen || lodDim.regen[x][z]))
|
||||
{
|
||||
if (buildableBuffers[x][z] != null && buildableBuffers[x][z].building() && (fullRegen || lodDim.isRegionToRegen(x, z)))
|
||||
buildableBuffers[x][z].end();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called from the LodRenderer to create the
|
||||
* BufferBuilders at the right size.
|
||||
* Upload all buildableBuffers to the GPU.
|
||||
*/
|
||||
private void uploadBuffers(boolean fullRegen, LodDimension lodDim)
|
||||
{
|
||||
for (int x = 0; x < buildableVbos.length; x++)
|
||||
GlProxy glProxy = GlProxy.getInstance();
|
||||
|
||||
try
|
||||
{
|
||||
for (int z = 0; z < buildableVbos.length; z++)
|
||||
// make sure we are uploading to a different OpenGL context,
|
||||
// to prevent interference (IE stuttering) with the Minecraft context.
|
||||
glProxy.setGlContext(GlProxyContext.LOD_BUILDER);
|
||||
|
||||
for (int x = 0; x < buildableVbos.length; x++)
|
||||
{
|
||||
if (fullRegen || lodDim.regen[x][z])
|
||||
for (int z = 0; z < buildableVbos.length; z++)
|
||||
{
|
||||
buildableVbos[x][z].upload(buildableBuffers[x][z]);
|
||||
lodDim.regen[x][z] = false;
|
||||
if (fullRegen || lodDim.isRegionToRegen(x, z))
|
||||
{
|
||||
ByteBuffer builderBuffer = buildableBuffers[x][z].popNextBuffer().getSecond();
|
||||
vboUpload(buildableVbos[x][z], builderBuffer);
|
||||
lodDim.setRegenByArrayIndex(x, z, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IllegalStateException e)
|
||||
{
|
||||
ClientProxy.LOGGER.error(LodBufferBuilder.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// make sure no buffer is bound
|
||||
if (glProxy.getGlContext() == GlProxyContext.LOD_BUILDER)
|
||||
{
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
// make sure the context is disabled
|
||||
glProxy.setGlContext(GlProxyContext.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Uploads the uploadBuffer into the VBO in GPU memory.
|
||||
*/
|
||||
private void vboUpload(VertexBuffer vbo, ByteBuffer uploadBuffer)
|
||||
{
|
||||
// this shouldn't happen, but just to be safe
|
||||
if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER)
|
||||
{
|
||||
// this is how many points will be rendered
|
||||
vbo.vertexCount = (uploadBuffer.remaining() / vbo.format.getVertexSize());
|
||||
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
|
||||
|
||||
// subData only works if the memory is allocated beforehand.
|
||||
GL15C.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer.remaining(), GL15C.GL_DYNAMIC_DRAW);
|
||||
|
||||
// interestingly bufferSubData renders faster than glMapBuffer
|
||||
// even though OpenGLInsights-AsynchronousBufferTransfers says glMapBuffer
|
||||
// is faster for transferring data. They must put the data in different memory
|
||||
// or something.
|
||||
GL15C.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the newly created VBOs
|
||||
*/
|
||||
public VertexBuffer[][] getVertexBuffers()
|
||||
public VertexBuffersAndOffset getVertexBuffers()
|
||||
{
|
||||
// don't wait for the lock to open
|
||||
// since this is called on the main render thread
|
||||
if (bufferLock.tryLock())
|
||||
{
|
||||
VertexBuffer[][] tmp = drawableVbos;
|
||||
VertexBuffer[][] tmpVbo = drawableVbos;
|
||||
drawableVbos = buildableVbos;
|
||||
buildableVbos = tmp;
|
||||
|
||||
buildableVbos = tmpVbo;
|
||||
|
||||
drawableCenterChunkPos = buildableCenterChunkPos;
|
||||
|
||||
// the vbos have been swapped
|
||||
switchVbos = false;
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
return drawableVbos;
|
||||
|
||||
return new VertexBuffersAndOffset(drawableVbos, drawableCenterChunkPos);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simple container to pass multiple objects back in the getVertexBuffers method.
|
||||
*/
|
||||
public class VertexBuffersAndOffset
|
||||
{
|
||||
public VertexBuffer[][] vbos;
|
||||
public ChunkPos drawableCenterChunkPos;
|
||||
|
||||
public VertexBuffersAndOffset(VertexBuffer[][] newVbos, ChunkPos newDrawableCenterChunkPos)
|
||||
{
|
||||
vbos = newVbos;
|
||||
drawableCenterChunkPos = newDrawableCenterChunkPos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is true the buildable near and far
|
||||
* buffers have been generated and are ready to be
|
||||
@@ -598,6 +597,5 @@ public class LodBufferBuilder
|
||||
{
|
||||
return switchVbos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,10 +17,14 @@
|
||||
*/
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.enums.DebugMode;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.util.ColorUtil;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
/**
|
||||
@@ -32,22 +36,25 @@ import net.minecraft.util.math.BlockPos;
|
||||
*/
|
||||
public abstract class AbstractLodTemplate
|
||||
{
|
||||
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
|
||||
LevelPos levelPos, DebugMode debugging);
|
||||
|
||||
|
||||
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap);
|
||||
|
||||
/**
|
||||
* add the given position and color to the buffer
|
||||
*/
|
||||
protected void addPosAndColor(BufferBuilder buffer,
|
||||
double x, double y, double z,
|
||||
int red, int green, int blue, int alpha)
|
||||
int color)
|
||||
{
|
||||
buffer.vertex(x, y, z).color(red, green, blue, alpha).endVertex();
|
||||
|
||||
buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), ColorUtil.getAlpha(color)).endVertex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns in bytes how much buffer memory is required
|
||||
* for one LOD object
|
||||
*/
|
||||
public abstract int getBufferMemoryForSingleNode();
|
||||
public abstract int getBufferMemoryForSingleNode(int maxVerticalData);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,448 @@
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DebugMode;
|
||||
import com.seibel.lod.util.ColorUtil;
|
||||
import com.seibel.lod.util.DataPointUtil;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
|
||||
public class Box
|
||||
{
|
||||
|
||||
public static final int OFFSET = 0;
|
||||
public static final int WIDTH = 1;
|
||||
|
||||
public static final int X = 0;
|
||||
public static final int Y = 1;
|
||||
public static final int Z = 2;
|
||||
|
||||
public static final int MIN = 0;
|
||||
public static final int MAX = 1;
|
||||
|
||||
public static final int VOID_FACE = 0;
|
||||
|
||||
public static final Direction[] DIRECTIONS = new Direction[]{
|
||||
Direction.UP,
|
||||
Direction.DOWN,
|
||||
Direction.WEST,
|
||||
Direction.EAST,
|
||||
Direction.NORTH,
|
||||
Direction.SOUTH};
|
||||
|
||||
public static final Direction[] ADJ_DIRECTIONS = new Direction[]{
|
||||
Direction.EAST,
|
||||
Direction.WEST,
|
||||
Direction.SOUTH,
|
||||
Direction.NORTH};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<Direction, int[][]> DIRECTION_VERTEX_MAP = new HashMap<Direction, int[][]>()
|
||||
{{
|
||||
put(Direction.UP, new int[][]{
|
||||
{0, 1, 0},
|
||||
{0, 1, 1},
|
||||
{1, 1, 1},
|
||||
{1, 1, 0}});
|
||||
put(Direction.DOWN, new int[][]{
|
||||
{1, 0, 0},
|
||||
{1, 0, 1},
|
||||
{0, 0, 1},
|
||||
{0, 0, 0}});
|
||||
put(Direction.EAST, new int[][]{
|
||||
{1, 1, 0},
|
||||
{1, 1, 1},
|
||||
{1, 0, 1},
|
||||
{1, 0, 0}});
|
||||
put(Direction.WEST, new int[][]{
|
||||
{0, 0, 0},
|
||||
{0, 0, 1},
|
||||
{0, 1, 1},
|
||||
{0, 1, 0}});
|
||||
put(Direction.SOUTH, new int[][]{
|
||||
{1, 0, 1},
|
||||
{1, 1, 1},
|
||||
{0, 1, 1},
|
||||
{0, 0, 1}});
|
||||
put(Direction.NORTH, new int[][]{
|
||||
{0, 0, 0},
|
||||
{0, 1, 0},
|
||||
{1, 1, 0},
|
||||
{1, 0, 0}});
|
||||
}};
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<Direction, int[]> FACE_DIRECTION = new HashMap<Direction, int[]>()
|
||||
{{
|
||||
put(Direction.UP, new int[]{Y, MAX});
|
||||
put(Direction.DOWN, new int[]{Y, MIN});
|
||||
put(Direction.EAST, new int[]{X, MAX});
|
||||
put(Direction.WEST, new int[]{X, MIN});
|
||||
put(Direction.SOUTH, new int[]{Z, MAX});
|
||||
put(Direction.NORTH, new int[]{Z, MIN});
|
||||
}};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<Direction, int[]> DIRECTION_NORMAL_MAP = new HashMap<Direction, int[]>()
|
||||
{{
|
||||
put(Direction.UP, new int[]{0, 1, 0});
|
||||
put(Direction.DOWN, new int[]{0, -1, 0});
|
||||
put(Direction.EAST, new int[]{1, 0, 0});
|
||||
put(Direction.WEST, new int[]{-1, 0, 0});
|
||||
put(Direction.SOUTH, new int[]{0, 0, 1});
|
||||
put(Direction.NORTH, new int[]{0, 0, -1});
|
||||
}};
|
||||
|
||||
public int[][] box;
|
||||
public long[] order;
|
||||
public Map<Direction, int[]> colorMap;
|
||||
public int debugColor;
|
||||
public Map<Direction, int[][]> adjHeightAndDepth;
|
||||
public Map<Direction, boolean[]> culling;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public Box()
|
||||
{
|
||||
box = new int[2][3];
|
||||
//order = new long[DetailDistanceUtil.getMaxVerticalData(0)];
|
||||
colorMap = new HashMap<Direction, int[]>()
|
||||
{{
|
||||
put(Direction.UP, new int[1]);
|
||||
put(Direction.DOWN, new int[1]);
|
||||
put(Direction.EAST, new int[1]);
|
||||
put(Direction.WEST, new int[1]);
|
||||
put(Direction.SOUTH, new int[1]);
|
||||
put(Direction.NORTH, new int[1]);
|
||||
}};
|
||||
adjHeightAndDepth = new HashMap<Direction, int[][]>()
|
||||
{{
|
||||
put(Direction.EAST, new int[32][2]);
|
||||
put(Direction.WEST, new int[32][2]);
|
||||
put(Direction.SOUTH, new int[32][2]);
|
||||
put(Direction.NORTH, new int[32][2]);
|
||||
}};
|
||||
culling = new HashMap<Direction, boolean[]>()
|
||||
{{
|
||||
put(Direction.UP, new boolean[1]);
|
||||
put(Direction.DOWN, new boolean[1]);
|
||||
put(Direction.EAST, new boolean[1]);
|
||||
put(Direction.WEST, new boolean[1]);
|
||||
put(Direction.SOUTH, new boolean[1]);
|
||||
put(Direction.NORTH, new boolean[1]);
|
||||
}};
|
||||
}
|
||||
|
||||
public void setColor(int color)
|
||||
{
|
||||
this.debugColor = color;
|
||||
for (Direction direction : DIRECTIONS)
|
||||
{
|
||||
colorMap.get(direction)[0] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true));
|
||||
}
|
||||
}
|
||||
|
||||
public int getColor(Direction direction)
|
||||
{
|
||||
if (LodConfig.CLIENT.debugging.debugMode.get() != DebugMode.SHOW_DETAIL)
|
||||
{
|
||||
return colorMap.get(direction)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return ColorUtil.applyShade(debugColor, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true));
|
||||
}
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
for (int i = 0; i < box.length; i++)
|
||||
{
|
||||
Arrays.fill(box[i], 0);
|
||||
}
|
||||
|
||||
for (Direction direction : DIRECTIONS)
|
||||
{
|
||||
colorMap.get(direction)[0] = 0;
|
||||
}
|
||||
|
||||
//Arrays.fill(order, DataPointUtil.EMPTY_DATA);
|
||||
for (Direction direction : ADJ_DIRECTIONS)
|
||||
{
|
||||
if(isCulled(direction)){
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < adjHeightAndDepth.get(direction).length; i++)
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[i][0] = VOID_FACE;
|
||||
adjHeightAndDepth.get(direction)[i][1] = VOID_FACE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setUpCulling(int cullingDistance, BlockPos playerPos)
|
||||
{
|
||||
for (Direction direction : DIRECTIONS)
|
||||
{
|
||||
if(direction == Direction.DOWN)
|
||||
culling.get(direction)[0] = playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance;
|
||||
else if(direction == Direction.UP)
|
||||
culling.get(direction)[0] = playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance;
|
||||
else if(direction == Direction.WEST)
|
||||
culling.get(direction)[0] = -playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance;
|
||||
else if(direction == Direction.NORTH)
|
||||
culling.get(direction)[0] = -playerPos.get(direction.getAxis()) > getFacePos(direction) + cullingDistance;
|
||||
else if(direction == Direction.EAST)
|
||||
culling.get(direction)[0] = -playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance;
|
||||
else if(direction == Direction.SOUTH)
|
||||
culling.get(direction)[0] = -playerPos.get(direction.getAxis()) < getFacePos(direction) - cullingDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCulled(Direction direction)
|
||||
{
|
||||
return culling.get(direction)[0];
|
||||
}
|
||||
|
||||
public void setAdjData(Map<Direction, long[]> adjData)
|
||||
{
|
||||
int height;
|
||||
int depth;
|
||||
int minY = getMinY();
|
||||
int maxY = getMaxY();
|
||||
for (Direction direction : ADJ_DIRECTIONS)
|
||||
{
|
||||
/*if(isCulled(direction)){
|
||||
continue;
|
||||
}*/
|
||||
|
||||
long[] dataPoint = adjData.get(direction);
|
||||
if (dataPoint == null || DataPointUtil.isItVoid(dataPoint[0]))
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[0][0] = maxY;
|
||||
adjHeightAndDepth.get(direction)[0][1] = minY;
|
||||
adjHeightAndDepth.get(direction)[1][0] = VOID_FACE;
|
||||
adjHeightAndDepth.get(direction)[1][1] = VOID_FACE;
|
||||
continue;
|
||||
}
|
||||
|
||||
//We order the adj list
|
||||
/**TODO remove this if the order is maintained naturally*/
|
||||
/*order[0] = 0;
|
||||
int count = 0;
|
||||
for (int i = 0; i < dataPoint.length; i++)
|
||||
{
|
||||
int j = i - 1;
|
||||
if(DataPointUtil.isItVoid(dataPoint[i]) || !DataPointUtil.doesItExist(dataPoint[i]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
while (j >= 0 && DataPointUtil.getHeight(order[j]) < DataPointUtil.getHeight(dataPoint[i]))
|
||||
{
|
||||
order[j + 1] = order[j];
|
||||
j = j - 1;
|
||||
}
|
||||
order[j + 1] = dataPoint[i];
|
||||
count++;
|
||||
}*/
|
||||
|
||||
int i;
|
||||
int faceToDraw = 0;
|
||||
boolean firstFace = true;
|
||||
boolean toFinish = false;
|
||||
boolean allAbove = true;
|
||||
long singleAdjDataPoint;
|
||||
for (i = 0; i < dataPoint.length; i++)
|
||||
{
|
||||
singleAdjDataPoint = dataPoint[i];
|
||||
/*for (i = 0; i < count; i++)
|
||||
{
|
||||
singleAdjDataPoint = order[i];*/
|
||||
|
||||
if(DataPointUtil.isItVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||
{
|
||||
break;
|
||||
}
|
||||
height = DataPointUtil.getHeight(singleAdjDataPoint);
|
||||
depth = DataPointUtil.getDepth(singleAdjDataPoint);
|
||||
|
||||
if (depth <= maxY) {
|
||||
allAbove = false;
|
||||
if (height < minY)
|
||||
{//the adj data is lower than the current data
|
||||
//we break since all the other data will be lower
|
||||
|
||||
if (firstFace)
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[0][0] = getMaxY();
|
||||
adjHeightAndDepth.get(direction)[0][1] = getMinY();
|
||||
} else
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][1] = getMinY();
|
||||
}
|
||||
faceToDraw++;
|
||||
toFinish = false;
|
||||
break;
|
||||
} else if (depth <= minY && height >= maxY)
|
||||
{//the adj data contains the current
|
||||
//we do not draw the face
|
||||
adjHeightAndDepth.get(direction)[0][0] = VOID_FACE;
|
||||
adjHeightAndDepth.get(direction)[0][1] = VOID_FACE;
|
||||
break;
|
||||
} else if (depth <= minY)//&& height < maxY
|
||||
{//the adj data intersect the lower part of the current data
|
||||
//if this is the only face we use the maxY and break
|
||||
//if there was other face we finish the last one and break
|
||||
if (firstFace)
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[0][0] = getMaxY();
|
||||
adjHeightAndDepth.get(direction)[0][1] = height;
|
||||
} else
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][1] = height;
|
||||
}
|
||||
toFinish = false;
|
||||
faceToDraw++;
|
||||
break;
|
||||
} else if (height >= maxY)//depth > minY &&
|
||||
{//the adj data intersect the higher part of the current data
|
||||
//we start the creation of a new face
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][0] = depth;
|
||||
firstFace = false;
|
||||
toFinish = true;
|
||||
} else {//if (depth > minY && height < maxY)
|
||||
//the adj data is contained in the current data
|
||||
if (firstFace)
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[0][0] = getMaxY();
|
||||
}
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][1] = height;
|
||||
faceToDraw++;
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][0] = depth;
|
||||
firstFace = false;
|
||||
toFinish = true;
|
||||
}
|
||||
}
|
||||
//else {//the adj data is higher than the current data
|
||||
//we continue since there could be some other data that intersect the current
|
||||
//}
|
||||
}
|
||||
if(allAbove){
|
||||
adjHeightAndDepth.get(direction)[0][0] = getMaxY();
|
||||
adjHeightAndDepth.get(direction)[0][1] = getMinY();
|
||||
faceToDraw++;
|
||||
}
|
||||
else if (toFinish)
|
||||
{
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][1] = minY;
|
||||
faceToDraw++;
|
||||
}
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][0] = VOID_FACE;
|
||||
adjHeightAndDepth.get(direction)[faceToDraw][1] = VOID_FACE;
|
||||
}
|
||||
}
|
||||
|
||||
public void set(int xWidth, int yWidth, int zWidth)
|
||||
{
|
||||
box[WIDTH][X] = xWidth;
|
||||
box[WIDTH][Y] = yWidth;
|
||||
box[WIDTH][Z] = zWidth;
|
||||
}
|
||||
|
||||
public void move(int xOffset, int yOffset, int zOffset)
|
||||
{
|
||||
box[OFFSET][X] = xOffset;
|
||||
box[OFFSET][Y] = yOffset;
|
||||
box[OFFSET][Z] = zOffset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getFacePos(Direction direction)
|
||||
{
|
||||
return box[OFFSET][FACE_DIRECTION.get(direction)[0]] + box[WIDTH][FACE_DIRECTION.get(direction)[0]] * FACE_DIRECTION.get(direction)[1];
|
||||
}
|
||||
|
||||
public int getCoord(Direction direction, int axis, int vertexIndex)
|
||||
{
|
||||
return box[OFFSET][axis] + box[WIDTH][axis] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][axis];
|
||||
}
|
||||
|
||||
public int getX(Direction direction, int vertexIndex)
|
||||
{
|
||||
return box[OFFSET][X] + box[WIDTH][X] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][X];
|
||||
}
|
||||
|
||||
public int getY(Direction direction, int vertexIndex)
|
||||
{
|
||||
return box[OFFSET][Y] + box[WIDTH][Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y];
|
||||
}
|
||||
|
||||
public int getY(Direction direction, int vertexIndex, int adjIndex)
|
||||
{
|
||||
if (direction == Direction.DOWN || direction == Direction.UP)
|
||||
{
|
||||
return box[OFFSET][Y] + box[WIDTH][Y] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y];
|
||||
} else
|
||||
{
|
||||
return adjHeightAndDepth.get(direction)[adjIndex][1 - DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Y]];
|
||||
}
|
||||
}
|
||||
|
||||
public int getZ(Direction direction, int vertexIndex)
|
||||
{
|
||||
return box[OFFSET][Z] + box[WIDTH][Z] * DIRECTION_VERTEX_MAP.get(direction)[vertexIndex][Z];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the given direction should be rendered.
|
||||
* TODO what does adjIndex represent?
|
||||
*/
|
||||
public boolean shouldRenderFace(Direction direction, int adjIndex)
|
||||
{
|
||||
if (direction == Direction.UP || direction == Direction.DOWN)
|
||||
{
|
||||
return adjIndex == 0;
|
||||
}
|
||||
return !(adjHeightAndDepth.get(direction)[adjIndex][0] == VOID_FACE && adjHeightAndDepth.get(direction)[adjIndex][1] == VOID_FACE);
|
||||
|
||||
}
|
||||
|
||||
public int getMinX()
|
||||
{
|
||||
return box[OFFSET][X];
|
||||
}
|
||||
|
||||
public int getMaxX()
|
||||
{
|
||||
return box[OFFSET][X] + box[WIDTH][X];
|
||||
}
|
||||
|
||||
public int getMinY()
|
||||
{
|
||||
return box[OFFSET][Y];
|
||||
}
|
||||
|
||||
public int getMaxY()
|
||||
{
|
||||
return box[OFFSET][Y] + box[WIDTH][Y];
|
||||
}
|
||||
|
||||
public int getMinZ()
|
||||
{
|
||||
return box[OFFSET][Z];
|
||||
}
|
||||
|
||||
public int getMaxZ()
|
||||
{
|
||||
return box[OFFSET][Z] + box[WIDTH][Z];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,18 +17,15 @@
|
||||
*/
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.enums.DebugMode;
|
||||
import com.seibel.lod.enums.ShadingMode;
|
||||
import com.seibel.lod.objects.DataPoint;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.util.ColorUtil;
|
||||
import com.seibel.lod.util.DataPointUtil;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
/**
|
||||
@@ -39,7 +36,6 @@ import net.minecraft.util.math.BlockPos;
|
||||
*/
|
||||
public class CubicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
private final int CULL_OFFSET = 16;
|
||||
|
||||
public CubicLodTemplate()
|
||||
{
|
||||
@@ -47,40 +43,41 @@ public class CubicLodTemplate extends AbstractLodTemplate
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
|
||||
LevelPos levelPos, DebugMode debugging)
|
||||
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap)
|
||||
{
|
||||
AxisAlignedBB bbox;
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
int width = 1 << levelPos.detailLevel;
|
||||
|
||||
// add each LOD for the detail level
|
||||
bbox = generateBoundingBox(
|
||||
DataPoint.getHeight(data),
|
||||
DataPoint.getDepth(data),
|
||||
width,
|
||||
levelPos.posX * width,
|
||||
0,
|
||||
levelPos.posZ * width);
|
||||
|
||||
int color = DataPoint.getColor(data);
|
||||
int color = DataPointUtil.getLightColor(data, lightMap);
|
||||
if (debugging != DebugMode.OFF)
|
||||
{
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[levelPos.detailLevel].getRGB();
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
|
||||
}
|
||||
|
||||
if (bbox != null)
|
||||
generateBoundingBox(
|
||||
box,
|
||||
DataPointUtil.getHeight(data),
|
||||
DataPointUtil.getDepth(data),
|
||||
width,
|
||||
posX * width,
|
||||
0,
|
||||
posZ * width,
|
||||
bufferCenterBlockPos,
|
||||
adjData,
|
||||
color);
|
||||
|
||||
if (box != null)
|
||||
{
|
||||
addBoundingBoxToBuffer(buffer, bbox, color, playerBlockPos, adjData);
|
||||
addBoundingBoxToBuffer(buffer, box);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private AxisAlignedBB generateBoundingBox(int height, int depth, int width, double xOffset, double yOffset, double zOffset)
|
||||
private void generateBoundingBox(Box box, int height, int depth, int width, double xOffset, double yOffset, double zOffset, BlockPos bufferCenterBlockPos, Map<Direction, long[]> adjData, int color)
|
||||
{
|
||||
// don't add an LOD if it is empty
|
||||
if (height == -1 && depth == -1)
|
||||
return null;
|
||||
return;
|
||||
|
||||
if (depth == height)
|
||||
{
|
||||
@@ -89,240 +86,44 @@ public class CubicLodTemplate extends AbstractLodTemplate
|
||||
height++;
|
||||
}
|
||||
|
||||
return new AxisAlignedBB(0, depth, 0, width, height, width).move(xOffset, yOffset, zOffset);
|
||||
// offset the AABB by it's x/z position in the world since
|
||||
// it uses doubles to specify its location, unlike the model view matrix
|
||||
// which only uses floats
|
||||
double x = -bufferCenterBlockPos.getX();
|
||||
double z = -bufferCenterBlockPos.getZ();
|
||||
box.reset();
|
||||
box.setColor(color);
|
||||
box.set(width, height - depth, width);
|
||||
box.move((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
|
||||
box.setUpCulling(32, bufferCenterBlockPos);
|
||||
box.setAdjData(adjData);
|
||||
}
|
||||
|
||||
private void addBoundingBoxToBuffer(BufferBuilder buffer, AxisAlignedBB bb, int c, BlockPos playerBlockPos, short[][][] adjData)
|
||||
private void addBoundingBoxToBuffer(BufferBuilder buffer, Box box)
|
||||
{
|
||||
int topColor = c;
|
||||
int bottomColor = c;
|
||||
int northColor = c;
|
||||
int southColor = c;
|
||||
int westColor = c;
|
||||
int eastColor = c;
|
||||
|
||||
// darken the bottom and side colors if requested
|
||||
if (LodConfig.CLIENT.graphics.shadingMode.get() == ShadingMode.DARKEN_SIDES)
|
||||
for (Direction direction : Box.DIRECTIONS)
|
||||
{
|
||||
// the side colors are different because
|
||||
// when using fast lighting in Minecraft the north/south
|
||||
// and east/west sides are different in a similar way
|
||||
/**TODO OPTIMIZE THIS STEP*/
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
topColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.UP, true));
|
||||
bottomColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.DOWN, true));
|
||||
northColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.NORTH, true));
|
||||
southColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.SOUTH, true));
|
||||
westColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.WEST, true));
|
||||
eastColor = ColorUtil.applyShade(c, mc.level.getShade(Direction.EAST, true));
|
||||
}
|
||||
|
||||
// apply the user specified saturation and brightness
|
||||
float saturationMultiplier = LodConfig.CLIENT.graphics.saturationMultiplier.get().floatValue();
|
||||
float brightnessMultiplier = LodConfig.CLIENT.graphics.brightnessMultiplier.get().floatValue();
|
||||
|
||||
if (saturationMultiplier != 1 || brightnessMultiplier != 1)
|
||||
{
|
||||
topColor = ColorUtil.applySaturationAndBrightnessMultipliers(topColor, saturationMultiplier, brightnessMultiplier);
|
||||
bottomColor = ColorUtil.applySaturationAndBrightnessMultipliers(bottomColor, saturationMultiplier, brightnessMultiplier);
|
||||
northColor = ColorUtil.applySaturationAndBrightnessMultipliers(northColor, saturationMultiplier, brightnessMultiplier);
|
||||
southColor = ColorUtil.applySaturationAndBrightnessMultipliers(southColor, saturationMultiplier, brightnessMultiplier);
|
||||
westColor = ColorUtil.applySaturationAndBrightnessMultipliers(westColor, saturationMultiplier, brightnessMultiplier);
|
||||
eastColor = ColorUtil.applySaturationAndBrightnessMultipliers(eastColor, saturationMultiplier, brightnessMultiplier);
|
||||
}
|
||||
int minY;
|
||||
int maxY;
|
||||
short[] data;
|
||||
|
||||
int red;
|
||||
int green;
|
||||
int blue;
|
||||
int alpha;
|
||||
boolean disableCulling = true;
|
||||
/**TODO make all of this more automatic if possible*/
|
||||
if (playerBlockPos.getY() > bb.maxY - CULL_OFFSET || disableCulling)
|
||||
{
|
||||
red = ColorUtil.getRed(topColor);
|
||||
green = ColorUtil.getGreen(topColor);
|
||||
blue = ColorUtil.getBlue(topColor);
|
||||
alpha = ColorUtil.getAlpha(topColor);
|
||||
// top (facing up)
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
if (playerBlockPos.getY() < bb.minY + CULL_OFFSET || disableCulling)
|
||||
{
|
||||
red = ColorUtil.getRed(bottomColor);
|
||||
green = ColorUtil.getGreen(bottomColor);
|
||||
blue = ColorUtil.getBlue(bottomColor);
|
||||
alpha = ColorUtil.getAlpha(bottomColor);
|
||||
// bottom (facing down)
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
|
||||
if (playerBlockPos.getZ() > bb.minZ - CULL_OFFSET || disableCulling)
|
||||
{
|
||||
red = ColorUtil.getRed(northColor);
|
||||
green = ColorUtil.getGreen(northColor);
|
||||
blue = ColorUtil.getBlue(northColor);
|
||||
alpha = ColorUtil.getAlpha(northColor);
|
||||
// south (facing -Z)
|
||||
data = adjData[1][1];
|
||||
if (data == null)
|
||||
int adjIndex = 0;
|
||||
while (box.shouldRenderFace(direction, adjIndex))
|
||||
{
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxY = DataPoint.getHeight(data);
|
||||
if (maxY < bb.maxY)
|
||||
for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++)
|
||||
{
|
||||
minY = (int) Math.max(maxY, bb.minY);
|
||||
addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha);
|
||||
}
|
||||
minY = DataPoint.getDepth(data);
|
||||
if (minY > bb.minY)
|
||||
{
|
||||
maxY = (int) Math.min(minY, bb.maxY);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (playerBlockPos.getZ() < bb.maxZ + CULL_OFFSET || disableCulling)
|
||||
{
|
||||
red = ColorUtil.getRed(southColor);
|
||||
green = ColorUtil.getGreen(southColor);
|
||||
blue = ColorUtil.getBlue(southColor);
|
||||
alpha = ColorUtil.getAlpha(southColor);
|
||||
data = adjData[1][0];
|
||||
// north (facing +Z)
|
||||
if (data == null)
|
||||
{
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxY = DataPoint.getHeight(data);
|
||||
if (maxY < bb.maxY)
|
||||
{
|
||||
minY = (int) Math.max(maxY, bb.minY);
|
||||
addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
minY = DataPoint.getDepth(data);
|
||||
if (minY > bb.minY)
|
||||
{
|
||||
maxY = (int) Math.min(minY, bb.maxY);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (playerBlockPos.getX() < bb.maxX + CULL_OFFSET || disableCulling)
|
||||
{
|
||||
red = ColorUtil.getRed(westColor);
|
||||
green = ColorUtil.getGreen(westColor);
|
||||
blue = ColorUtil.getBlue(westColor);
|
||||
alpha = ColorUtil.getAlpha(westColor);
|
||||
// west (facing -X)
|
||||
data = adjData[0][0];
|
||||
if (data == null)
|
||||
{
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxY = DataPoint.getHeight(data);
|
||||
if (maxY < bb.maxY)
|
||||
{
|
||||
minY = (int) Math.max(maxY, bb.minY);
|
||||
addPosAndColor(buffer, bb.minX, minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
minY = DataPoint.getDepth(data);
|
||||
if (minY > bb.minY)
|
||||
{
|
||||
maxY = (int) Math.min(minY, bb.maxY);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.minX, maxY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (playerBlockPos.getX() > bb.minX - CULL_OFFSET || disableCulling)
|
||||
{
|
||||
red = ColorUtil.getRed(eastColor);
|
||||
green = ColorUtil.getGreen(eastColor);
|
||||
blue = ColorUtil.getBlue(eastColor);
|
||||
alpha = ColorUtil.getAlpha(eastColor);
|
||||
// east (facing +X)
|
||||
data = adjData[0][1];
|
||||
if (data == null)
|
||||
{
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxY = DataPoint.getHeight(data);
|
||||
if (maxY < bb.maxY)
|
||||
{
|
||||
minY = (int) Math.max(maxY, bb.minY);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, minY, bb.minZ, red, green, blue, alpha);
|
||||
}
|
||||
minY = DataPoint.getDepth(data);
|
||||
if (minY > bb.minY)
|
||||
{
|
||||
maxY = (int) Math.min(minY, bb.maxY);
|
||||
addPosAndColor(buffer, bb.maxX, maxY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, maxY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
|
||||
addPosAndColor(buffer,
|
||||
box.getX(direction, vertexIndex),
|
||||
box.getY(direction, vertexIndex, adjIndex),
|
||||
box.getZ(direction, vertexIndex),
|
||||
box.getColor(direction));
|
||||
}
|
||||
adjIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleNode()
|
||||
public int getBufferMemoryForSingleNode(int maxVerticalData)
|
||||
{
|
||||
// (sidesOnACube * pointsInASquare * (positionPoints + colorPoints)))
|
||||
return (6 * 4 * (3 + 4));
|
||||
// TODO, someone please comment what these magic numbers mean
|
||||
return 2 * 4 * (3 + 4) + 4 * 4 * Math.max((maxVerticalData+1)/2,1) * (3 + 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
*/
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.enums.DebugMode;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
/**
|
||||
@@ -35,14 +39,14 @@ import net.minecraft.util.math.BlockPos;
|
||||
public class DynamicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
|
||||
LevelPos levelPos, DebugMode debugging)
|
||||
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleNode()
|
||||
public int getBufferMemoryForSingleNode(int maxVerticalData)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
*/
|
||||
package com.seibel.lod.builders.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.enums.DebugMode;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
|
||||
import net.minecraft.client.renderer.BufferBuilder;
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
/**
|
||||
@@ -33,14 +37,14 @@ import net.minecraft.util.math.BlockPos;
|
||||
public class TriangularLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(BufferBuilder buffer, BlockPos playerBlockPos, short[] data, short[][][] adjData,
|
||||
LevelPos levelPos, DebugMode debugging)
|
||||
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, NativeImage lightMap)
|
||||
{
|
||||
System.err.println("DynamicLodTemplate not implemented!");
|
||||
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferMemoryForSingleNode()
|
||||
public int getBufferMemoryForSingleNode(int maxVerticalData)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -26,13 +26,13 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.builders.LodBuilder;
|
||||
import com.seibel.lod.builders.LodBuilderConfig;
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
@@ -69,11 +69,12 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-26-2021
|
||||
* @version 9-7-2021
|
||||
*/
|
||||
public class LodNodeGenWorker implements IWorker
|
||||
{
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
//public static ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new LodThreadFactory(LodNodeGenWorker.class.getSimpleName()));
|
||||
|
||||
private boolean threadStarted = false;
|
||||
private LodChunkGenThread thread;
|
||||
@@ -86,7 +87,7 @@ public class LodNodeGenWorker implements IWorker
|
||||
|
||||
|
||||
|
||||
public LodNodeGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode, LodRenderer newLodRenderer,
|
||||
public LodNodeGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
@@ -94,9 +95,6 @@ public class LodNodeGenWorker implements IWorker
|
||||
if (newPos == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
|
||||
|
||||
if (newLodRenderer == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null LodRenderer");
|
||||
|
||||
if (newLodBuilder == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
|
||||
|
||||
@@ -108,11 +106,15 @@ public class LodNodeGenWorker implements IWorker
|
||||
|
||||
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newGenerationMode, newLodRenderer,
|
||||
thread = new LodChunkGenThread(newPos, newGenerationMode,
|
||||
newLodBuilder,
|
||||
newLodDimension, newServerWorld);
|
||||
}
|
||||
|
||||
public static void resetGenerator(){
|
||||
ExecutorService genThreads = Executors.newFixedThreadPool(LodConfig.CLIENT.threading.numberOfWorldGenerationThreads.get(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
@@ -130,7 +132,7 @@ public class LodNodeGenWorker implements IWorker
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
Thread newThread = new Thread(thread);
|
||||
newThread.setPriority(3);
|
||||
newThread.setPriority(5);
|
||||
genThreads.execute(newThread);
|
||||
}
|
||||
|
||||
@@ -159,17 +161,15 @@ public class LodNodeGenWorker implements IWorker
|
||||
public final LodDimension lodDim;
|
||||
public final DistanceGenerationMode generationMode;
|
||||
public final LodBuilder lodBuilder;
|
||||
public final LodRenderer lodRenderer;
|
||||
|
||||
private ChunkPos pos;
|
||||
|
||||
public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode, LodRenderer newLodRenderer,
|
||||
public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, ServerWorld newServerWorld)
|
||||
{
|
||||
pos = newPos;
|
||||
generationMode = newGenerationMode;
|
||||
lodRenderer = newLodRenderer;
|
||||
lodBuilder = newLodBuilder;
|
||||
lodDim = newLodDimension;
|
||||
serverWorld = newServerWorld;
|
||||
@@ -228,9 +228,9 @@ public class LodNodeGenWorker implements IWorker
|
||||
// System.out.println(endTime - startTime);
|
||||
|
||||
}// if in range
|
||||
else{
|
||||
//else{
|
||||
|
||||
}
|
||||
//}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -588,16 +588,11 @@ public class LodNodeGenWorker implements IWorker
|
||||
private BlockClusterFeatureConfig cloneBlockClusterFeatureConfig(BlockClusterFeatureConfig config)
|
||||
{
|
||||
WeightedBlockStateProvider provider = new WeightedBlockStateProvider();
|
||||
for(Entry<BlockState> state : ((WeightedBlockStateProvider) config.stateProvider).weightedList.entries)
|
||||
provider.weightedList.entries.add(state);
|
||||
provider.weightedList.entries.addAll(((WeightedBlockStateProvider) config.stateProvider).weightedList.entries);
|
||||
|
||||
HashSet<Block> whitelist = new HashSet<>();
|
||||
for(Block block : config.whitelist)
|
||||
whitelist.add(block);
|
||||
HashSet<Block> whitelist = new HashSet<>(config.whitelist);
|
||||
|
||||
HashSet<BlockState> blacklist = new HashSet<>();
|
||||
for(BlockState state : config.blacklist)
|
||||
blacklist.add(state);
|
||||
HashSet<BlockState> blacklist = new HashSet<>(config.blacklist);
|
||||
|
||||
|
||||
BlockClusterFeatureConfig.Builder builder = new BlockClusterFeatureConfig.Builder(provider, config.blockPlacer);
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
package com.seibel.lod.builders.worldGeneration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
|
||||
import com.seibel.lod.builders.GenerationRequest;
|
||||
import com.seibel.lod.builders.LodBuilder;
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.util.LevelPosUtil;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.objects.PosToGenerateContainer;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.common.WorldWorkerManager;
|
||||
@@ -40,7 +30,7 @@ import net.minecraftforge.common.WorldWorkerManager;
|
||||
*/
|
||||
public class LodWorldGenerator
|
||||
{
|
||||
public Minecraft mc = Minecraft.getInstance();
|
||||
public MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
|
||||
|
||||
/**
|
||||
* This holds the thread used to generate new LODs off the main thread.
|
||||
@@ -75,10 +65,6 @@ public class LodWorldGenerator
|
||||
*/
|
||||
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
|
||||
|
||||
public volatile ConcurrentMap<LevelPos, MutableBoolean> nodeToGenerate;
|
||||
|
||||
SortedSet<LevelPos> nodeToGenerateListNear;
|
||||
SortedSet<LevelPos> nodeToGenerateListFar;
|
||||
|
||||
private LodWorldGenerator()
|
||||
{
|
||||
@@ -108,14 +94,9 @@ public class LodWorldGenerator
|
||||
try
|
||||
{
|
||||
// round the player's block position down to the nearest chunk BlockPos
|
||||
ChunkPos playerChunkPos = new ChunkPos(mc.player.blockPosition());
|
||||
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
|
||||
int playerPosX = mc.getPlayer().blockPosition().getX();
|
||||
int playerPosZ = mc.getPlayer().blockPosition().getZ();
|
||||
|
||||
// used when determining which chunks are closer when queuing distance
|
||||
// generation
|
||||
int minChunkDist = Integer.MAX_VALUE;
|
||||
|
||||
ArrayList<GenerationRequest> chunksToGen = new ArrayList<>(maxChunkGenRequests);
|
||||
// if we don't have a full number of chunks to generate in chunksToGen
|
||||
// we can top it off from this reserve
|
||||
|
||||
@@ -123,126 +104,81 @@ public class LodWorldGenerator
|
||||
//=======================================//
|
||||
// create the generation Request objects //
|
||||
//=======================================//
|
||||
List<GenerationRequest> generationRequestList = new ArrayList<>(maxChunkGenRequests);
|
||||
|
||||
if (nodeToGenerate == null)
|
||||
nodeToGenerate = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
Comparator<LevelPos> posNearComparator = LevelPos.getPosComparator(
|
||||
playerBlockPosRounded.getX(),
|
||||
playerBlockPosRounded.getZ());
|
||||
Comparator<LevelPos> posFarComparator = LevelPos.getPosAndDetailComparator(
|
||||
playerBlockPosRounded.getX(),
|
||||
playerBlockPosRounded.getZ());
|
||||
nodeToGenerateListNear = new TreeSet(posNearComparator);
|
||||
nodeToGenerateListFar = new TreeSet(posFarComparator);
|
||||
|
||||
lodDim.getDataToGenerate(
|
||||
nodeToGenerate,
|
||||
playerBlockPosRounded.getX(),
|
||||
playerBlockPosRounded.getZ());
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
PosToGenerateContainer posToGenerate = lodDim.getDataToGenerate(
|
||||
maxChunkGenRequests,
|
||||
playerPosX,
|
||||
playerPosZ);
|
||||
//System.out.println(posToGenerate);
|
||||
//here we prepare two sorted set
|
||||
//the first contains the near pos to render
|
||||
//the second contain the far pos to render
|
||||
byte farDetail = (byte) 7;
|
||||
for (LevelPos pos : nodeToGenerate.keySet())
|
||||
{
|
||||
if (!nodeToGenerate.get(pos).booleanValue())
|
||||
{
|
||||
nodeToGenerate.remove(pos);
|
||||
} else
|
||||
{
|
||||
if (pos.detailLevel > farDetail){
|
||||
nodeToGenerateListFar.add(pos);
|
||||
}
|
||||
nodeToGenerateListNear.add(pos);
|
||||
nodeToGenerate.get(pos).setFalse();
|
||||
}
|
||||
}
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
boolean nearOrFar = true;
|
||||
boolean stopSwitch = false;
|
||||
int near = 0;
|
||||
int far = 0;
|
||||
|
||||
int maxDistance;
|
||||
byte circle;
|
||||
LevelPos levelPos;
|
||||
int requesting = maxChunkGenRequests;
|
||||
int requestingFar = maxChunkGenRequests / 4;
|
||||
while (requesting > 0 && !nodeToGenerateListNear.isEmpty())
|
||||
for (int index = 0; index < posToGenerate.getNumberOfPos(); index++)
|
||||
{
|
||||
levelPos = nodeToGenerateListNear.first();
|
||||
//.out.println(levelPos);
|
||||
nodeToGenerate.remove(levelPos);
|
||||
nodeToGenerateListNear.remove(levelPos);
|
||||
nodeToGenerateListFar.remove(levelPos);
|
||||
|
||||
//maxDistance = levelPos.maxDistance(
|
||||
// playerBlockPosRounded.getX(),
|
||||
// playerBlockPosRounded.getZ());
|
||||
//circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance);
|
||||
generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(levelPos.detailLevel)));
|
||||
requesting--;
|
||||
if (requestingFar > 0 && !nodeToGenerateListFar.isEmpty())
|
||||
if (posToGenerate.getNthDetail(near, true) != 0 && near < posToGenerate.getNumberOfNearPos())
|
||||
{
|
||||
levelPos = nodeToGenerateListFar.first();
|
||||
nodeToGenerate.remove(levelPos);
|
||||
nodeToGenerateListNear.remove(levelPos);
|
||||
nodeToGenerateListFar.remove(levelPos);
|
||||
if (levelPos.detailLevel >= farDetail)
|
||||
detailLevel = (byte) (posToGenerate.getNthDetail(near, true) - 1);
|
||||
posX = posToGenerate.getNthPosX(near, true);
|
||||
posZ = posToGenerate.getNthPosZ(near, true);
|
||||
near++;
|
||||
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
|
||||
{
|
||||
//maxDistance = levelPos.maxDistance( playerBlockPosRounded.getX(), playerBlockPosRounded.getZ());
|
||||
//circle = DetailDistanceUtil.getDistanceGenerationInverse(maxDistance);
|
||||
generationRequestList.add(new GenerationRequest(levelPos, DetailDistanceUtil.getDistanceGenerationMode(levelPos.detailLevel)));
|
||||
requestingFar--;
|
||||
requesting--;
|
||||
// prevent generating the same chunk multiple times
|
||||
if (positionWaitingToBeGenerated.contains(chunkPos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//====================================//
|
||||
// get the closet generation requests //
|
||||
//====================================//
|
||||
|
||||
// determine which points in the posListToGenerate
|
||||
// should actually be queued to generate
|
||||
for (GenerationRequest generationRequest : generationRequestList)
|
||||
{
|
||||
ChunkPos chunkPos = generationRequest.getChunkPos();
|
||||
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
|
||||
{
|
||||
// prevent generating the same chunk multiple times
|
||||
if (positionWaitingToBeGenerated.contains(chunkPos))
|
||||
{
|
||||
// don't add null chunkPos (which shouldn't happen anyway)
|
||||
// or add more to the generation queue
|
||||
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||
continue;
|
||||
|
||||
positionWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||
WorldWorkerManager.addWorker(genWorker);
|
||||
}
|
||||
|
||||
|
||||
if (posToGenerate.getNthDetail(far, false) != 0 && far < posToGenerate.getNumberOfFarPos())
|
||||
{
|
||||
detailLevel = (byte) (posToGenerate.getNthDetail(far, false) - 1);
|
||||
posX = posToGenerate.getNthPosX(far, false);
|
||||
posZ = posToGenerate.getNthPosZ(far, false);
|
||||
far++;
|
||||
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests)
|
||||
{
|
||||
// prevent generating the same chunk multiple times
|
||||
if (positionWaitingToBeGenerated.contains(chunkPos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
chunksToGen.add(generationRequest);
|
||||
|
||||
} // lod null and can generate more chunks
|
||||
} // positions to generate
|
||||
// don't add null chunkPos (which shouldn't happen anyway)
|
||||
// or add more to the generation queue
|
||||
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||
continue;
|
||||
|
||||
|
||||
//=============================//
|
||||
// start the LodNodeGenWorkers //
|
||||
//=============================//
|
||||
|
||||
// issue #19
|
||||
// TODO add a way for a server side mod to generate chunks requested here
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
// start chunk generation
|
||||
for (GenerationRequest generationRequest : generationRequestList)
|
||||
{
|
||||
// don't add null chunkPos (which shouldn't happen anyway)
|
||||
// or add more to the generation queue
|
||||
ChunkPos chunkPos = generationRequest.getChunkPos();
|
||||
if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||
continue;
|
||||
|
||||
positionWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, generationRequest.generationMode, renderer, lodBuilder, lodDim, serverWorld);
|
||||
WorldWorkerManager.addWorker(genWorker);
|
||||
positionWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||
WorldWorkerManager.addWorker(genWorker);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e)
|
||||
|
||||
@@ -27,13 +27,16 @@ import com.electronwill.nightconfig.core.file.CommentedFileConfig;
|
||||
import com.electronwill.nightconfig.core.io.WritingMode;
|
||||
import com.seibel.lod.ModInfo;
|
||||
import com.seibel.lod.enums.DebugMode;
|
||||
import com.seibel.lod.enums.DistanceCalculatorType;
|
||||
import com.seibel.lod.enums.DetailDropOff;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.DistanceQualityDropOff;
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.FogDrawOverride;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.GenerationPriority;
|
||||
import com.seibel.lod.enums.HorizontalQuality;
|
||||
import com.seibel.lod.enums.HorizontalResolution;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
import com.seibel.lod.enums.ShadingMode;
|
||||
import com.seibel.lod.enums.VerticalQuality;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
@@ -44,34 +47,32 @@ import net.minecraftforge.fml.config.ModConfig;
|
||||
* This handles any configuration the user has access to.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 9-1-2021
|
||||
* @version 9-24-2021
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class LodConfig
|
||||
{
|
||||
public static class Client
|
||||
{
|
||||
public final Graphics graphics;
|
||||
public final WorldGenerator worldGenerator;
|
||||
public final Threading threading;
|
||||
public final Debugging debugging;
|
||||
public final Buffers buffers;
|
||||
|
||||
public Client(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.push("client");
|
||||
{
|
||||
graphics = new Graphics(builder);
|
||||
worldGenerator = new WorldGenerator(builder);
|
||||
threading = new Threading(builder);
|
||||
debugging = new Debugging(builder);
|
||||
buffers = new Buffers(builder);
|
||||
}
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
public final Graphics graphics;
|
||||
public final WorldGenerator worldGenerator;
|
||||
public final Threading threading;
|
||||
public final Debugging debugging;
|
||||
public final Buffers buffers;
|
||||
|
||||
public Client(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.push("client");
|
||||
{
|
||||
graphics = new Graphics(builder);
|
||||
worldGenerator = new WorldGenerator(builder);
|
||||
threading = new Threading(builder);
|
||||
debugging = new Debugging(builder);
|
||||
buffers = new Buffers(builder);
|
||||
}
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
//================//
|
||||
// Client Configs //
|
||||
@@ -79,112 +80,122 @@ public class LodConfig
|
||||
|
||||
public static class Graphics
|
||||
{
|
||||
public ForgeConfigSpec.BooleanValue drawLODs;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<FogDistance> fogDistance;
|
||||
public ForgeConfigSpec.EnumValue<FogDrawOverride> fogDrawOverride;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodTemplate> lodTemplate;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> maxDrawDetail;
|
||||
public ForgeConfigSpec.EnumValue<HorizontalResolution> drawResolution;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
|
||||
// public ForgeConfigSpec.EnumValue<ShadingMode> shadingMode;
|
||||
|
||||
public ForgeConfigSpec.IntValue lodQuality;
|
||||
public ForgeConfigSpec.EnumValue<HorizontalQuality> horizontalQuality;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<DetailDropOff> detailDropOff;
|
||||
|
||||
public ForgeConfigSpec.IntValue lodChunkRenderDistance;
|
||||
|
||||
public ForgeConfigSpec.DoubleValue brightnessMultiplier;
|
||||
public ForgeConfigSpec.DoubleValue saturationMultiplier;
|
||||
public ForgeConfigSpec.BooleanValue disableDirectionalCulling;
|
||||
|
||||
public ForgeConfigSpec.BooleanValue alwaysDrawAtMaxQuality;
|
||||
|
||||
|
||||
Graphics(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment("These settings control how the LODs look.").push(this.getClass().getSimpleName());
|
||||
|
||||
drawLODs = builder
|
||||
.comment("\n\n"
|
||||
+ " If false LODs will not be drawn, \n"
|
||||
+ " however they will still be generated \n"
|
||||
+ " and saved to file for later use. \n")
|
||||
.define("drawLODs", true);
|
||||
|
||||
fogDistance = builder
|
||||
.comment("\n\n"
|
||||
+ " At what distance should Fog be drawn on the LODs? \n"
|
||||
+ " If the fog cuts off ubruptly or you are using Optifine's \"fast\" fog option \n"
|
||||
+ " set this to " + FogDistance.NEAR.toString() + " or " + FogDistance.FAR.toString() + ". \n")
|
||||
+ " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n")
|
||||
.defineEnum("fogDistance", FogDistance.NEAR_AND_FAR);
|
||||
|
||||
fogDrawOverride = builder
|
||||
.comment("\n\n"
|
||||
+ " When should fog be drawn? \n"
|
||||
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING.toString() + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ". \n"
|
||||
+ " " + FogDrawOverride.NEVER_DRAW_FOG.toString() + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST.toString() + ": Always draw fast fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY.toString() + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
|
||||
.defineEnum("fogDrawOverride", FogDrawOverride.USE_OPTIFINE_FOG_SETTING);
|
||||
+ " " + FogDrawOverride.USE_OPTIFINE_FOG_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY + ". \n"
|
||||
+ " " + FogDrawOverride.NEVER_DRAW_FOG + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FAST + ": Always draw fast fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.ALWAYS_DRAW_FOG_FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n")
|
||||
.defineEnum("fogDrawOverride", FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
|
||||
|
||||
lodTemplate = builder
|
||||
.comment("\n\n"
|
||||
+ " How should the LODs be drawn? \n"
|
||||
+ " NOTE: Currently only " + LodTemplate.CUBIC.toString() + " is implemented! \n"
|
||||
+ " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n"
|
||||
+ " \n"
|
||||
+ " " + LodTemplate.CUBIC.toString() + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
|
||||
+ " " + LodTemplate.TRIANGULAR.toString() + ": LOD Chunks smoothly transition between other. \n"
|
||||
+ " " + LodTemplate.DYNAMIC.toString() + ": LOD Chunks smoothly transition between other, \n"
|
||||
+ " " + LodTemplate.CUBIC + ": LOD Chunks are drawn as rectangular prisms (boxes). \n"
|
||||
+ " " + LodTemplate.TRIANGULAR + ": LOD Chunks smoothly transition between other. \n"
|
||||
+ " " + LodTemplate.DYNAMIC + ": LOD Chunks smoothly transition between other, \n"
|
||||
+ " " + " unless a neighboring chunk is at a significantly different height. \n")
|
||||
.defineEnum("lodTemplate", LodTemplate.CUBIC);
|
||||
|
||||
maxDrawDetail = builder
|
||||
detailDropOff = builder
|
||||
.comment("\n\n"
|
||||
+ " How smooth should the detail transition for LODs be? \n"
|
||||
+ DetailDropOff.BY_CHUNK + ": quality is determined per-chunk (best quality option, may cause stuttering when moving)\n"
|
||||
+ DetailDropOff.BY_REGION_FANCY + ": quality is determiend per-region (quality option)\n"
|
||||
+ DetailDropOff.BY_REGION_FAST + ": quality is determiend per-region (performance option)\n")
|
||||
.defineEnum("detailDropOff", DetailDropOff.BY_CHUNK);
|
||||
|
||||
drawResolution = builder
|
||||
.comment("\n\n"
|
||||
+ " What is the maximum detail level that LODs should be drawn at? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("lodDrawQuality", LodDetail.FULL);
|
||||
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("Draw resolution", HorizontalResolution.BLOCK);
|
||||
|
||||
lodQuality = builder
|
||||
horizontalQuality = builder
|
||||
.comment("\n\n"
|
||||
+ " this value is multiplied by 128 and determine \n"
|
||||
+ " how much the quality decrease over distance \n")
|
||||
.defineInRange("lodQuality", 1, 1, 4);
|
||||
+ " This indicates how quickly LODs drop off in quality. \n"
|
||||
+ " " + HorizontalQuality.LOW + ": quality drops every 4 chunks. \n"
|
||||
+ " " + HorizontalQuality.MEDIUM + ": quality drops every 8 chunks. \n"
|
||||
+ " " + HorizontalQuality.HIGH + ": quality drops every 16 chunks. \n")
|
||||
.defineEnum("lodDrawQuality", HorizontalQuality.MEDIUM);
|
||||
|
||||
lodChunkRenderDistance = builder
|
||||
.comment("\n\n"
|
||||
+ " This is the render distance of the mod \n")
|
||||
.defineInRange("lodChunkRenderDistane", 64, 32, 512);
|
||||
+ " The mod's render distance, measured in chunks. \n")
|
||||
.defineInRange("lodChunkRenderDistance", 64, 32, 1024);
|
||||
|
||||
shadingMode = builder
|
||||
disableDirectionalCulling = builder
|
||||
.comment("\n\n"
|
||||
+ " What kind of shading should the LODs have? \n"
|
||||
+ " \n"
|
||||
+ " " + ShadingMode.NONE.toString() + " \n"
|
||||
+ " " + "LODs will have the same lighting on every side. \n"
|
||||
+ " " + "Can make large similarly colored areas hard to differentiate. \n"
|
||||
+ "\n"
|
||||
+ " " + ShadingMode.DARKEN_SIDES.toString() + " \n"
|
||||
+ " " + "LODs will have darker sides and bottoms to simulate Minecraft's flat lighting.")
|
||||
.defineEnum("lightingMode", ShadingMode.DARKEN_SIDES);
|
||||
+ " If false LODs that are behind the player's camera \n"
|
||||
+ " aren't drawn, increasing performance. \n\n"
|
||||
+ ""
|
||||
+ " If true all LODs are drawn, even those behind \n"
|
||||
+ " the player's camera, decreasing performance. \n\n"
|
||||
+ ""
|
||||
+ " Disable this if you see LODs disapearing. \n"
|
||||
+ " (This may happen if you are using a camera mod) \n")
|
||||
.define("disableDirectionalCulling", false);
|
||||
|
||||
brightnessMultiplier = builder
|
||||
// shadingMode = builder
|
||||
// .comment("\n\n"
|
||||
// + " What kind of shading should the LODs have? \n"
|
||||
// + " \n"
|
||||
// + " " + ShadingMode.NONE + " \n"
|
||||
// + " " + "LODs will have the same lighting on every side. \n"
|
||||
// + " " + "Can make large similarly colored areas hard to differentiate. \n"
|
||||
// + "\n"
|
||||
// + " " + ShadingMode.GAME_SHADING + " \n"
|
||||
// + " " + "LODs will have darker sides and bottoms to simulate Minecraft's flat lighting.")
|
||||
// .defineEnum("lightingMode", ShadingMode.GAME_SHADING);
|
||||
|
||||
alwaysDrawAtMaxQuality = builder
|
||||
.comment("\n\n"
|
||||
+ " Change how bright LOD colors are. \n"
|
||||
+ " 0 = black \n"
|
||||
+ " 1 = normal color value \n"
|
||||
+ " 2 = washed out colors \n")
|
||||
.defineInRange("brightnessMultiplier", 1.0, 0, 2);
|
||||
|
||||
saturationMultiplier = builder
|
||||
.comment("\n\n"
|
||||
+ " Change how saturated LOD colors are. \n"
|
||||
+ " 0 = black and white \n"
|
||||
+ " 1 = normal saturation \n"
|
||||
+ " 2 = very saturated \n")
|
||||
.defineInRange("saturationMultiplier", 1.0, 0, 2);
|
||||
|
||||
+ " Disable LOD quality falloff, "
|
||||
+ " all LODs will be drawn at the highest "
|
||||
+ " available detail level. "
|
||||
+ " "
|
||||
+ " WARNING "
|
||||
+ " This could cause a Out Of Memory crash on render "
|
||||
+ " distances higher than 128 \n")
|
||||
.define("alwaysDrawAtMaxQuality", false);
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
@@ -192,63 +203,78 @@ public class LodConfig
|
||||
|
||||
public static class WorldGenerator
|
||||
{
|
||||
public ForgeConfigSpec.EnumValue<LodDetail> maxGenerationDetail;
|
||||
public ForgeConfigSpec.EnumValue<VerticalQuality> lodQualityMode;
|
||||
public ForgeConfigSpec.EnumValue<HorizontalResolution> generationResolution;
|
||||
public ForgeConfigSpec.EnumValue<DistanceGenerationMode> distanceGenerationMode;
|
||||
public ForgeConfigSpec.EnumValue<GenerationPriority> generationPriority;
|
||||
public ForgeConfigSpec.BooleanValue allowUnstableFeatureGeneration;
|
||||
public ForgeConfigSpec.EnumValue<DistanceCalculatorType> lodDistanceCalculatorType;
|
||||
|
||||
public ForgeConfigSpec.EnumValue<DistanceQualityDropOff> lodDistanceCalculatorType;
|
||||
|
||||
WorldGenerator(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment("These settings control how LODs outside your normal view range are generated.").push(this.getClass().getSimpleName());
|
||||
|
||||
maxGenerationDetail = builder
|
||||
lodQualityMode = builder
|
||||
.comment("\n\n"
|
||||
+ " Use 3d lods or 2d lods? \n"
|
||||
+ " " + VerticalQuality.HEIGHTMAP + ": LODs are solid from the lowest world point to the highest. Not good for floating islands or caves. Faster \n"
|
||||
+ " " + VerticalQuality.MULTI_LOD + ": LODs have gaps between vertical blocks. Good for floating islands and caves. Slower \n")
|
||||
.defineEnum("lodQualityMode", VerticalQuality.HEIGHTMAP);
|
||||
|
||||
generationResolution = builder
|
||||
.comment("\n\n"
|
||||
+ " What is the maximum detail level that LODs should be generated at? \n"
|
||||
+ " " + LodDetail.SINGLE.toString() + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + LodDetail.DOUBLE.toString() + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.QUAD.toString() + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.HALF.toString() + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + LodDetail.FULL.toString() + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("lodGenerationQuality", LodDetail.HALF);
|
||||
+ " " + HorizontalResolution.CHUNK + ": render 1 LOD for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.HALF_CHUNK + ": render 4 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.FOUR_BLOCKS + ": render 16 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.TWO_BLOCKS + ": render 64 LODs for each Chunk. \n"
|
||||
+ " " + HorizontalResolution.BLOCK + ": render 256 LODs for each Chunk. \n")
|
||||
.defineEnum("Generation Resolution", HorizontalResolution.BLOCK);
|
||||
|
||||
lodDistanceCalculatorType = builder
|
||||
.comment("\n\n"
|
||||
+ " " + DistanceCalculatorType.LINEAR + " \n"
|
||||
+ " " + DistanceQualityDropOff.LINEAR + " \n"
|
||||
+ " with LINEAR calculator the quality of block decrease \n"
|
||||
+ " linearly to the distance of the player \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceCalculatorType.QUADRATIC + " \n"
|
||||
+ " with LINEAR calculator the quality of block decrease \n"
|
||||
+ " quadratically to the distance of the player \n"
|
||||
+ "\n"
|
||||
+ " " + DistanceQualityDropOff.QUADRATIC + " \n"
|
||||
+ " with QUADRATIC calculator the quality of block decrease \n"
|
||||
+ " quadratically to the distance of the player \n")
|
||||
.defineEnum("lodDistanceComputation", DistanceQualityDropOff.LINEAR);
|
||||
|
||||
generationPriority = builder
|
||||
.comment("\n\n"
|
||||
+ " " + GenerationPriority.FAR_FIRST + " \n"
|
||||
+ " LODs are generated from low to high detail\n"
|
||||
+ " with a small priority for far regions. \n"
|
||||
+ " This fills in the world fastest. \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceCalculatorType.RENDER_DEPENDANT + " \n"
|
||||
+ " with LINEAR calculator the quality of block decrease \n"
|
||||
+ " quadratically to the distance of the player \n")
|
||||
.defineEnum("lodDistanceComputation", DistanceCalculatorType.LINEAR);
|
||||
+ "\n"
|
||||
+ " " + GenerationPriority.NEAR_FIRST + " \n"
|
||||
+ " LODs are generated around the player \n"
|
||||
+ " in a spiral, similar to vanilla minecraft. \n")
|
||||
.defineEnum("Generation priority", GenerationPriority.NEAR_FIRST);
|
||||
|
||||
distanceGenerationMode = builder
|
||||
.comment("\n\n"
|
||||
+ " Note: The times listed here are the amount of time it took \n"
|
||||
+ " the developer's PC to generate 1 chunk, \n"
|
||||
+ " one of the developer's PC to generate 1 chunk, \n"
|
||||
+ " and are included so you can compare the \n"
|
||||
+ " different generation options. Your mileage may vary. \n"
|
||||
+ "\n"
|
||||
|
||||
+ " " + DistanceGenerationMode.NONE.toString() + " \n"
|
||||
|
||||
+ " " + DistanceGenerationMode.NONE + " \n"
|
||||
+ " Don't run the distance generator. \n"
|
||||
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY.toString() + " \n"
|
||||
+ " Only generate the biomes and use biome \n"
|
||||
+ " grass/foliage color, water color, or snow color \n"
|
||||
+ " to generate the color. \n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY + " \n"
|
||||
+ " Only generate the biomes and use the biome's \n"
|
||||
+ " grass color, water color, or snow color. \n"
|
||||
+ " Doesn't generate height, everything is shown at sea level. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT.toString() + " \n"
|
||||
+ " " + DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT + " \n"
|
||||
+ " Same as BIOME_ONLY, except instead \n"
|
||||
+ " of always using sea level as the LOD height \n"
|
||||
+ " different biome types (mountain, ocean, forest, etc.) \n"
|
||||
@@ -256,30 +282,30 @@ public class LodConfig
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SURFACE.toString() + " \n"
|
||||
+ " " + DistanceGenerationMode.SURFACE + " \n"
|
||||
+ " Generate the world surface, \n"
|
||||
+ " this does NOT include caves, trees, \n"
|
||||
+ " or structures. \n"
|
||||
+ " Multithreaded - Faster (10-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.FEATURES.toString() + " \n"
|
||||
+ " " + DistanceGenerationMode.FEATURES + " \n"
|
||||
+ " Generate everything except structures. \n"
|
||||
+ " WARNING: This may cause world generation bugs or instability! \n"
|
||||
+ " Multithreaded - Fast (15-20 ms) \n"
|
||||
|
||||
+ "\n"
|
||||
+ " " + DistanceGenerationMode.SERVER.toString() + " \n"
|
||||
+ " " + DistanceGenerationMode.SERVER + " \n"
|
||||
+ " Ask the server to generate/load each chunk. \n"
|
||||
+ " This is the most compatible, but causes server/simulation lag. \n"
|
||||
+ " This will also show player made structures if you \n"
|
||||
+ " are adding the mod to a pre-existing world. \n"
|
||||
+ " This will show player made structures, which can \n"
|
||||
+ " be useful if you are adding the mod to a pre-existing world. \n"
|
||||
+ " Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms) \n")
|
||||
.defineEnum("distanceGenerationMode", DistanceGenerationMode.SURFACE);
|
||||
|
||||
allowUnstableFeatureGeneration = builder
|
||||
.comment("\n\n"
|
||||
+ " When using the " + DistanceGenerationMode.FEATURES.toString() + " generation mode \n"
|
||||
+ " When using the " + DistanceGenerationMode.FEATURES + " generation mode \n"
|
||||
+ " some features may not be thread safe, which could \n"
|
||||
+ " cause instability and crashes. \n"
|
||||
+ " By default (false) those features are skipped, \n"
|
||||
@@ -296,7 +322,6 @@ public class LodConfig
|
||||
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n")
|
||||
.define("allowUnstableFeatureGeneration", false);
|
||||
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
@@ -318,7 +343,7 @@ public class LodConfig
|
||||
+ " this number. If you want to increase LOD generation speed, \n"
|
||||
+ " increase this number. \n"
|
||||
+ " \n"
|
||||
+ " The maximum value is the number of processors on your CPU. \n"
|
||||
+ " The maximum value is the number of logical processors on your CPU. \n"
|
||||
+ " Requires a restart to take effect. \n")
|
||||
.defineInRange("numberOfWorldGenerationThreads", Runtime.getRuntime().availableProcessors() / 2, 1, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
@@ -329,7 +354,7 @@ public class LodConfig
|
||||
+ " If you experience high CPU useage when NOT generating distant \n"
|
||||
+ " LODs, lower this number. \n"
|
||||
+ " \n"
|
||||
+ " The maximum value is the number of processors on your CPU. \n"
|
||||
+ " The maximum value is the number of logical processors on your CPU. \n"
|
||||
+ " Requires a restart to take effect. \n")
|
||||
.defineInRange("numberOfBufferBuilderThreads", Runtime.getRuntime().availableProcessors(), 1, Runtime.getRuntime().availableProcessors());
|
||||
|
||||
@@ -340,22 +365,23 @@ public class LodConfig
|
||||
public static class Debugging
|
||||
{
|
||||
public ForgeConfigSpec.EnumValue<DebugMode> debugMode;
|
||||
public ForgeConfigSpec.BooleanValue enableDebugKeybinding;
|
||||
public ForgeConfigSpec.BooleanValue enableDebugKeybindings;
|
||||
|
||||
Debugging(ForgeConfigSpec.Builder builder)
|
||||
{
|
||||
builder.comment("These settings can be used by to look for bugs, or see how certain parts of the mod are working.").push(this.getClass().getSimpleName());
|
||||
builder.comment("These settings can be used to look for bugs, or see how certain aspects of the mod work.").push(this.getClass().getSimpleName());
|
||||
|
||||
debugMode = builder
|
||||
.comment("\n\n"
|
||||
+ " " + DebugMode.OFF.toString() + ": LODs will draw with their normal colors. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL.toString() + ": LOD colors will be based on their detail. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME.toString() + ": LOD colors will be based on their detail, drawn with wireframe. \n")
|
||||
+ " " + DebugMode.OFF + ": LODs will draw with their normal colors. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL + ": LOD colors will be based on their detail level. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME + ": LOD colors will be based on their detail level, drawn as a wireframe. \n")
|
||||
.defineEnum("debugMode", DebugMode.OFF);
|
||||
|
||||
enableDebugKeybinding = builder
|
||||
enableDebugKeybindings = builder
|
||||
.comment("\n\n"
|
||||
+ " If true the F4 key can be used to cycle through the different debug modes. \n")
|
||||
+ " If true the F4 key can be used to cycle through the different debug modes. \n"
|
||||
+ " and the F6 key can be used to enable and disable LOD rendering.")
|
||||
.define("enableDebugKeybinding", false);
|
||||
|
||||
builder.pop();
|
||||
@@ -392,18 +418,10 @@ public class LodConfig
|
||||
+ " rebuild the vertex buffers when the LOD regions change? \n")
|
||||
.defineInRange("bufferRebuildLodChangeTimeout", 5000, 1, 60000);
|
||||
|
||||
|
||||
builder.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* {@link Path} to the configuration file of this mod
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public enum DetailDropOff
|
||||
{
|
||||
BY_REGION_FAST,
|
||||
BY_REGION_FANCY,
|
||||
BY_CHUNK,
|
||||
}
|
||||
+1
-6
@@ -4,7 +4,7 @@ package com.seibel.lod.enums;
|
||||
* @author Leonardo Amato
|
||||
* @version 22-08-2021
|
||||
*/
|
||||
public enum DistanceCalculatorType
|
||||
public enum DistanceQualityDropOff
|
||||
{
|
||||
/**
|
||||
* different Lod detail render and generate linearly to the distance
|
||||
@@ -15,9 +15,4 @@ public enum DistanceCalculatorType
|
||||
* different Lod detail render and generate quadratically to the distance
|
||||
*/
|
||||
QUADRATIC,
|
||||
|
||||
/**
|
||||
* we calculate the distance based on game render distance and mod render distance
|
||||
*/
|
||||
RENDER_DEPENDANT;
|
||||
}
|
||||
+5
-19
@@ -18,26 +18,12 @@
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
/**
|
||||
* NE, SE, SW, NW
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 1-20-2020
|
||||
* @author Leonardo Amato
|
||||
*/
|
||||
public enum LodCorner
|
||||
public enum GenerationPriority
|
||||
{
|
||||
/** -Z, +X */
|
||||
NE(0),
|
||||
/** +Z, +X */
|
||||
SE(1),
|
||||
/** +Z, -X */
|
||||
SW(2),
|
||||
/** -Z, -X */
|
||||
NW(3);
|
||||
|
||||
public final int value;
|
||||
|
||||
private LodCorner(int newValue)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
NEAR_FIRST,
|
||||
|
||||
FAR_FIRST;
|
||||
}
|
||||
+17
-21
@@ -18,33 +18,29 @@
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
/**
|
||||
* NONE <br>
|
||||
* BIOME_ONLY <br>
|
||||
* BIOME_ONLY_SIMULATE_HEIGHT <br>
|
||||
* SURFACE <br>
|
||||
* FEATURES <br>
|
||||
* SERVER <br><br>
|
||||
*
|
||||
* In order of fastest to slowest.
|
||||
*
|
||||
* USE_OPTIFINE_FOG_SETTING, <br>
|
||||
* NEVER_DRAW_FOG, <br>
|
||||
* ALWAYS_DRAW_FOG_FAST, <br>
|
||||
* ALWAYS_DRAW_FOG_FANCY <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leonardo Amato
|
||||
* @version 8-7-2021
|
||||
* @version 7-03-2021
|
||||
*/
|
||||
public enum LodRenderDistance
|
||||
public enum HorizontalQuality
|
||||
{
|
||||
SHORT(32),
|
||||
/** Lods are 2D with heightMap */
|
||||
LOW(64),
|
||||
|
||||
MEDIUM(64),
|
||||
/** Lods expand in three dimension */
|
||||
MEDIUM(128),
|
||||
|
||||
FAR(128);
|
||||
/** Lods expand in three dimension */
|
||||
HIGH(256);
|
||||
|
||||
public int distanceUnit;
|
||||
|
||||
/** The higher the number the more complete the generation is. */
|
||||
public final int renderDistance;
|
||||
|
||||
LodRenderDistance(int complexity)
|
||||
HorizontalQuality(int distanceUnit)
|
||||
{
|
||||
this.renderDistance = complexity;
|
||||
this.distanceUnit = distanceUnit;
|
||||
}
|
||||
}
|
||||
}
|
||||
+17
-18
@@ -28,24 +28,23 @@ import com.seibel.lod.util.LodUtil;
|
||||
* @author James Seibel
|
||||
* @version 8-11-2021
|
||||
*/
|
||||
public enum LodDetail
|
||||
public enum HorizontalResolution
|
||||
{
|
||||
/** render 1 LOD for each chunk */
|
||||
SINGLE(1, 4),
|
||||
CHUNK(1, 4),
|
||||
|
||||
/** render 4 LODs for each chunk */
|
||||
DOUBLE(2, 3),
|
||||
HALF_CHUNK(2, 3),
|
||||
|
||||
/** render 16 LODs for each chunk */
|
||||
QUAD(4, 2),
|
||||
FOUR_BLOCKS(4, 2),
|
||||
|
||||
/** render 64 LODs for each chunk */
|
||||
HALF(8, 1),
|
||||
TWO_BLOCKS(8, 1),
|
||||
|
||||
/** render 256 LODs for each chunk */
|
||||
FULL(16, 0);
|
||||
|
||||
|
||||
BLOCK(16, 0);
|
||||
|
||||
/** How many DataPoints should
|
||||
* be drawn per side per LodChunk */
|
||||
public final int dataPointLengthCount;
|
||||
@@ -69,12 +68,12 @@ public enum LodDetail
|
||||
* 2nd dimension: An array of all LodDetails that are less than or <br>
|
||||
* equal to that detailLevel
|
||||
*/
|
||||
private static LodDetail[][] lowerDetailArrays;
|
||||
private static HorizontalResolution[][] lowerDetailArrays;
|
||||
|
||||
|
||||
|
||||
|
||||
private LodDetail(int newLengthCount, int newDetailLevel)
|
||||
private HorizontalResolution(int newLengthCount, int newDetailLevel)
|
||||
{
|
||||
detailLevel = (byte) newDetailLevel;
|
||||
dataPointLengthCount = newLengthCount;
|
||||
@@ -113,20 +112,20 @@ public enum LodDetail
|
||||
* Returns an array of all LodDetails that have a detail level
|
||||
* that is less than or equal to the given LodDetail
|
||||
*/
|
||||
public static LodDetail[] getSelfAndLowerDetails(LodDetail detail)
|
||||
public static HorizontalResolution[] getSelfAndLowerDetails(HorizontalResolution detail)
|
||||
{
|
||||
if (lowerDetailArrays == null)
|
||||
{
|
||||
// run first time setup
|
||||
lowerDetailArrays = new LodDetail[LodDetail.values().length][];
|
||||
lowerDetailArrays = new HorizontalResolution[HorizontalResolution.values().length][];
|
||||
|
||||
// go through each LodDetail
|
||||
for(LodDetail currentDetail : LodDetail.values())
|
||||
for(HorizontalResolution currentDetail : HorizontalResolution.values())
|
||||
{
|
||||
ArrayList<LodDetail> lowerDetails = new ArrayList<>();
|
||||
ArrayList<HorizontalResolution> lowerDetails = new ArrayList<>();
|
||||
|
||||
// find the details lower than currentDetail
|
||||
for(LodDetail compareDetail : LodDetail.values())
|
||||
for(HorizontalResolution compareDetail : HorizontalResolution.values())
|
||||
{
|
||||
if (currentDetail.detailLevel <= compareDetail.detailLevel)
|
||||
{
|
||||
@@ -138,7 +137,7 @@ public enum LodDetail
|
||||
Collections.sort(lowerDetails);
|
||||
Collections.reverse(lowerDetails);
|
||||
|
||||
lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new LodDetail[lowerDetails.size()]);
|
||||
lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new HorizontalResolution[lowerDetails.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +145,9 @@ public enum LodDetail
|
||||
}
|
||||
|
||||
/** Returns what detail level should be used at a given distance and maxDistance. */
|
||||
public static LodDetail getDetailForDistance(LodDetail maxDetailLevel, int distance, int maxDistance)
|
||||
public static HorizontalResolution getDetailForDistance(HorizontalResolution maxDetailLevel, int distance, int maxDistance)
|
||||
{
|
||||
LodDetail[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
|
||||
HorizontalResolution[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
|
||||
int distaneBetweenDetails = maxDistance / lowerDetails.length;
|
||||
int index = LodUtil.clamp(0, distance / distaneBetweenDetails, lowerDetails.length - 1);
|
||||
|
||||
@@ -54,8 +54,8 @@ public enum LodTemplate
|
||||
}
|
||||
|
||||
|
||||
public int getBufferMemoryForSingleLod()
|
||||
public int getBufferMemoryForSingleLod(int maxVerticalData)
|
||||
{
|
||||
return template.getBufferMemoryForSingleNode();
|
||||
return template.getBufferMemoryForSingleNode(maxVerticalData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* This file is part of the LOD Mod, licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* NE, SE, SW, NW <br>
|
||||
* NORTH, SOUTH, EAST, WEST, <br>
|
||||
* CENTER
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-30-2021
|
||||
*/
|
||||
public enum RelativeChunkPos
|
||||
{
|
||||
/** +X, -Z */
|
||||
NE(0, 1,-1),
|
||||
/** +X, +Z */
|
||||
SE(1, 1,1),
|
||||
/** -X, +Z */
|
||||
SW(2, -1,1),
|
||||
/** -X, -Z */
|
||||
NW(3, -1,-1),
|
||||
|
||||
/** -Z */
|
||||
NORTH(4, 0,-1),
|
||||
/** +Z */
|
||||
SOUTH(5, 0,1),
|
||||
|
||||
/** +X */
|
||||
EAST(6, 1,0),
|
||||
/** -X */
|
||||
WEST(7, -1,0),
|
||||
|
||||
CENTER(8, 0,0);
|
||||
|
||||
/** index used when referencing objects in an array */
|
||||
public final int index;
|
||||
|
||||
/** position relative to X CENTER */
|
||||
public final int x;
|
||||
/** position relative to Z CENTER */
|
||||
public final int z;
|
||||
|
||||
/** NORTH, SOUTH, EAST, WEST */
|
||||
public static EnumSet<RelativeChunkPos> ADJACENT = EnumSet.of(NORTH, SOUTH, EAST, WEST);
|
||||
/** NE, NW, SE, SW */
|
||||
public static EnumSet<RelativeChunkPos> DIAGONAL = EnumSet.of(NE, NW, SE, SW);
|
||||
|
||||
|
||||
public static EnumSet<RelativeChunkPos> NW_CORNER = EnumSet.of(WEST, NW, NORTH);
|
||||
public static EnumSet<RelativeChunkPos> NE_CORNER = EnumSet.of(EAST, NE, NORTH);
|
||||
public static EnumSet<RelativeChunkPos> SW_CORNER = EnumSet.of(WEST, SW, SOUTH);
|
||||
public static EnumSet<RelativeChunkPos> SE_CORNER = EnumSet.of(EAST, SE, SOUTH);
|
||||
|
||||
/** NW_CORNER, NE_CORNER, SW_CORNER, SE_CORNER <br>
|
||||
* Contains the 3 points surrounding a corner. */
|
||||
public static ArrayList<EnumSet<RelativeChunkPos>> CORNERS = new ArrayList<EnumSet<RelativeChunkPos>>( Arrays.asList(NW_CORNER, NE_CORNER, SW_CORNER, SE_CORNER) );
|
||||
|
||||
private RelativeChunkPos(int newIndex, int newX, int newZ)
|
||||
{
|
||||
index = newIndex;
|
||||
x = newX;
|
||||
z = newZ;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,5 +14,5 @@ public enum ShadingMode
|
||||
|
||||
/** LODs will have darker sides and bottoms to simulate top down lighting.
|
||||
Fastest */
|
||||
DARKEN_SIDES;
|
||||
GAME_SHADING;
|
||||
}
|
||||
+13
-28
@@ -18,34 +18,19 @@
|
||||
package com.seibel.lod.enums;
|
||||
|
||||
/**
|
||||
* TOP, NORTH, SOUTH, EAST, WEST, BOTTOM
|
||||
*
|
||||
* USE_OPTIFINE_FOG_SETTING, <br>
|
||||
* NEVER_DRAW_FOG, <br>
|
||||
* ALWAYS_DRAW_FOG_FAST, <br>
|
||||
* ALWAYS_DRAW_FOG_FANCY <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-17-2020
|
||||
* @version 7-03-2021
|
||||
*/
|
||||
public enum ColorDirection
|
||||
public enum VerticalQuality
|
||||
{
|
||||
// used for colors
|
||||
/** +Y */
|
||||
TOP(0),
|
||||
|
||||
/** -Z */
|
||||
NORTH(1),
|
||||
/** +Z */
|
||||
SOUTH(2),
|
||||
|
||||
/** +X */
|
||||
EAST(3),
|
||||
/** -X */
|
||||
WEST(4),
|
||||
|
||||
/** -Y */
|
||||
BOTTOM(5);
|
||||
|
||||
public final int value;
|
||||
|
||||
private ColorDirection(int newValue)
|
||||
{
|
||||
value = newValue;
|
||||
}
|
||||
}
|
||||
/** Lods are 2D with heightMap */
|
||||
HEIGHTMAP,
|
||||
|
||||
/** Lods expand in three dimension */
|
||||
MULTI_LOD;
|
||||
}
|
||||
@@ -17,21 +17,22 @@
|
||||
*/
|
||||
package com.seibel.lod.handlers;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.objects.LevelContainer;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.enums.VerticalQuality;
|
||||
import com.seibel.lod.objects.*;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
@@ -42,15 +43,10 @@ import com.seibel.lod.util.LodUtil;
|
||||
* to file.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-24-2021
|
||||
* @version 9-7-2021
|
||||
*/
|
||||
public class LodDimensionFileHandler
|
||||
{
|
||||
/**
|
||||
* This is what separates each piece of data
|
||||
*/
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
|
||||
|
||||
private LodDimension loadedDimension = null;
|
||||
public long regionLastWriteTime[][];
|
||||
@@ -64,11 +60,7 @@ public class LodDimensionFileHandler
|
||||
/**
|
||||
* .txt
|
||||
*/
|
||||
private static final String FILE_EXTENSION = ".txt";
|
||||
/**
|
||||
* lod/
|
||||
*/
|
||||
private static final String LOD_FOLDER_NAME = "lod";
|
||||
private static final String FILE_EXTENSION = ".dat";
|
||||
/**
|
||||
* detail-#
|
||||
*/
|
||||
@@ -88,12 +80,7 @@ public class LodDimensionFileHandler
|
||||
* file handler, older versions (smaller numbers) will be deleted and overwritten,
|
||||
* newer versions (larger numbers) will be ignored and won't be read.
|
||||
*/
|
||||
public static final int LOD_SAVE_FILE_VERSION = 4;
|
||||
|
||||
/**
|
||||
* This is the string written before the file version
|
||||
*/
|
||||
private static final String LOD_FILE_VERSION_PREFIX = "lod_save_file_version";
|
||||
public static final int LOD_SAVE_FILE_VERSION = 6;
|
||||
|
||||
/**
|
||||
* Allow saving asynchronously, but never try to save multiple regions
|
||||
@@ -126,21 +113,21 @@ public class LodDimensionFileHandler
|
||||
* Return the LodRegion region at the given coordinates.
|
||||
* (null if the file doesn't exist)
|
||||
*/
|
||||
public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode)
|
||||
public LodRegion loadRegionFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||
{
|
||||
int regionX = regionPos.x;
|
||||
int regionZ = regionPos.z;
|
||||
LodRegion region = new LodRegion(LodUtil.REGION_DETAIL_LEVEL,regionPos, generationMode);
|
||||
LodRegion region = new LodRegion(LodUtil.REGION_DETAIL_LEVEL,regionPos, generationMode, verticalQuality);
|
||||
for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel, verticalQuality);
|
||||
|
||||
try
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel);
|
||||
|
||||
// if the fileName was null that means the folder is inaccessible
|
||||
// for some reason
|
||||
if (fileName == null)
|
||||
throw new IllegalArgumentException("Game folder is not accessible");
|
||||
throw new IllegalArgumentException("Unable to read region [" + regionX + ", " + regionZ + "] file, no fileName.");
|
||||
|
||||
|
||||
File f = new File(fileName);
|
||||
@@ -151,65 +138,58 @@ public class LodDimensionFileHandler
|
||||
// return anything
|
||||
continue;
|
||||
}
|
||||
String data = "";
|
||||
BufferedReader bufferedReader = new BufferedReader(new FileReader(f));
|
||||
data = bufferedReader.readLine();
|
||||
int fileVersion = -1;
|
||||
byte[] data = {0};
|
||||
long dataSize = f.length();
|
||||
dataSize -= 1;
|
||||
if (dataSize > 0) {
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(f))) {
|
||||
int fileVersion = -1;
|
||||
fileVersion = inputStream.read();
|
||||
// check if this file can be read by this file handler
|
||||
if (fileVersion < LOD_SAVE_FILE_VERSION) {
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
inputStream.close();
|
||||
f.delete();
|
||||
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version found: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" File was been deleted.");
|
||||
|
||||
if (data != null && !data.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(data.substring(data.indexOf(' ')).trim());
|
||||
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a version
|
||||
// keep the version as -1
|
||||
fileVersion = -1;
|
||||
break;
|
||||
} else if (fileVersion > LOD_SAVE_FILE_VERSION) {
|
||||
// the file we are reading is a newer version,
|
||||
// close the reader and ignore the file, we don't
|
||||
// want to accidently delete anything the user may want.
|
||||
inputStream.close();
|
||||
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version found: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" this region will not be written to in order to protect the newer file.");
|
||||
|
||||
break;
|
||||
}
|
||||
// this file is a readable version, begin reading the file
|
||||
data = new byte[(int) dataSize];
|
||||
inputStream.read(data);
|
||||
inputStream.close();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
// check if this file can be read by this file handler
|
||||
if (fileVersion < LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is an older version,
|
||||
// close the reader and delete the file.
|
||||
bufferedReader.close();
|
||||
f.delete();
|
||||
ClientProxy.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" File was been deleted.");
|
||||
|
||||
break;
|
||||
} else if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// close the reader and ignore the file, we don't
|
||||
// want to accidently delete anything the user may want.
|
||||
bufferedReader.close();
|
||||
ClientProxy.LOGGER.info("Newer LOD region file for region: (" + regionX + "," + regionZ + ") version: " + fileVersion +
|
||||
", version requested: " + LOD_SAVE_FILE_VERSION +
|
||||
" this region will not be written to in order to protect the newer file.");
|
||||
|
||||
break;
|
||||
}
|
||||
} else
|
||||
{
|
||||
// there is no data in this file
|
||||
bufferedReader.close();
|
||||
break;
|
||||
}
|
||||
|
||||
// this file is a readable version, begin reading the file
|
||||
data = bufferedReader.readLine();
|
||||
|
||||
bufferedReader.close();
|
||||
region.addLevel(new LevelContainer(data));
|
||||
switch (region.getLodQualityMode()){
|
||||
default:
|
||||
case HEIGHTMAP:
|
||||
region.addLevel(new SingleLevelContainer(data));
|
||||
break;
|
||||
case MULTI_LOD:
|
||||
region.addLevel(new VerticalLevelContainer(data));
|
||||
break;
|
||||
}
|
||||
//region.addLevel(new SingleLevelContainer(data));
|
||||
} catch (Exception e)
|
||||
{
|
||||
// the buffered reader encountered a
|
||||
// problem reading the file
|
||||
|
||||
ClientProxy.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -239,10 +219,10 @@ public class LodDimensionFileHandler
|
||||
{
|
||||
for (int j = 0; j < loadedDimension.getWidth(); j++)
|
||||
{
|
||||
if (loadedDimension.isRegionDirty[i][j] && loadedDimension.regions[i][j] != null)
|
||||
if (loadedDimension.isRegionToRegen(i,j) && loadedDimension.getRegionByArrayIndex(i,j) != null)
|
||||
{
|
||||
saveRegionToFile(loadedDimension.regions[i][j]);
|
||||
loadedDimension.isRegionDirty[i][j] = false;
|
||||
saveRegionToFile(loadedDimension.getRegionByArrayIndex(i,j));
|
||||
loadedDimension.setRegenByArrayIndex(i, j,false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,14 +247,14 @@ public class LodDimensionFileHandler
|
||||
int z = region.regionPosZ;
|
||||
for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(x, z, region.getGenerationMode(), detailLevel);
|
||||
String fileName = getFileNameAndPathForRegion(x, z, region.getGenerationMode(), detailLevel, region.getLodQualityMode());
|
||||
File oldFile = new File(fileName);
|
||||
|
||||
// if the fileName was null that means the folder is inaccessible
|
||||
// for some reason
|
||||
if (fileName == null)
|
||||
{
|
||||
ClientProxy.LOGGER.warn("Unable to save region [" + x + ", " + z + "] to file.");
|
||||
ClientProxy.LOGGER.warn("Unable to save region [" + x + ", " + z + "] to file, no fileName.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -295,54 +275,45 @@ public class LodDimensionFileHandler
|
||||
// is the correct version.
|
||||
// (to make sure we don't overwrite a newer
|
||||
// version file if it exists)
|
||||
|
||||
BufferedReader br = new BufferedReader(new FileReader(oldFile));
|
||||
String s = br.readLine();
|
||||
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||
|
||||
if (s != null && !s.isEmpty())
|
||||
{
|
||||
// try to get the file version
|
||||
try
|
||||
{
|
||||
fileVersion = Integer.parseInt(s.substring(s.indexOf(' ')).trim());
|
||||
} catch (NumberFormatException | StringIndexOutOfBoundsException e)
|
||||
{
|
||||
// this file doesn't have a correctly formated version
|
||||
// just overwrite the file
|
||||
}
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(oldFile))) {
|
||||
fileVersion = inputStream.read();
|
||||
inputStream.close();
|
||||
} catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
br.close();
|
||||
|
||||
// check if this file can be written to by the file handler
|
||||
if (fileVersion <= LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// we are good to continue and overwrite the old file
|
||||
} else // if(fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
if (fileVersion > LOD_SAVE_FILE_VERSION) {
|
||||
// the file we are reading is a newer version,
|
||||
// don't write anything, we don't want to accidently
|
||||
// delete anything the user may want.
|
||||
return;
|
||||
}
|
||||
} // if(fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
//else {
|
||||
// we are good to continue and overwrite the old file
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
// the old file is good, now create a new save file
|
||||
File newFile = new File(fileName + TMP_FILE_EXTENSION);
|
||||
FileWriter fw = new FileWriter(newFile);
|
||||
try (OutputStream outputStream = new BufferedOutputStream(new FileOutputStream(newFile))) {
|
||||
|
||||
// add the version of this file
|
||||
fw.write(LOD_FILE_VERSION_PREFIX + " " + LOD_SAVE_FILE_VERSION + "\n");
|
||||
// add the version of this file
|
||||
outputStream.write(LOD_SAVE_FILE_VERSION);
|
||||
|
||||
// add each LodChunk to the file
|
||||
fw.write(region.getLevel(detailLevel).toString());
|
||||
fw.close();
|
||||
// add each LodChunk to the file
|
||||
outputStream.write(region.getLevel(detailLevel).toDataString());
|
||||
outputStream.close();
|
||||
|
||||
// overwrite the old file with the new one
|
||||
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
// overwrite the old file with the new one
|
||||
Files.move(newFile.toPath(), oldFile.toPath(), StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
}catch (IOException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
ClientProxy.LOGGER.error("LOD file write error: ");
|
||||
ClientProxy.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@@ -363,7 +334,7 @@ public class LodDimensionFileHandler
|
||||
* <p>
|
||||
* Returns null if there is an IO Exception.
|
||||
*/
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel)
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -372,9 +343,10 @@ public class LodDimensionFileHandler
|
||||
// or
|
||||
// ".\Super Flat\data"
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
|
||||
generationMode.toString() + File.separatorChar +
|
||||
DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
verticalQuality + File.separatorChar +
|
||||
generationMode.toString() + File.separatorChar +
|
||||
DETAIL_FOLDER_NAME_PREFIX + detailLevel + File.separatorChar +
|
||||
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
|
||||
} catch (IOException | SecurityException e)
|
||||
{
|
||||
ClientProxy.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
package com.seibel.lod.handlers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.seibel.lod.enums.FogQuality;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
/**
|
||||
* This object is used to get variables from methods
|
||||
@@ -29,27 +29,32 @@ import net.minecraft.client.Minecraft;
|
||||
* in Optifine.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-03-2021
|
||||
* @version 9-7-2021
|
||||
*/
|
||||
public class ReflectionHandler
|
||||
{
|
||||
private Minecraft mc = Minecraft.getInstance();
|
||||
public static final ReflectionHandler INSTANCE = new ReflectionHandler();
|
||||
private MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
|
||||
|
||||
public Field ofFogField = null;
|
||||
|
||||
public ReflectionHandler()
|
||||
public Method vertexBufferUploadMethod = null;
|
||||
|
||||
|
||||
|
||||
private ReflectionHandler()
|
||||
{
|
||||
setupFogField();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Similar to setupFovMethod.
|
||||
* finds the Optifine fog type field
|
||||
*/
|
||||
private void setupFogField()
|
||||
{
|
||||
// get every variable from the entity renderer
|
||||
Field[] optionFields = mc.options.getClass().getDeclaredFields();
|
||||
|
||||
Field[] optionFields = mc.getOptions().getClass().getDeclaredFields();
|
||||
|
||||
// try and find the ofFogType variable in gameSettings
|
||||
for (Field field : optionFields)
|
||||
{
|
||||
@@ -59,14 +64,15 @@ public class ReflectionHandler
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we didn't find the field,
|
||||
// either optifine isn't installed, or
|
||||
// optifine changed the name of the variable
|
||||
ofFogField = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get what type of fog optifine is currently set to render.
|
||||
*/
|
||||
@@ -79,35 +85,34 @@ public class ReflectionHandler
|
||||
// the setup method wasn't called yet.
|
||||
return FogQuality.FANCY;
|
||||
}
|
||||
|
||||
|
||||
int returnNum = 0;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
returnNum = (int) ofFogField.get(mc.options);
|
||||
returnNum = (int) ofFogField.get(mc.getOptions());
|
||||
} catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
switch (returnNum)
|
||||
{
|
||||
// optifine's "default" option,
|
||||
// it should never be called in this case
|
||||
case 0:
|
||||
return FogQuality.FAST;
|
||||
|
||||
// optifine's "default" option,
|
||||
// it should never be called in this case
|
||||
case 0:
|
||||
return FogQuality.FAST;
|
||||
|
||||
// normal options
|
||||
case 1:
|
||||
return FogQuality.FAST;
|
||||
case 2:
|
||||
return FogQuality.FANCY;
|
||||
case 3:
|
||||
return FogQuality.OFF;
|
||||
|
||||
default:
|
||||
return FogQuality.FAST;
|
||||
case 1:
|
||||
return FogQuality.FAST;
|
||||
case 2:
|
||||
return FogQuality.FANCY;
|
||||
case 3:
|
||||
return FogQuality.OFF;
|
||||
|
||||
default:
|
||||
return FogQuality.FAST;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,26 +17,25 @@
|
||||
*/
|
||||
package com.seibel.lod.mixin;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.seibel.lod.LodMain;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.seibel.lod.LodMain;
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
|
||||
/**
|
||||
* This class is used to mix in my rendering code
|
||||
* before Minecraft starts rendering blocks.
|
||||
* If this wasn't done the LODs would render on top
|
||||
* If this wasn't done and we used Forge's
|
||||
* render last event, the LODs would render on top
|
||||
* of the normal terrain.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 05-29-2021
|
||||
* @version 9-19-2021
|
||||
*/
|
||||
@Mixin(WorldRenderer.class)
|
||||
public class MixinWorldRenderer
|
||||
@@ -56,7 +55,7 @@ public class MixinWorldRenderer
|
||||
{
|
||||
// only render if LODs are enabled and
|
||||
// only render before solid blocks
|
||||
if (LodConfig.CLIENT.graphics.drawLODs.get() && renderType.equals(RenderType.solid()))
|
||||
LodMain.client_proxy.renderLods(previousPartialTicks);
|
||||
if (ClientProxy.drawLods && renderType.equals(RenderType.solid()))
|
||||
LodMain.client_proxy.renderLods(matrixStackIn, previousPartialTicks);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
public class DataPoint
|
||||
{
|
||||
|
||||
public static short[] createDataPoint(int height, int depth, int red, int green, int blue)
|
||||
{
|
||||
return new short[]{(short) height, (short) depth, (short) red, (short) green, (short) blue};
|
||||
}
|
||||
|
||||
public static short getHeight(short[] dataPoint)
|
||||
{
|
||||
return dataPoint[0];
|
||||
}
|
||||
|
||||
public static short getDepth(short[] dataPoint)
|
||||
{
|
||||
return dataPoint[1];
|
||||
}
|
||||
|
||||
public static short getRed(short[] dataPoint)
|
||||
{
|
||||
return dataPoint[2];
|
||||
}
|
||||
|
||||
public static short getGreen(short[] dataPoint)
|
||||
{
|
||||
return dataPoint[3];
|
||||
}
|
||||
|
||||
public static short getBlue(short[] dataPoint)
|
||||
{
|
||||
return dataPoint[4];
|
||||
}
|
||||
|
||||
public static short[] getHeightDepth(short[] dataPoint)
|
||||
{
|
||||
return new short[]{dataPoint[0], dataPoint[1]};
|
||||
}
|
||||
|
||||
public static int getColor(short[] dataPoint)
|
||||
{
|
||||
int R = (dataPoint[2] << 16) & 0x00FF0000;
|
||||
int G = (dataPoint[3] << 8) & 0x0000FF00;
|
||||
int B = dataPoint[4] & 0x000000FF;
|
||||
return 0xFF000000 | R | G | B;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +1,92 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
public class LevelContainer implements Serializable
|
||||
public interface LevelContainer
|
||||
{
|
||||
public static final char VERTICAL_DATA_DELIMITER = '\t';
|
||||
public static final char DATA_DELIMITER = ' ';
|
||||
|
||||
public static final char DATA_DELIMITER = ',';
|
||||
/**With this you can add data to the level container
|
||||
*
|
||||
* @param data actual data to add in a array of long format.
|
||||
* @param posX x position in the detail level
|
||||
* @param posZ z position in the detail level
|
||||
* @param index z position in the detail level
|
||||
* @return true if correctly added, false otherwise
|
||||
*/
|
||||
public boolean addData(long data, int posX, int posZ, int index);
|
||||
|
||||
public final byte detailLevel;
|
||||
/**With this you can add data to the level container
|
||||
*
|
||||
* @param data actual data to add in a array of long format.
|
||||
* @param posX x position in the detail level
|
||||
* @param posZ z position in the detail level
|
||||
* @return true if correctly added, false otherwise
|
||||
*/
|
||||
public boolean addSingleData(long data, int posX, int posZ);
|
||||
|
||||
public final byte[][][] colors;
|
||||
/**With this you can get data from the level container
|
||||
*
|
||||
* @param posX x position in the detail level
|
||||
* @param posZ z position in the detail level
|
||||
* @return the data in long array format
|
||||
*/
|
||||
public long getData(int posX, int posZ, int index);
|
||||
|
||||
public final short[][] height;
|
||||
/**With this you can get data from the level container
|
||||
*
|
||||
* @param posX x position in the detail level
|
||||
* @param posZ z position in the detail level
|
||||
* @return the data in long array format
|
||||
*/
|
||||
public long getSingleData(int posX, int posZ);
|
||||
|
||||
public final short[][] depth;
|
||||
/**
|
||||
* @param posX x position in the detail level
|
||||
* @param posZ z position in the detail level
|
||||
* @return true only if the data exist
|
||||
*/
|
||||
public boolean doesItExist(int posX, int posZ);
|
||||
|
||||
public final boolean[][] dataExistence;
|
||||
|
||||
public LevelContainer(byte detailLevel, byte[][][] colors, short[][] height, short[][] depth, boolean[][] dataExistence)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
this.colors = colors;
|
||||
this.height = height;
|
||||
this.depth = depth;
|
||||
this.dataExistence = dataExistence;
|
||||
}
|
||||
|
||||
public LevelContainer(String data)
|
||||
{
|
||||
|
||||
int index = 0;
|
||||
int lastIndex = 0;
|
||||
/**
|
||||
* @return return the deatilLevel of this level container
|
||||
*/
|
||||
public byte getDetailLevel();
|
||||
|
||||
|
||||
index = data.indexOf(DATA_DELIMITER, 0);
|
||||
this.detailLevel = (byte) Integer.parseInt(data.substring(0, index));
|
||||
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
public int getMaxVerticalData();
|
||||
|
||||
this.colors = new byte[size][size][3];
|
||||
this.height = new short[size][size];
|
||||
this.depth = new short[size][size];
|
||||
this.dataExistence = new boolean[size][size];
|
||||
int intCol;
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
{
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
intCol = Integer.parseInt(data.substring(lastIndex + 1, index), 16);
|
||||
colors[x][z][0] = (byte) ((intCol >> 16) - 128);
|
||||
colors[x][z][1] = (byte) ((intCol >> 8) - 128);
|
||||
colors[x][z][2] = (byte) (intCol - 128);
|
||||
public void clear(int posX, int posZ);
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
height[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16);
|
||||
/**This return a level container with detail level lower than the current level.
|
||||
* The new level container may use information of this level.
|
||||
* @return the new level container
|
||||
*/
|
||||
public LevelContainer expand();
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
depth[x][z] = Short.parseShort(data.substring(lastIndex + 1, index), 16);
|
||||
/**
|
||||
*
|
||||
* @param lowerLevelContainer lower level where we extract the data
|
||||
* @param posX x position in the detail level to update
|
||||
* @param posZ z position in the detail level to update
|
||||
*/
|
||||
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ);
|
||||
|
||||
lastIndex = index;
|
||||
index = data.indexOf(DATA_DELIMITER, lastIndex + 1);
|
||||
dataExistence[x][z] = Boolean.parseBoolean(data.substring(lastIndex + 1, index));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
* @return data as a String
|
||||
*/
|
||||
public byte[] toDataString();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int combinedCol;
|
||||
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
stringBuilder.append(detailLevel);
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
{
|
||||
//Converting the colors to intColor and then to HEX
|
||||
combinedCol = ((colors[x][z][0] + 128) << 16) | ((colors[x][z][1] + 128) << 8) | ((colors[x][z][2] + 128));
|
||||
stringBuilder.append(Integer.toHexString(combinedCol));
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
stringBuilder.append(Integer.toHexString(height[x][z] & 0xffff));
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
stringBuilder.append(Integer.toHexString(depth[x][z] & 0xffff));
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
stringBuilder.append(dataExistence[x][z]);
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
* @return data as a String
|
||||
*/
|
||||
public int getMaxNumberOfLods();
|
||||
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
* @return data as a String
|
||||
*/
|
||||
public int getMaxMemoryUse();
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package com.seibel.lod.objects.LevelPos;
|
||||
|
||||
public interface ImmutableLevelPos
|
||||
{
|
||||
public LevelPos getConvertedLevelPos(byte newDetailLevel);
|
||||
public LevelPos getRegionModuleLevelPos();
|
||||
}
|
||||
@@ -1,415 +0,0 @@
|
||||
package com.seibel.lod.objects.LevelPos;
|
||||
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
public class LevelPos implements Cloneable, ImmutableLevelPos, MutableLevelPos, Comparable<LevelPos>
|
||||
{
|
||||
public byte detailLevel;
|
||||
public int posX;
|
||||
public int posZ;
|
||||
public boolean flag;
|
||||
|
||||
public LevelPos()
|
||||
{
|
||||
}
|
||||
|
||||
public LevelPos(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
this.posX = posX;
|
||||
this.posZ = posZ;
|
||||
this.detailLevel = detailLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* this operation does not change the state
|
||||
*/
|
||||
public LevelPos getConvertedLevelPos(byte newDetailLevel)
|
||||
{
|
||||
if (newDetailLevel >= detailLevel)
|
||||
{
|
||||
int width = 1 << (newDetailLevel - detailLevel);
|
||||
return new LevelPos(
|
||||
newDetailLevel,
|
||||
Math.floorDiv(posX, width),
|
||||
Math.floorDiv(posZ, width));
|
||||
} else
|
||||
{
|
||||
int width = 1 << (detailLevel - newDetailLevel);
|
||||
return new LevelPos(
|
||||
newDetailLevel,
|
||||
posX * width,
|
||||
posZ * width);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this operation does not change the state
|
||||
*/
|
||||
public LevelPos getRegionModuleLevelPos()
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
return new LevelPos(
|
||||
detailLevel,
|
||||
Math.floorMod(posX, width),
|
||||
Math.floorMod(posZ, width));
|
||||
}
|
||||
|
||||
/**
|
||||
* this operation changes the state
|
||||
*/
|
||||
public void convert(byte newDetailLevel)
|
||||
{
|
||||
if (newDetailLevel >= detailLevel)
|
||||
{
|
||||
int width = 1 << (newDetailLevel - detailLevel);
|
||||
detailLevel = newDetailLevel;
|
||||
posX = Math.floorDiv(posX, width);
|
||||
posZ = Math.floorDiv(posZ, width);
|
||||
} else
|
||||
{
|
||||
int width = 1 << (detailLevel - newDetailLevel);
|
||||
detailLevel = newDetailLevel;
|
||||
posX = posX * width;
|
||||
posZ = posZ * width;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* this operation changes the state
|
||||
*/
|
||||
public void performRegionModule()
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
posX = Math.floorMod(posX, width);
|
||||
posZ = Math.floorMod(posZ, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* this operation changes the state
|
||||
*/
|
||||
public void applyOffset(int xOffset, int zOffset)
|
||||
{
|
||||
posX = posX + xOffset;
|
||||
posX = posZ + zOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* this operation changes the state
|
||||
*/
|
||||
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ)
|
||||
{
|
||||
detailLevel = newDetailLevel;
|
||||
posX = newPosX;
|
||||
posZ = newPosZ;
|
||||
}
|
||||
|
||||
public RegionPos getRegionPos()
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
return new RegionPos(
|
||||
Math.floorDiv(posX, width),
|
||||
Math.floorDiv(posZ, width));
|
||||
}
|
||||
|
||||
public int getRegionPosX()
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
return Math.floorDiv(posX, width);
|
||||
}
|
||||
|
||||
public int getRegionPosZ()
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
return Math.floorDiv(posZ, width);
|
||||
}
|
||||
|
||||
public int getChunkPosX()
|
||||
{
|
||||
if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel)
|
||||
{
|
||||
int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel);
|
||||
return Math.floorDiv(posX, width);
|
||||
} else
|
||||
{
|
||||
int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
return posX * width;
|
||||
}
|
||||
}
|
||||
|
||||
public int getChunkPosZ()
|
||||
{
|
||||
if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel)
|
||||
{
|
||||
int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel);
|
||||
return Math.floorDiv(posZ, width);
|
||||
} else
|
||||
{
|
||||
int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
return posZ * width;
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkPos getChunkPos()
|
||||
{
|
||||
if (LodUtil.CHUNK_DETAIL_LEVEL >= detailLevel)
|
||||
{
|
||||
int width = 1 << (LodUtil.CHUNK_DETAIL_LEVEL - detailLevel);
|
||||
return new ChunkPos(
|
||||
Math.floorDiv(posX, width),
|
||||
Math.floorDiv(posZ, width));
|
||||
} else
|
||||
{
|
||||
int width = 1 << (detailLevel - LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
return new ChunkPos(
|
||||
posX * width,
|
||||
posZ * width);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO fix the region disappearing for a second
|
||||
*/
|
||||
|
||||
public int maxDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ)
|
||||
{
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
int startPosX = regionPosX * 512 + posX * width;
|
||||
int startPosZ = regionPosZ * 512 + posZ * width;
|
||||
int endPosX = startPosX + width;
|
||||
int endPosZ = startPosZ + width;
|
||||
|
||||
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
|
||||
return maxDistance;
|
||||
}
|
||||
|
||||
public int maxDistance(int playerPosX, int playerPosZ)
|
||||
{
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
int startPosX = posX * width;
|
||||
int startPosZ = posZ * width;
|
||||
int endPosX = startPosX + width;
|
||||
int endPosZ = startPosZ + width;
|
||||
|
||||
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
|
||||
return maxDistance;
|
||||
}
|
||||
|
||||
public int minDistance(int playerPosX, int playerPosZ, int regionPosX, int regionPosZ)
|
||||
{
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
int startPosX = regionPosX * 512 + posX * width;
|
||||
int startPosZ = regionPosZ * 512 + posZ * width;
|
||||
int endPosX = startPosX + width;
|
||||
int endPosZ = startPosZ + width;
|
||||
|
||||
boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX;
|
||||
boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ;
|
||||
if (inXArea && inZArea)
|
||||
{
|
||||
return 0;
|
||||
} else if (inXArea)
|
||||
{
|
||||
return Math.min(
|
||||
Math.abs(playerPosZ - startPosZ),
|
||||
Math.abs(playerPosZ - endPosZ)
|
||||
);
|
||||
} else if (inZArea)
|
||||
{
|
||||
return Math.min(
|
||||
Math.abs(playerPosX - startPosX),
|
||||
Math.abs(playerPosX - endPosX)
|
||||
);
|
||||
} else
|
||||
{
|
||||
int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
return minDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public int minDistance(int playerPosX, int playerPosZ)
|
||||
{
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
int startPosX = posX * width;
|
||||
int startPosZ = posZ * width;
|
||||
int endPosX = startPosX + width;
|
||||
int endPosZ = startPosZ + width;
|
||||
|
||||
boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX;
|
||||
boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ;
|
||||
if (inXArea && inZArea)
|
||||
{
|
||||
return 0;
|
||||
} else if (inXArea)
|
||||
{
|
||||
return Math.min(
|
||||
Math.abs(playerPosZ - startPosZ),
|
||||
Math.abs(playerPosZ - endPosZ)
|
||||
);
|
||||
} else if (inZArea)
|
||||
{
|
||||
return Math.min(
|
||||
Math.abs(playerPosX - startPosX),
|
||||
Math.abs(playerPosX - endPosX)
|
||||
);
|
||||
} else
|
||||
{
|
||||
int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
return minDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelPosDistanceComparator getPosComparator(int playerPosX, int playerPosZ)
|
||||
{
|
||||
return new LevelPosDistanceComparator(playerPosX, playerPosZ);
|
||||
}
|
||||
|
||||
public static LevelPosDetailComparator getPosAndDetailComparator(int playerPosX, int playerPosZ)
|
||||
{
|
||||
return new LevelPosDetailComparator(playerPosX, playerPosZ);
|
||||
}
|
||||
|
||||
public static class LevelPosDistanceComparator implements Comparator<LevelPos>
|
||||
{
|
||||
int playerPosX;
|
||||
int playerPosZ;
|
||||
|
||||
public LevelPosDistanceComparator(int playerPosX, int playerPosZ)
|
||||
{
|
||||
this.playerPosX = playerPosX;
|
||||
this.playerPosZ = playerPosZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(LevelPos first, LevelPos second)
|
||||
{
|
||||
return Integer.compare(
|
||||
first.minDistance(playerPosX, playerPosZ),
|
||||
second.minDistance(playerPosX, playerPosZ));
|
||||
}
|
||||
}
|
||||
|
||||
public static class LevelPosDetailComparator implements Comparator<LevelPos>
|
||||
{
|
||||
int playerPosX;
|
||||
int playerPosZ;
|
||||
|
||||
public LevelPosDetailComparator(int playerPosX, int playerPosZ)
|
||||
{
|
||||
this.playerPosX = playerPosX;
|
||||
this.playerPosZ = playerPosZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compare(LevelPos first, LevelPos second)
|
||||
{
|
||||
int compareResult = Integer.compare(second.detailLevel, first.detailLevel);
|
||||
if (compareResult == 0)
|
||||
{
|
||||
compareResult = Integer.compare(
|
||||
first.minDistance(playerPosX, playerPosZ),
|
||||
second.minDistance(playerPosX, playerPosZ));
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
}
|
||||
|
||||
public static LevelPosComparator getComparator()
|
||||
{
|
||||
return new LevelPosComparator();
|
||||
}
|
||||
|
||||
public static class LevelPosComparator implements Comparator<LevelPos>
|
||||
{
|
||||
@Override
|
||||
public int compare(LevelPos first, LevelPos second)
|
||||
{
|
||||
int compareResult = Integer.compare(first.detailLevel, second.detailLevel);
|
||||
if (compareResult == 0)
|
||||
{
|
||||
compareResult = Integer.compare(
|
||||
first.posX,
|
||||
second.posX);
|
||||
}
|
||||
if (compareResult == 0)
|
||||
{
|
||||
compareResult = Integer.compare(
|
||||
first.posZ,
|
||||
second.posZ);
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(LevelPos other)
|
||||
{
|
||||
int compareResult = Integer.compare(this.detailLevel, other.detailLevel);
|
||||
if (compareResult == 0)
|
||||
{
|
||||
compareResult = Integer.compare(
|
||||
this.posX,
|
||||
other.posX);
|
||||
}
|
||||
if (compareResult == 0)
|
||||
{
|
||||
compareResult = Integer.compare(
|
||||
this.posZ,
|
||||
other.posZ);
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public LevelPos clone()
|
||||
{
|
||||
return new LevelPos(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int hash = 7;
|
||||
hash = 31 * hash + (int) detailLevel;
|
||||
hash = 31 * hash + posX;
|
||||
hash = 31 * hash + posZ;
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other)
|
||||
{
|
||||
return (this.detailLevel == ((LevelPos) other).detailLevel &&
|
||||
this.posX == ((LevelPos) other).posX &&
|
||||
this.posZ == ((LevelPos) other).posZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = (detailLevel + " " + posX + " " + posZ);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package com.seibel.lod.objects.LevelPos;
|
||||
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
public interface MutableLevelPos
|
||||
{
|
||||
public void convert(byte newDetailLevel);
|
||||
|
||||
public void performRegionModule();
|
||||
|
||||
public void applyOffset(int xOffset, int zOffset);
|
||||
|
||||
public void changeParameters(byte newDetailLevel, int newPosX, int newPosZ);
|
||||
}
|
||||
@@ -19,22 +19,18 @@ package com.seibel.lod.objects;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidParameterException;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.GenerationPriority;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
import com.seibel.lod.enums.VerticalQuality;
|
||||
import com.seibel.lod.handlers.LodDimensionFileHandler;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.util.LodThreadFactory;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import com.seibel.lod.util.*;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
@@ -47,7 +43,7 @@ import net.minecraft.world.server.ServerWorld;
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 8-8-2021
|
||||
* @version 9-18-2021
|
||||
*/
|
||||
public class LodDimension
|
||||
{
|
||||
@@ -63,12 +59,30 @@ public class LodDimension
|
||||
*/
|
||||
private volatile int halfWidth;
|
||||
|
||||
// these three variables are private to force use of the getWidth() method
|
||||
// which is a safer way to get the width then directly asking the arrays
|
||||
/**
|
||||
* stores all the regions in this dimension
|
||||
*/
|
||||
public volatile LodRegion[][] regions;
|
||||
|
||||
public volatile LodRegion regions[][];
|
||||
public volatile boolean isRegionDirty[][];
|
||||
public volatile boolean regen[][];
|
||||
/** if true that means there are regions in this dimension
|
||||
* that need to have their buffers rebuilt. */
|
||||
/**
|
||||
* stores if the region at the given x and z index needs to be saved to disk
|
||||
*/
|
||||
private volatile boolean[][] isRegionDirty;
|
||||
/**
|
||||
* stores if the region at the given x and z index needs to be regenerated
|
||||
*/
|
||||
private volatile boolean[][] regionNeedsRegen;
|
||||
/**
|
||||
* stores if the buffer size at the given x and z index needs to be changed
|
||||
*/
|
||||
private volatile boolean[][] setupBuffer;
|
||||
|
||||
/**
|
||||
* if true that means there are regions in this dimension
|
||||
* that need to have their buffers rebuilt.
|
||||
*/
|
||||
public volatile boolean regenDimension = false;
|
||||
|
||||
private volatile RegionPos center;
|
||||
@@ -88,8 +102,8 @@ public class LodDimension
|
||||
lastGenChunk = null;
|
||||
dimension = newDimension;
|
||||
width = newWidth;
|
||||
halfWidth = (int) Math.floor(width / 2);
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
halfWidth = width / 2;
|
||||
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
|
||||
if (newDimension != null && lodWorld != null)
|
||||
{
|
||||
try
|
||||
@@ -110,8 +124,8 @@ public class LodDimension
|
||||
{
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(mc.gameDirectory.getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + LodUtil.getDimensionIDFromWorld(mc.level));
|
||||
saveDir = new File(mc.getGameDirectory().getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + mc.getCurrentDimensionId());
|
||||
}
|
||||
|
||||
fileHandler = new LodDimensionFileHandler(saveDir, this);
|
||||
@@ -125,7 +139,8 @@ public class LodDimension
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
regen = new boolean[width][width];
|
||||
regionNeedsRegen = new boolean[width][width];
|
||||
setupBuffer = new boolean[width][width];
|
||||
|
||||
//treeGenerator((int) mc.player.getX(),(int) mc.player.getZ());
|
||||
|
||||
@@ -239,12 +254,10 @@ public class LodDimension
|
||||
|
||||
|
||||
/**
|
||||
* return needed memory in byte
|
||||
* return needed memory in bytes
|
||||
*/
|
||||
public int getMinMemoryNeeded()
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
int count = 0;
|
||||
LodRegion region;
|
||||
|
||||
@@ -255,32 +268,36 @@ public class LodDimension
|
||||
region = regions[x][z];
|
||||
if (region != null)
|
||||
{
|
||||
count += region.getMinMemoryNeeded();
|
||||
count += region.getMinMemoryNeeded(LodConfig.CLIENT.graphics.lodTemplate.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the region at the given X and Z
|
||||
* <br>
|
||||
* Returns null if the region doesn't exist
|
||||
* or is outside the loaded area.
|
||||
*/
|
||||
public LodRegion getRegion(LevelPos levelPos)
|
||||
public LodRegion getRegion(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
int xRegion = LevelPosUtil.getRegion(detailLevel, posX);
|
||||
int zRegion = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||
int xIndex = (xRegion - center.x) + halfWidth;
|
||||
int zIndex = (zRegion - center.z) + halfWidth;
|
||||
|
||||
RegionPos regionPos = levelPos.getRegionPos();
|
||||
int xIndex = (regionPos.x - center.x) + halfWidth;
|
||||
int zIndex = (regionPos.z - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(regionPos.x, regionPos.z))
|
||||
throw new ArrayIndexOutOfBoundsException("Region for level pos " + levelPos + " out of range");
|
||||
if (!regionIsInRange(xRegion, zRegion))
|
||||
return null;
|
||||
//throw new ArrayIndexOutOfBoundsException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " out of range");
|
||||
else if (regions[xIndex][zIndex] == null)
|
||||
throw new InvalidParameterException("Region for level pos " + levelPos + " not currently initialized");
|
||||
else if (regions[xIndex][zIndex].getMinDetailLevel() > levelPos.detailLevel)
|
||||
throw new InvalidParameterException("Region for level pos " + levelPos + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel());
|
||||
return null;
|
||||
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " not currently initialized");
|
||||
else if (regions[xIndex][zIndex].getMinDetailLevel() > detailLevel)
|
||||
return null;
|
||||
//throw new InvalidParameterException("Region for level pos " + LevelPosUtil.toString(detailLevel, posX, posZ) + " currently only reach level " + regions[xIndex][zIndex].getMinDetailLevel());
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
@@ -290,15 +307,25 @@ public class LodDimension
|
||||
* Returns null if the region doesn't exist
|
||||
* or is outside the loaded area.
|
||||
*/
|
||||
public LodRegion getRegion(RegionPos regionPos)
|
||||
public LodRegion getRegion(int regionPosX, int regionPosZ)
|
||||
{
|
||||
int xIndex = (regionPos.x - center.x) + halfWidth;
|
||||
int zIndex = (regionPos.z - center.z) + halfWidth;
|
||||
int xIndex = (regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(regionPos.x, regionPos.z))
|
||||
throw new ArrayIndexOutOfBoundsException("Region " + regionPos + " out of range");
|
||||
if (!regionIsInRange(regionPosX, regionPosZ))
|
||||
return null;
|
||||
//throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range");
|
||||
else if (regions[xIndex][zIndex] == null)
|
||||
throw new InvalidParameterException("Region " + regionPos + " not currently initialized");
|
||||
return null;
|
||||
//throw new InvalidParameterException("Region " + regionPosX + " " + regionPosZ + " not currently initialized");
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful when needing to iterate over every region.
|
||||
*/
|
||||
public LodRegion getRegionByArrayIndex(int xIndex, int zIndex)
|
||||
{
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
@@ -310,7 +337,7 @@ public class LodDimension
|
||||
public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
|
||||
{
|
||||
int xIndex = (newRegion.regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (center.z - newRegion.regionPosZ) + halfWidth;
|
||||
int zIndex = (newRegion.regionPosZ - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ))
|
||||
// out of range
|
||||
@@ -325,7 +352,7 @@ public class LodDimension
|
||||
*/
|
||||
public void treeCutter(int playerPosX, int playerPosZ)
|
||||
{
|
||||
ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos();
|
||||
ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||
if (lastCutChunk == null)
|
||||
lastCutChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1);
|
||||
if (newPlayerChunk.x != lastCutChunk.x || newPlayerChunk.z != lastCutChunk.z)
|
||||
@@ -338,7 +365,6 @@ public class LodDimension
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte levelToCut;
|
||||
LevelPos levelPos = new LevelPos();
|
||||
|
||||
for (int x = 0; x < regions.length; x++)
|
||||
{
|
||||
@@ -351,13 +377,13 @@ public class LodDimension
|
||||
//if this is not the case w
|
||||
if (regions[x][z] != null)
|
||||
{
|
||||
levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ);
|
||||
minDistance = levelPos.minDistance(playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getDistanceTreeCutInverse(minDistance);
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
|
||||
levelToCut = DetailDistanceUtil.getCutLodDetail(detail);
|
||||
if (regions[x][z].getMinDetailLevel() > levelToCut)
|
||||
{
|
||||
regions[x][z].cutTree(levelToCut);
|
||||
setupBuffer[x][z] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,7 +401,8 @@ public class LodDimension
|
||||
public void treeGenerator(int playerPosX, int playerPosZ)
|
||||
{
|
||||
DistanceGenerationMode generationMode = LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get();
|
||||
ChunkPos newPlayerChunk = (new LevelPos((byte) 0, playerPosX, playerPosZ)).getChunkPos();
|
||||
ChunkPos newPlayerChunk = new ChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||
VerticalQuality verticalQuality = LodConfig.CLIENT.worldGenerator.lodQualityMode.get();
|
||||
|
||||
if (lastGenChunk == null)
|
||||
lastGenChunk = new ChunkPos(newPlayerChunk.x + 1, newPlayerChunk.z - 1);
|
||||
@@ -390,42 +417,41 @@ public class LodDimension
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte levelToGen;
|
||||
LevelPos levelPos = new LevelPos();
|
||||
for (int x = 0; x < regions.length; x++)
|
||||
{
|
||||
for (int z = 0; z < regions.length; z++)
|
||||
{
|
||||
regionX = (x + center.x) - halfWidth;
|
||||
regionZ = (z + center.z) - halfWidth;
|
||||
levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ);
|
||||
final RegionPos regionPos = new RegionPos(regionX, regionZ);
|
||||
region = regions[x][z];
|
||||
//We require that the region we are checking is loaded with at least this level
|
||||
|
||||
levelPos.changeParameters(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ);
|
||||
minDistance = levelPos.minDistance(playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getDistanceTreeGenInverse(minDistance);
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance);
|
||||
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
|
||||
if (region == null || region.getGenerationMode() != generationMode)
|
||||
{
|
||||
//First case, region has to be initialized
|
||||
|
||||
//We check if there is a file at the target level
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode);
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
|
||||
//if there is no file we initialize the region
|
||||
if (regions[x][z] == null)
|
||||
{
|
||||
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode);
|
||||
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality);
|
||||
}
|
||||
regen[x][z] = true;
|
||||
regionNeedsRegen[x][z] = true;
|
||||
regenDimension = true;
|
||||
setupBuffer[x][z] = true;
|
||||
|
||||
} else if (region.getMinDetailLevel() > levelToGen)
|
||||
{
|
||||
//Second case, region has been initialized but at a higher level
|
||||
//We expand the region by introducing the missing layer
|
||||
region.expand(levelToGen);
|
||||
setupBuffer[x][z] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -439,32 +465,31 @@ public class LodDimension
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinates it will be overwritten.
|
||||
*/
|
||||
public synchronized Boolean addData(LevelPos levelPos, short[] lodDataPoint, boolean dontSave, boolean serverQuality)
|
||||
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave)
|
||||
{
|
||||
|
||||
// don't continue if the region can't be saved
|
||||
RegionPos regionPos = levelPos.getRegionPos();
|
||||
if (!regionIsInRange(regionPos.x, regionPos.z))
|
||||
{
|
||||
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
|
||||
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||
|
||||
LodRegion region = getRegion(regionPosX, regionPosZ);
|
||||
if (region == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
LodRegion region = getRegion(levelPos);
|
||||
|
||||
boolean nodeAdded = region.addData(levelPos, lodDataPoint, serverQuality);
|
||||
boolean nodeAdded = region.addData(detailLevel, posX, posZ, verticalIndex, data);
|
||||
// only save valid LODs to disk
|
||||
if (!dontSave && fileHandler != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (regionPos.x - center.x) + halfWidth;
|
||||
int zIndex = (regionPos.z - center.z) + halfWidth;
|
||||
int xIndex = (regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
regen[xIndex][zIndex] = true;
|
||||
regionNeedsRegen[xIndex][zIndex] = true;
|
||||
regenDimension = true;
|
||||
} catch (ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// This method was probably called when the dimension was changing size.
|
||||
// Hopefully this shouldn't be an issue.
|
||||
}
|
||||
@@ -472,10 +497,11 @@ public class LodDimension
|
||||
return nodeAdded;
|
||||
}
|
||||
|
||||
public void setToRegen(int xRegion, int zRegion){
|
||||
public void setToRegen(int xRegion, int zRegion)
|
||||
{
|
||||
int xIndex = (xRegion - center.x) + halfWidth;
|
||||
int zIndex = (zRegion - center.z) + halfWidth;
|
||||
regen[xIndex][zIndex] = true;
|
||||
regionNeedsRegen[xIndex][zIndex] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,32 +509,97 @@ public class LodDimension
|
||||
*
|
||||
* @return list of quadTrees
|
||||
*/
|
||||
public void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, int playerPosX, int playerPosZ)
|
||||
public PosToGenerateContainer getDataToGenerate(int maxDataToGenerate, int playerPosX, int playerPosZ)
|
||||
{
|
||||
|
||||
int n = regions.length;
|
||||
int xIndex;
|
||||
int zIndex;
|
||||
PosToGenerateContainer posToGenerate;
|
||||
LodRegion region;
|
||||
RegionPos regionPos;
|
||||
for (int xRegion = 0; xRegion < n; xRegion++)
|
||||
int x, z, dx, dz, t;
|
||||
x = 0;
|
||||
z = 0;
|
||||
dx = 0;
|
||||
dz = -1;
|
||||
switch (LodConfig.CLIENT.worldGenerator.generationPriority.get())
|
||||
{
|
||||
for (int zRegion = 0; zRegion < n; zRegion++)
|
||||
{
|
||||
try
|
||||
default:
|
||||
case NEAR_FIRST:
|
||||
posToGenerate = new PosToGenerateContainer((byte) 10, maxDataToGenerate, 0, playerPosX, playerPosZ);
|
||||
int playerChunkX = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerPosX);
|
||||
int playerChunkZ = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerPosZ);
|
||||
int xChunkToCheck;
|
||||
int zChunkToCheck;
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
long data;
|
||||
int numbChunksWide = (width) * 32;
|
||||
int circleLimit = Integer.MAX_VALUE;
|
||||
for (int i = 0; i < numbChunksWide * numbChunksWide; i++)
|
||||
{
|
||||
xIndex = (xRegion + center.x) - halfWidth;
|
||||
zIndex = (zRegion + center.z) - halfWidth;
|
||||
regionPos = new RegionPos(xIndex, zIndex);
|
||||
region = getRegion(regionPos);
|
||||
region.getDataToGenerate(dataToGenerate, playerPosX, playerPosZ);
|
||||
// use this for square generation
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
//e.printStackTrace();
|
||||
// use this for circular generation
|
||||
if (maxDataToGenerate < 0)
|
||||
{
|
||||
if (circleLimit < Math.abs(x) && circleLimit < Math.abs(z))
|
||||
break;
|
||||
} else if (maxDataToGenerate == 0)
|
||||
{
|
||||
maxDataToGenerate--;
|
||||
circleLimit = (int) (Math.abs(x) * 1.41f);
|
||||
}
|
||||
|
||||
xChunkToCheck = x + playerChunkX;
|
||||
zChunkToCheck = z + playerChunkZ;
|
||||
//distance = LevelPosUtil.maxDistance(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, zChunkToCheck, playerChunkX, playerChunkZ);
|
||||
region = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, zChunkToCheck);
|
||||
if (region == null)
|
||||
continue;
|
||||
detailLevel = region.getMinDetailLevel();
|
||||
|
||||
posX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, detailLevel);
|
||||
posZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, zChunkToCheck, detailLevel);
|
||||
data = getSingleData(detailLevel, posX, posZ);
|
||||
if (DataPointUtil.getGenerationMode(data) < LodConfig.CLIENT.worldGenerator.distanceGenerationMode.get().complexity)
|
||||
{
|
||||
posToGenerate.addPosToGenerate(detailLevel, posX, posZ);
|
||||
if (maxDataToGenerate >= 0)
|
||||
maxDataToGenerate--;
|
||||
}
|
||||
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z)))
|
||||
{
|
||||
t = dx;
|
||||
dx = -dz;
|
||||
dz = t;
|
||||
}
|
||||
x += dx;
|
||||
z += dz;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FAR_FIRST:
|
||||
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, (int) (maxDataToGenerate * 0.25), playerPosX, playerPosZ);
|
||||
int n = regions.length;
|
||||
int xRegion;
|
||||
int zRegion;
|
||||
|
||||
for (int i = 0; i < width * width; i++)
|
||||
{
|
||||
xRegion = x + center.x;
|
||||
zRegion = z + center.z;
|
||||
region = getRegion(xRegion, zRegion);
|
||||
if (region != null)
|
||||
region.getDataToGenerate(posToGenerate, playerPosX, playerPosZ);
|
||||
if ((x == z) || ((x < 0) && (x == -z)) || ((x > 0) && (x == 1 - z)))
|
||||
{
|
||||
t = dx;
|
||||
dx = -dz;
|
||||
dz = t;
|
||||
}
|
||||
x += dx;
|
||||
z += dz;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return posToGenerate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -516,33 +607,27 @@ public class LodDimension
|
||||
*
|
||||
* @return list of nodes
|
||||
*/
|
||||
public void getDataToRender(ConcurrentMap<LevelPos, MutableBoolean> dataToRender, RegionPos regionPos, int playerPosX, int playerPosZ)
|
||||
public void getDataToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX,
|
||||
int playerPosZ)
|
||||
{
|
||||
try
|
||||
{
|
||||
LodRegion region = getRegion(regionPos);
|
||||
region.getDataToRender(dataToRender, playerPosX, playerPosZ);
|
||||
} catch (NullPointerException e)
|
||||
{
|
||||
System.out.println(regionPos);
|
||||
e.printStackTrace();
|
||||
} catch (Exception e)
|
||||
{
|
||||
//e.printStackTrace();
|
||||
}
|
||||
LodRegion region = getRegion(regionPos.x, regionPos.z);
|
||||
if (region != null)
|
||||
region.getDataToRender(posToRender, playerPosX, playerPosZ, LodConfig.CLIENT.worldGenerator.generationPriority.get() == GenerationPriority.NEAR_FIRST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the LodNodeData at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public short[] getData(ChunkPos chunkPos)
|
||||
public int getMaxVerticalData(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPos.x, chunkPos.z);
|
||||
return getData(levelPos);
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return region.getMaxVerticalData(detailLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -552,26 +637,19 @@ public class LodDimension
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public short[] getData(LevelPos levelPos)
|
||||
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
try
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
LodRegion region = getRegion(levelPos);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return region.getData(levelPos);
|
||||
|
||||
} catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
return DataPointUtil.EMPTY_DATA;
|
||||
}
|
||||
|
||||
return region.getData(detailLevel, posX, posZ, verticalIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -582,50 +660,97 @@ public class LodDimension
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public void updateData(LevelPos levelPos)
|
||||
public long getSingleData(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if (levelPos.detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + levelPos.detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodRegion region = getRegion(levelPos);
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
return DataPointUtil.EMPTY_DATA;
|
||||
}
|
||||
|
||||
return region.getSingleData(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
public void clear(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
region.clear(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
public boolean isRegionToRegen(int xIndex, int zIndex)
|
||||
{
|
||||
return regionNeedsRegen[xIndex][zIndex];
|
||||
}
|
||||
|
||||
public boolean isBufferToSetup(int xIndex, int zIndex)
|
||||
{
|
||||
return setupBuffer[xIndex][zIndex];
|
||||
}
|
||||
|
||||
public void setRegenByArrayIndex(int xIndex, int zIndex, boolean newRegen)
|
||||
{
|
||||
regionNeedsRegen[xIndex][zIndex] = newRegen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data point at the given X and Z coordinates
|
||||
* in this dimension.
|
||||
* <br>
|
||||
* Returns null if the LodChunk doesn't exist or
|
||||
* is outside the loaded area.
|
||||
*/
|
||||
public void updateData(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
|
||||
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
region.updateArea(levelPos);
|
||||
region.updateArea(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* return true if and only if the node at that position exist
|
||||
*/
|
||||
public boolean doesDataExist(LevelPos levelPos)
|
||||
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
try
|
||||
{
|
||||
LodRegion region = getRegion(levelPos);
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return region.doesDataExist(levelPos.clone());
|
||||
} catch (Exception e)
|
||||
if (region == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return region.doesDataExist(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the region at the given X and Z coordinates from the
|
||||
* RegionFileHandler.
|
||||
*/
|
||||
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel, DistanceGenerationMode generationMode)
|
||||
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel, DistanceGenerationMode
|
||||
generationMode, VerticalQuality verticalQuality)
|
||||
{
|
||||
if (fileHandler != null)
|
||||
return fileHandler.loadRegionFromFile(detailLevel, regionPos, generationMode);
|
||||
return fileHandler.loadRegionFromFile(detailLevel, regionPos, generationMode, verticalQuality);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
@@ -662,7 +787,9 @@ public class LodDimension
|
||||
return center.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* returns the width of the dimension in regions
|
||||
*/
|
||||
public int getWidth()
|
||||
{
|
||||
if (regions != null)
|
||||
@@ -677,6 +804,7 @@ public class LodDimension
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setRegionWidth(int newWidth)
|
||||
{
|
||||
width = newWidth;
|
||||
@@ -684,7 +812,8 @@ public class LodDimension
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
regen = new boolean[width][width];
|
||||
regionNeedsRegen = new boolean[width][width];
|
||||
setupBuffer = new boolean[width][width];
|
||||
|
||||
// populate isRegionDirty
|
||||
for (int i = 0; i < width; i++)
|
||||
@@ -696,9 +825,6 @@ public class LodDimension
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
LevelPos levelPos;
|
||||
LodRegion region;
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
@@ -721,7 +847,31 @@ public class LodDimension
|
||||
}
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
System.out.println(stringBuilder);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public int getMemoryRequired(int x, int z, LodTemplate template)
|
||||
{
|
||||
/*return regions[x][z].getMinMemoryNeeded(template);*/
|
||||
|
||||
/*TODO add memory use calculated with the following cases
|
||||
switch (LodConfig.CLIENT.graphics.detailDropOff.get())
|
||||
{
|
||||
default:
|
||||
case BY_BLOCK:
|
||||
break;
|
||||
case BY_REGION_FANCY:
|
||||
break;
|
||||
case BY_REGION_FAST:
|
||||
}*/
|
||||
int minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, x, z, halfWidth, halfWidth);
|
||||
int detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
|
||||
int levelToGen = DetailDistanceUtil.getLodDrawDetail(detail);
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelToGen);
|
||||
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detail);
|
||||
int numberOfLods = size * size * maxVerticalData;
|
||||
int memoryUse = numberOfLods * template.getBufferMemoryForSingleLod(maxVerticalData);
|
||||
System.out.println(detail + " " + memoryUse + " " + numberOfLods + " " + template.getBufferMemoryForSingleLod(maxVerticalData));
|
||||
return memoryUse;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||
|
||||
import com.seibel.lod.builders.LodBuilder;
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
import com.seibel.lod.enums.VerticalQuality;
|
||||
import com.seibel.lod.util.DataPointUtil;
|
||||
import com.seibel.lod.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.util.LevelPosUtil;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
/**
|
||||
@@ -19,148 +17,122 @@ import com.seibel.lod.util.LodUtil;
|
||||
* 0 for x, 1 for z in 2D
|
||||
* 0 for x, 1 for y, 2 for z in 3D
|
||||
*/
|
||||
|
||||
public class LodRegion implements Serializable
|
||||
public class LodRegion
|
||||
{
|
||||
//x coord,
|
||||
private byte minDetailLevel;
|
||||
private static final byte POSSIBLE_LOD = 10;
|
||||
private int numberOfPoints;
|
||||
private DistanceGenerationMode generationMode;
|
||||
//private int numberOfPoints;
|
||||
//For each of the following field the first slot is for the level of detail
|
||||
//Important: byte have a [-128, 127] range. When converting from or to int a 128 should be added or removed
|
||||
//If there is a bug with color then it's probably caused by this.
|
||||
//in the future other fields like transparency and light level could be added
|
||||
private byte[][][][] colors;
|
||||
|
||||
private short[][][] height;
|
||||
private LevelContainer[] dataContainer;
|
||||
|
||||
private short[][][] depth;
|
||||
|
||||
private boolean[][][] dataExistence;
|
||||
|
||||
private DistanceGenerationMode generationMode;
|
||||
private VerticalQuality verticalQuality;
|
||||
|
||||
public final int regionPosX;
|
||||
public final int regionPosZ;
|
||||
|
||||
public LodRegion(LevelContainer levelContainer, RegionPos regionPos, DistanceGenerationMode generationMode)
|
||||
public LodRegion(RegionPos regionPos)
|
||||
{
|
||||
this.generationMode = generationMode;
|
||||
this.minDetailLevel = LodUtil.REGION_DETAIL_LEVEL;
|
||||
this.regionPosX = regionPos.x;
|
||||
this.regionPosZ = regionPos.z;
|
||||
this.minDetailLevel = levelContainer.detailLevel;
|
||||
dataContainer = new LevelContainer[POSSIBLE_LOD];
|
||||
}
|
||||
|
||||
//Array of matrices of arrays
|
||||
colors = new byte[POSSIBLE_LOD][][][];
|
||||
public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||
{
|
||||
this.minDetailLevel = minDetailLevel;
|
||||
this.regionPosX = regionPos.x;
|
||||
this.regionPosZ = regionPos.z;
|
||||
this.verticalQuality = verticalQuality;
|
||||
this.generationMode = generationMode;
|
||||
dataContainer = new LevelContainer[POSSIBLE_LOD];
|
||||
|
||||
//Arrays of matrices
|
||||
height = new short[POSSIBLE_LOD][][];
|
||||
depth = new short[POSSIBLE_LOD][][];
|
||||
dataExistence = new boolean[POSSIBLE_LOD][][];
|
||||
|
||||
colors[minDetailLevel] = levelContainer.colors;
|
||||
height[minDetailLevel] = levelContainer.height;
|
||||
depth[minDetailLevel] = levelContainer.depth;
|
||||
dataExistence[minDetailLevel] = levelContainer.dataExistence;
|
||||
|
||||
//Initialize all the different matrices
|
||||
for (byte lod = (byte) (minDetailLevel + 1); lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
|
||||
// Initialize all the different matrices
|
||||
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
|
||||
{
|
||||
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
|
||||
colors[lod] = new byte[size][size][3];
|
||||
height[lod] = new short[size][size];
|
||||
depth[lod] = new short[size][size];
|
||||
dataExistence[lod] = new boolean[size][size];
|
||||
}
|
||||
int width;
|
||||
LevelPos levelPos = new LevelPos();
|
||||
for (byte tempLod = (byte) (minDetailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
|
||||
{
|
||||
width = 1 << (LodUtil.REGION_DETAIL_LEVEL - tempLod);
|
||||
for (int x = 0; x < width; x++)
|
||||
switch (verticalQuality)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
levelPos.changeParameters(tempLod, x, z);
|
||||
update(levelPos);
|
||||
}
|
||||
default:
|
||||
case HEIGHTMAP:
|
||||
dataContainer[lod] = new SingleLevelContainer(lod);
|
||||
break;
|
||||
case MULTI_LOD:
|
||||
dataContainer[lod] = new VerticalLevelContainer(lod);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LodRegion(byte minDetailLevel, RegionPos regionPos, DistanceGenerationMode generationMode)
|
||||
public VerticalQuality getLodQualityMode()
|
||||
{
|
||||
this.generationMode = generationMode;
|
||||
this.minDetailLevel = minDetailLevel;
|
||||
this.regionPosX = regionPos.x;
|
||||
this.regionPosZ = regionPos.z;
|
||||
return verticalQuality;
|
||||
}
|
||||
|
||||
//Array of matrices of arrays
|
||||
colors = new byte[POSSIBLE_LOD][][][];
|
||||
public DistanceGenerationMode getGenerationMode()
|
||||
{
|
||||
return generationMode;
|
||||
}
|
||||
|
||||
//Arrays of matrices
|
||||
height = new short[POSSIBLE_LOD][][];
|
||||
depth = new short[POSSIBLE_LOD][][];
|
||||
dataExistence = new boolean[POSSIBLE_LOD][][];
|
||||
|
||||
|
||||
//Initialize all the different matrices
|
||||
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
|
||||
{
|
||||
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - lod);
|
||||
colors[lod] = new byte[size][size][3];
|
||||
height[lod] = new short[size][size];
|
||||
depth[lod] = new short[size][size];
|
||||
dataExistence[lod] = new boolean[size][size];
|
||||
|
||||
}
|
||||
public int getMaxVerticalData(byte detailLevel)
|
||||
{
|
||||
return dataContainer[detailLevel].getMaxVerticalData();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method can be used to insert data into the LodRegion
|
||||
*
|
||||
* @param levelPos
|
||||
* @param dataPoint
|
||||
* @return
|
||||
* @return if the data was added successfully
|
||||
*/
|
||||
public boolean addData(LevelPos levelPos, short[] dataPoint, boolean serverQuality)
|
||||
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data)
|
||||
{
|
||||
levelPos.performRegionModule();
|
||||
if (!doesDataExist(levelPos) || serverQuality)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
|
||||
//update the number of node present
|
||||
if (this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ]) numberOfPoints++;
|
||||
|
||||
//add the node data
|
||||
this.height[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getHeight(dataPoint);
|
||||
this.depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = DataPoint.getDepth(dataPoint);
|
||||
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] = (byte) (DataPoint.getRed(dataPoint) - 128);
|
||||
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] = (byte) (DataPoint.getGreen(dataPoint) - 128);
|
||||
this.colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] = (byte) (DataPoint.getBlue(dataPoint) - 128);
|
||||
this.dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ] = true;
|
||||
return true;
|
||||
} else
|
||||
// For some reason the dataContainer can contain null entries
|
||||
if (this.dataContainer[detailLevel] == null)
|
||||
{
|
||||
return false;
|
||||
if (verticalQuality == VerticalQuality.HEIGHTMAP)
|
||||
this.dataContainer[detailLevel] = new SingleLevelContainer(detailLevel);
|
||||
else
|
||||
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
|
||||
}
|
||||
|
||||
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the data in the position relative to the level of detail
|
||||
*
|
||||
* @param levelPos
|
||||
* @return the data at the relative pos and level
|
||||
*/
|
||||
public short[] getData(LevelPos levelPos)
|
||||
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
levelPos = levelPos.getRegionModuleLevelPos();
|
||||
return new short[]{height[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
|
||||
depth[levelPos.detailLevel][levelPos.posX][levelPos.posZ],
|
||||
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][0] + 128),
|
||||
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][1] + 128),
|
||||
(short) (colors[levelPos.detailLevel][levelPos.posX][levelPos.posZ][2] + 128)
|
||||
};
|
||||
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return the data in the position relative to the level of detail
|
||||
*
|
||||
* @return the data at the relative pos and level
|
||||
*/
|
||||
public long getSingleData(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return dataContainer[detailLevel].getSingleData(posX, posZ);
|
||||
}
|
||||
|
||||
public void clear(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
dataContainer[detailLevel].clear(posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,47 +140,36 @@ public class LodRegion implements Serializable
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, int playerPosX, int playerPosZ)
|
||||
public void getDataToGenerate(PosToGenerateContainer posToGenerate, int playerPosX, int playerPosZ)
|
||||
{
|
||||
LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0);
|
||||
getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ);
|
||||
getDataToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ);
|
||||
|
||||
}
|
||||
|
||||
private void getDataToGenerate(ConcurrentMap<LevelPos, MutableBoolean> dataToGenerate, LevelPos levelPos, int playerPosX, int playerPosZ)
|
||||
private void getDataToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
|
||||
{
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel);
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
|
||||
//here i calculate the the LevelPos is in range
|
||||
//This is important to avoid any kind of hole in the generation
|
||||
int minDistance = levelPos.minDistance(playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
//nt minDistance = LevelPosUtil.minDistance(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size, playerPosX, playerPosZ);
|
||||
int maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
|
||||
|
||||
int posX = levelPos.posX;
|
||||
int posZ = levelPos.posZ;
|
||||
byte detailLevel = levelPos.detailLevel;
|
||||
byte childDetailLevel = (byte) (detailLevel - 1);
|
||||
int childPosX = posX * 2;
|
||||
int childPosZ = posZ * 2;
|
||||
|
||||
int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel);
|
||||
//we have reached the target detail level
|
||||
|
||||
if (DetailDistanceUtil.getDistanceGenerationInverse(minDistance) > detailLevel)
|
||||
byte targetDetailLevel = DetailDistanceUtil.getLodGenDetail(DetailDistanceUtil.getGenerationDetailFromDistance(maxDistance)).detailLevel;
|
||||
if (targetDetailLevel > detailLevel)
|
||||
{
|
||||
return;
|
||||
} else if (DetailDistanceUtil.getDistanceGenerationInverse(minDistance) == detailLevel)
|
||||
} else if (targetDetailLevel == detailLevel)
|
||||
{
|
||||
if (!doesDataExist(levelPos))
|
||||
if (!doesDataExist(detailLevel, posX, posZ))
|
||||
{
|
||||
levelPos.changeParameters(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
|
||||
if (dataToGenerate.containsKey(levelPos))
|
||||
{
|
||||
dataToGenerate.get(levelPos).setTrue();
|
||||
} else
|
||||
{
|
||||
dataToGenerate.put(levelPos.clone(), new MutableBoolean(true));
|
||||
}
|
||||
posToGenerate.addPosToGenerate(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
|
||||
}
|
||||
} else
|
||||
{
|
||||
@@ -221,19 +182,11 @@ public class LodRegion implements Serializable
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
|
||||
|
||||
if (!doesDataExist(levelPos))
|
||||
if (!doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
|
||||
{
|
||||
num++;
|
||||
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
|
||||
if (dataToGenerate.containsKey(levelPos))
|
||||
{
|
||||
dataToGenerate.get(levelPos).setTrue();
|
||||
} else
|
||||
{
|
||||
dataToGenerate.put(levelPos.clone(), new MutableBoolean(true));
|
||||
}
|
||||
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -245,8 +198,7 @@ public class LodRegion implements Serializable
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
|
||||
getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ);
|
||||
getDataToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,21 +207,12 @@ public class LodRegion implements Serializable
|
||||
{
|
||||
if (DetailDistanceUtil.getLodGenDetail(childDetailLevel).detailLevel <= (childDetailLevel))
|
||||
{
|
||||
levelPos.changeParameters(detailLevel, posX, posZ);
|
||||
levelPos.convert(childDetailLevel);
|
||||
if (!doesDataExist(levelPos))
|
||||
if (!doesDataExist(childDetailLevel, childPosX, childPosZ))
|
||||
{
|
||||
levelPos.changeParameters(levelPos.detailLevel, levelPos.posX + regionPosX * childSize, levelPos.posZ + regionPosZ * childSize);
|
||||
if (dataToGenerate.containsKey(levelPos))
|
||||
{
|
||||
dataToGenerate.get(levelPos).setTrue();
|
||||
} else
|
||||
{
|
||||
dataToGenerate.put(levelPos.clone(), new MutableBoolean(true));
|
||||
}
|
||||
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + regionPosX * childSize, childPosZ + regionPosZ * childSize);
|
||||
} else
|
||||
{
|
||||
getDataToGenerate(dataToGenerate, levelPos, playerPosX, playerPosZ);
|
||||
getDataToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,219 +223,180 @@ public class LodRegion implements Serializable
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public void getDataToRender(ConcurrentMap<LevelPos, MutableBoolean> dataToRender, int playerPosX, int playerPosZ)
|
||||
public void getDataToRender(PosToRenderContainer posToRender, int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
|
||||
{
|
||||
LevelPos levelPos = new LevelPos(LodUtil.REGION_DETAIL_LEVEL, 0, 0);
|
||||
getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ);
|
||||
getDataToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
private void getDataToRender(ConcurrentMap<LevelPos, MutableBoolean> dataToRender, LevelPos levelPos, int playerPosX, int playerPosZ)
|
||||
private void getDataToRender(PosToRenderContainer posToRender, byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
|
||||
{
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - levelPos.detailLevel);
|
||||
|
||||
int posX = levelPos.posX;
|
||||
int posZ = levelPos.posZ;
|
||||
byte detailLevel = levelPos.detailLevel;
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
|
||||
//here i calculate the the LevelPos is in range
|
||||
//This is important to avoid any kind of hole in the rendering
|
||||
int maxDistance = levelPos.maxDistance(playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
|
||||
byte supposedLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDistanceRenderingInverse(maxDistance));
|
||||
if (supposedLevel > detailLevel)
|
||||
return;
|
||||
else if (supposedLevel == detailLevel)
|
||||
byte supposedLevel;
|
||||
int maxDistance;
|
||||
boolean stopNow = false;
|
||||
int minDistance;
|
||||
int childLevel;
|
||||
switch (LodConfig.CLIENT.graphics.detailDropOff.get())
|
||||
{
|
||||
if (dataToRender.containsKey(levelPos))
|
||||
{
|
||||
levelPos.changeParameters(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
|
||||
try
|
||||
default:
|
||||
case BY_CHUNK:
|
||||
maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
supposedLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance));
|
||||
minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance));
|
||||
stopNow = detailLevel == childLevel - 1;
|
||||
|
||||
break;
|
||||
case BY_REGION_FANCY:
|
||||
supposedLevel = minDetailLevel;
|
||||
break;
|
||||
case BY_REGION_FAST:
|
||||
int playerRegionX = LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerPosX);
|
||||
int playerRegionZ = LevelPosUtil.getRegion(LodUtil.BLOCK_DETAIL_LEVEL, playerPosZ);
|
||||
if (playerRegionX == regionPosX && playerRegionZ == regionPosZ)
|
||||
{
|
||||
dataToRender.get(levelPos).setTrue();
|
||||
}catch (Exception e){
|
||||
/*TODO Fix this exception*/
|
||||
// This seems to happen more often when using an elytra in an amplified world
|
||||
// maybe it has something to do with the dimensions moving?
|
||||
ClientProxy.LOGGER.error("getDataToRender had a error at " + levelPos.getRegionPos() + ". Exception: " + e.getMessage());
|
||||
dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new MutableBoolean(true));
|
||||
maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
supposedLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance));
|
||||
minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance));
|
||||
stopNow = detailLevel == childLevel - 1;
|
||||
} else
|
||||
{
|
||||
maxDistance = LevelPosUtil.maxDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerRegionX * 512 + 256, playerRegionZ * 512 + 256);
|
||||
supposedLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance));
|
||||
}
|
||||
} else
|
||||
{
|
||||
dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new MutableBoolean(true));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (stopNow)
|
||||
{
|
||||
posToRender.addPosToRender(detailLevel,
|
||||
posX + regionPosX * size,
|
||||
posZ + regionPosZ * size);
|
||||
} else if (supposedLevel > detailLevel)
|
||||
{
|
||||
return;
|
||||
} else if (supposedLevel == detailLevel)
|
||||
{
|
||||
posToRender.addPosToRender(detailLevel,
|
||||
posX + regionPosX * size,
|
||||
posZ + regionPosZ * size);
|
||||
} else //case where (detailLevel > supposedLevel)
|
||||
{
|
||||
int childPosX = posX * 2;
|
||||
int childPosZ = posZ * 2;
|
||||
byte childDetailLevel = (byte) (detailLevel - 1);
|
||||
int childrenCount = 0;
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
|
||||
if (doesDataExist(levelPos)) childrenCount++;
|
||||
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
|
||||
{
|
||||
if (!requireCorrectDetailLevel)
|
||||
{
|
||||
childrenCount++;
|
||||
} else
|
||||
{
|
||||
getDataToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If all the four children exist we go deeper
|
||||
if (childrenCount == 4)
|
||||
{
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
levelPos.changeParameters((byte) (detailLevel - 1), childPosX + x, childPosZ + z);
|
||||
getDataToRender(dataToRender, levelPos, playerPosX, playerPosZ);
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
|
||||
levelPos.changeParameters(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size);
|
||||
if (dataToRender.containsKey(levelPos))
|
||||
if (!requireCorrectDetailLevel)
|
||||
{
|
||||
if (childrenCount == 4)
|
||||
{
|
||||
if (dataToRender.get(levelPos) == null)
|
||||
dataToRender.replace(levelPos, new MutableBoolean());
|
||||
dataToRender.get(levelPos).setTrue();
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
getDataToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
dataToRender.put(new LevelPos(detailLevel, posX + regionPosX * size, posZ + regionPosZ * size), new MutableBoolean(true));
|
||||
posToRender.addPosToRender(detailLevel,
|
||||
posX + regionPosX * size,
|
||||
posZ + regionPosZ * size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param levelPos
|
||||
*
|
||||
*/
|
||||
public void updateArea(LevelPos levelPos)
|
||||
public void updateArea(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
int width;
|
||||
int startX;
|
||||
int startZ;
|
||||
byte detailLevel = levelPos.detailLevel;
|
||||
int posX = levelPos.posX;
|
||||
int posZ = levelPos.posZ;
|
||||
for (byte bottom = (byte) (minDetailLevel + 1); bottom <= detailLevel; bottom++)
|
||||
{
|
||||
levelPos.convert(bottom);
|
||||
startX = levelPos.posX;
|
||||
startZ = levelPos.posZ;
|
||||
startX = LevelPosUtil.convert(detailLevel, posX, bottom);
|
||||
startZ = LevelPosUtil.convert(detailLevel, posZ, bottom);
|
||||
width = 1 << (detailLevel - bottom);
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
for (int z = 0; z < width; z++)
|
||||
{
|
||||
levelPos.changeParameters(bottom, startX + x, startZ + z);
|
||||
update(levelPos);
|
||||
update(bottom, startX + x, startZ + z);
|
||||
}
|
||||
}
|
||||
levelPos.changeParameters(detailLevel, posX, posZ);
|
||||
}
|
||||
for (byte tempLod = (byte) (detailLevel + 1); tempLod <= LodUtil.REGION_DETAIL_LEVEL; tempLod++)
|
||||
for (byte up = (byte) (detailLevel + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++)
|
||||
{
|
||||
levelPos.convert(tempLod);
|
||||
update(levelPos);
|
||||
update(up,
|
||||
LevelPosUtil.convert(detailLevel, posX, up),
|
||||
LevelPosUtil.convert(detailLevel, posZ, up));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param levelPos
|
||||
*
|
||||
*/
|
||||
private void update(LevelPos levelPos)
|
||||
private void update(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
levelPos.performRegionModule();
|
||||
int numberOfChildren = 0;
|
||||
int numberOfVoidChildren = 0;
|
||||
|
||||
int tempRed = 0;
|
||||
int tempGreen = 0;
|
||||
int tempBlue = 0;
|
||||
int tempHeight = 0;
|
||||
int tempDepth = 0;
|
||||
int newPosX;
|
||||
int newPosZ;
|
||||
byte newDetailLevel;
|
||||
int detailLevel = levelPos.detailLevel;
|
||||
int posX = levelPos.posX;
|
||||
int posZ = levelPos.posZ;
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
newPosX = 2 * posX + x;
|
||||
newPosZ = 2 * posZ + z;
|
||||
newDetailLevel = (byte) (detailLevel - 1);
|
||||
levelPos.changeParameters(newDetailLevel, newPosX, newPosZ);
|
||||
if (doesDataExist(levelPos))
|
||||
{
|
||||
if (height[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_HEIGHT
|
||||
&& depth[newDetailLevel][newPosX][newPosZ] != LodBuilder.DEFAULT_DEPTH)
|
||||
{
|
||||
numberOfChildren++;
|
||||
|
||||
tempRed += colors[newDetailLevel][newPosX][newPosZ][0];
|
||||
tempGreen += colors[newDetailLevel][newPosX][newPosZ][1];
|
||||
tempBlue += colors[newDetailLevel][newPosX][newPosZ][2];
|
||||
tempHeight += height[newDetailLevel][newPosX][newPosZ];
|
||||
tempDepth += depth[newDetailLevel][newPosX][newPosZ];
|
||||
} else
|
||||
{
|
||||
// void children have the default height (most likely -1)
|
||||
// and represent a LOD with no blocks in it
|
||||
numberOfVoidChildren++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (numberOfChildren > 0)
|
||||
{
|
||||
colors[detailLevel][posX][posZ][0] = (byte) (tempRed / numberOfChildren);
|
||||
colors[detailLevel][posX][posZ][1] = (byte) (tempGreen / numberOfChildren);
|
||||
colors[detailLevel][posX][posZ][2] = (byte) (tempBlue / numberOfChildren);
|
||||
height[detailLevel][posX][posZ] = (short) (tempHeight / numberOfChildren);
|
||||
depth[detailLevel][posX][posZ] = (short) (tempDepth / numberOfChildren);
|
||||
dataExistence[detailLevel][posX][posZ] = true;
|
||||
} else if (numberOfVoidChildren > 0)
|
||||
{
|
||||
colors[detailLevel][posX][posZ][0] = (byte) 0;
|
||||
colors[detailLevel][posX][posZ][1] = (byte) 0;
|
||||
colors[detailLevel][posX][posZ][2] = (byte) 0;
|
||||
|
||||
height[detailLevel][posX][posZ] = LodBuilder.DEFAULT_HEIGHT;
|
||||
depth[detailLevel][posX][posZ] = LodBuilder.DEFAULT_DEPTH;
|
||||
|
||||
dataExistence[detailLevel][posX][posZ] = true;
|
||||
}
|
||||
dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param levelPos
|
||||
* @return
|
||||
*/
|
||||
public boolean doesDataExist(LevelPos levelPos)
|
||||
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
try
|
||||
{
|
||||
levelPos = levelPos.getRegionModuleLevelPos();
|
||||
return dataExistence[levelPos.detailLevel][levelPos.posX][levelPos.posZ];
|
||||
} catch (NullPointerException e)
|
||||
{
|
||||
if (detailLevel < minDetailLevel) return false;
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
if (dataContainer == null || dataContainer[detailLevel] == null)
|
||||
return false;
|
||||
}
|
||||
return dataContainer[detailLevel].doesItExist(posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public DistanceGenerationMode getGenerationMode()
|
||||
public byte getGenerationMode(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return generationMode;
|
||||
if (dataContainer[detailLevel].doesItExist(posX, posZ))
|
||||
{
|
||||
//We take the bottom information always
|
||||
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleData(posX, posZ));
|
||||
} else
|
||||
{
|
||||
return DistanceGenerationMode.NONE.complexity;
|
||||
}
|
||||
}
|
||||
|
||||
public byte getMinDetailLevel()
|
||||
@@ -512,7 +416,7 @@ public class LodRegion implements Serializable
|
||||
{
|
||||
throw new IllegalArgumentException("getLevel asked for a level that does not exist: minimum " + minDetailLevel + " level requested " + detailLevel);
|
||||
}
|
||||
return new LevelContainer(detailLevel, colors[detailLevel], height[detailLevel], depth[detailLevel], dataExistence[detailLevel]);
|
||||
return dataContainer[detailLevel];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -520,15 +424,12 @@ public class LodRegion implements Serializable
|
||||
*/
|
||||
public void addLevel(LevelContainer levelContainer)
|
||||
{
|
||||
if (levelContainer.detailLevel < minDetailLevel - 1)
|
||||
if (levelContainer.getDetailLevel() < minDetailLevel - 1)
|
||||
{
|
||||
throw new IllegalArgumentException("addLevel requires a level that is at least the minimum level of the region -1 ");
|
||||
}
|
||||
if (levelContainer.detailLevel == minDetailLevel - 1) minDetailLevel = levelContainer.detailLevel;
|
||||
colors[levelContainer.detailLevel] = levelContainer.colors;
|
||||
height[levelContainer.detailLevel] = levelContainer.height;
|
||||
depth[levelContainer.detailLevel] = levelContainer.depth;
|
||||
dataExistence[levelContainer.detailLevel] = levelContainer.dataExistence;
|
||||
if (levelContainer.getDetailLevel() == minDetailLevel - 1) minDetailLevel = levelContainer.getDetailLevel();
|
||||
dataContainer[levelContainer.getDetailLevel()] = levelContainer;
|
||||
|
||||
}
|
||||
|
||||
@@ -541,10 +442,7 @@ public class LodRegion implements Serializable
|
||||
{
|
||||
for (byte tempLod = 0; tempLod < detailLevel; tempLod++)
|
||||
{
|
||||
colors[tempLod] = new byte[0][0][0];
|
||||
height[tempLod] = new short[0][0];
|
||||
depth[tempLod] = new short[0][0];
|
||||
dataExistence[tempLod] = new boolean[0][0];
|
||||
dataContainer[tempLod] = null;
|
||||
}
|
||||
minDetailLevel = detailLevel;
|
||||
}
|
||||
@@ -557,13 +455,13 @@ public class LodRegion implements Serializable
|
||||
{
|
||||
if (detailLevel < minDetailLevel)
|
||||
{
|
||||
for (byte tempLod = detailLevel; tempLod < minDetailLevel; tempLod++)
|
||||
for (byte tempLod = (byte) (minDetailLevel - 1); tempLod >= detailLevel; tempLod--)
|
||||
{
|
||||
int size = (short) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod);
|
||||
colors[tempLod] = new byte[size][size][3];
|
||||
height[tempLod] = new short[size][size];
|
||||
depth[tempLod] = new short[size][size];
|
||||
dataExistence[tempLod] = new boolean[size][size];
|
||||
if (dataContainer[tempLod + 1] == null)
|
||||
{
|
||||
dataContainer[tempLod + 1] = new SingleLevelContainer((byte) (tempLod + 1));
|
||||
}
|
||||
dataContainer[tempLod] = dataContainer[tempLod + 1].expand();
|
||||
}
|
||||
minDetailLevel = detailLevel;
|
||||
}
|
||||
@@ -579,8 +477,10 @@ public class LodRegion implements Serializable
|
||||
|
||||
/**
|
||||
* return needed memory in byte
|
||||
*
|
||||
* @param template
|
||||
*/
|
||||
public int getMinMemoryNeeded()
|
||||
public int getMinMemoryNeeded(LodTemplate template)
|
||||
{
|
||||
int count = 0;
|
||||
for (byte tempLod = LodUtil.REGION_DETAIL_LEVEL; tempLod > minDetailLevel; tempLod--)
|
||||
@@ -588,7 +488,7 @@ public class LodRegion implements Serializable
|
||||
//i'm doing a upper limit of the minimum
|
||||
//Color should be just 3 byte but i'm gonna calculate as 12 byte
|
||||
//Height and depth should be just 4 byte but i'm gonna calculate as 8 byte
|
||||
count += Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod) * (8 + 3 + 2 + 2 + 1);
|
||||
count += dataContainer[tempLod].getMaxMemoryUse() * template.getBufferMemoryForSingleLod(dataContainer[tempLod].getMaxVerticalData());
|
||||
//count += Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - tempLod) * (24 + 8 + 8 + 8 + 8);
|
||||
}
|
||||
return count;
|
||||
@@ -599,4 +499,12 @@ public class LodRegion implements Serializable
|
||||
{
|
||||
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
|
||||
}
|
||||
|
||||
public int getNumberOfLods()
|
||||
{
|
||||
int count = 0;
|
||||
for (LevelContainer container : dataContainer)
|
||||
count += container.getMaxNumberOfLods();
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import org.lwjgl.system.CallbackI;
|
||||
import com.seibel.lod.util.LevelPosUtil;
|
||||
|
||||
public class PosToGenerateContainer
|
||||
{
|
||||
@@ -12,90 +12,119 @@ public class PosToGenerateContainer
|
||||
private int maxFarSize;
|
||||
private int nearSize;
|
||||
private int farSize;
|
||||
private int[][] posToGenerate;
|
||||
private int[][] nearPosToGenerate;
|
||||
private int[][] farPosToGenerate;
|
||||
|
||||
|
||||
public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int maxFarDataToGenerate, int playerPosX, int playerPosZ)
|
||||
{
|
||||
this.playerPosX = playerPosX;
|
||||
this.playerPosZ = playerPosZ;
|
||||
this.farMinDetail = farMinDetail;
|
||||
maxNearSize = maxDataToGenerate;
|
||||
maxNearSize = maxDataToGenerate-maxFarDataToGenerate;
|
||||
maxFarSize = maxFarDataToGenerate;
|
||||
maxSize = maxDataToGenerate;
|
||||
nearSize = 0;
|
||||
farSize = 0;
|
||||
posToGenerate = new int[maxDataToGenerate][4];
|
||||
}
|
||||
|
||||
public void addPosToGenerate(int[] levelPos)
|
||||
{
|
||||
addPosToGenerate(LevelPosUtil.getDetailLevel(levelPos), LevelPosUtil.getPosX(levelPos), LevelPosUtil.getPosZ(levelPos));
|
||||
nearPosToGenerate = new int[maxDataToGenerate][4];
|
||||
farPosToGenerate = new int[maxDataToGenerate][4];
|
||||
}
|
||||
|
||||
public void addPosToGenerate(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
|
||||
int index;
|
||||
int[] tempPos = new int[]{
|
||||
detailLevel,
|
||||
posX,
|
||||
posZ,
|
||||
distance};
|
||||
if (detailLevel >= farMinDetail)
|
||||
{//We are introducing a position in the far array
|
||||
if (farSize < maxFarSize)
|
||||
{
|
||||
farSize++;
|
||||
if (nearSize == maxNearSize)
|
||||
{
|
||||
nearSize--;
|
||||
}
|
||||
maxNearSize--;
|
||||
}
|
||||
index = posToGenerate.length - farSize;
|
||||
while (index < posToGenerate.length - 1 && LevelPosUtil.compareLevelAndDistance(tempPos, posToGenerate[index + 1]) <= 0)
|
||||
{
|
||||
posToGenerate[index] = posToGenerate[index + 1];
|
||||
index++;
|
||||
}
|
||||
if (index <= posToGenerate.length - 1)
|
||||
posToGenerate[index] = tempPos;
|
||||
} else
|
||||
{//We are introducing a position in the near array
|
||||
if (nearSize < maxNearSize)
|
||||
nearSize++;
|
||||
index = nearSize - 1;
|
||||
|
||||
while (index > 0 && LevelPosUtil.compareDistance(tempPos, posToGenerate[index - 1]) <= 0)
|
||||
if(farSize < farPosToGenerate.length)
|
||||
farSize++;
|
||||
index = farSize;
|
||||
//while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
|
||||
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
|
||||
{
|
||||
posToGenerate[index] = posToGenerate[index - 1];
|
||||
farPosToGenerate[index][0] = farPosToGenerate[index - 1][0];
|
||||
farPosToGenerate[index][1] = farPosToGenerate[index - 1][1];
|
||||
farPosToGenerate[index][2] = farPosToGenerate[index - 1][2];
|
||||
farPosToGenerate[index][3] = farPosToGenerate[index - 1][3];
|
||||
index--;
|
||||
}
|
||||
if (index >= 0)
|
||||
posToGenerate[index] = tempPos;
|
||||
if (index != farSize-1 || farSize != farPosToGenerate.length)
|
||||
{
|
||||
farPosToGenerate[index][0] = detailLevel + 1;
|
||||
farPosToGenerate[index][1] = posX;
|
||||
farPosToGenerate[index][2] = posZ;
|
||||
farPosToGenerate[index][3] = distance;
|
||||
}
|
||||
} else
|
||||
{//We are introducing a position in the near array
|
||||
if(nearSize < nearPosToGenerate.length)
|
||||
nearSize++;
|
||||
index = nearSize-1;
|
||||
while (index > 0 && LevelPosUtil.compareDistance(distance, nearPosToGenerate[index - 1][3]) <= 0)
|
||||
{
|
||||
nearPosToGenerate[index][0] = nearPosToGenerate[index - 1][0];
|
||||
nearPosToGenerate[index][1] = nearPosToGenerate[index - 1][1];
|
||||
nearPosToGenerate[index][2] = nearPosToGenerate[index - 1][2];
|
||||
nearPosToGenerate[index][3] = nearPosToGenerate[index - 1][3];
|
||||
index--;
|
||||
}
|
||||
if (index != nearSize-1 || nearSize != nearPosToGenerate.length)
|
||||
{
|
||||
nearPosToGenerate[index][0] = detailLevel + 1;
|
||||
nearPosToGenerate[index][1] = posX;
|
||||
nearPosToGenerate[index][2] = posZ;
|
||||
nearPosToGenerate[index][3] = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getNumberOfPos()
|
||||
{
|
||||
return farSize + nearSize;
|
||||
return nearSize+farSize;
|
||||
}
|
||||
|
||||
public int[] getNthPos(int n)
|
||||
public int getNumberOfNearPos()
|
||||
{
|
||||
int index;
|
||||
if (n > farSize * 2)
|
||||
index = n - farSize;
|
||||
else if (n % 2 == 0)
|
||||
index = n / 2;
|
||||
return nearSize;
|
||||
}
|
||||
|
||||
public int getNumberOfFarPos()
|
||||
{
|
||||
return farSize;
|
||||
}
|
||||
|
||||
public int getNthDetail(int n, boolean near)
|
||||
{
|
||||
if (near)
|
||||
return nearPosToGenerate[n][0];
|
||||
else
|
||||
index = posToGenerate.length - n / 2 - 1;
|
||||
return posToGenerate[index];
|
||||
return farPosToGenerate[n][0];
|
||||
}
|
||||
public int getNthPosX(int n, boolean near)
|
||||
{
|
||||
if (near)
|
||||
return nearPosToGenerate[n][1];
|
||||
else
|
||||
return farPosToGenerate[n][1];
|
||||
}
|
||||
public int getNthPosZ(int n, boolean near)
|
||||
{
|
||||
if (near)
|
||||
return nearPosToGenerate[n][2];
|
||||
else
|
||||
return farPosToGenerate[n][2];
|
||||
}
|
||||
public int getNthGeneration(int n, boolean near)
|
||||
{
|
||||
if (near)
|
||||
return nearPosToGenerate[n][3];
|
||||
else
|
||||
return farPosToGenerate[n][3];
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
{/*
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Number of pos to generate ");
|
||||
builder.append(farSize + nearSize);
|
||||
@@ -111,7 +140,7 @@ public class PosToGenerateContainer
|
||||
builder.append('\n');
|
||||
for (int i = 0; i < nearSize; i++)
|
||||
{
|
||||
builder.append(posToGenerate[i][0]);
|
||||
builder.append(posToGenerate[i][0]-1);
|
||||
builder.append(" ");
|
||||
builder.append(posToGenerate[i][1]);
|
||||
builder.append(" ");
|
||||
@@ -125,7 +154,7 @@ public class PosToGenerateContainer
|
||||
builder.append('\n');
|
||||
for (int i = maxSize - 1; i >= maxSize - farSize; i--)
|
||||
{
|
||||
builder.append(posToGenerate[i][0]);
|
||||
builder.append(posToGenerate[i][0]-1);
|
||||
builder.append(" ");
|
||||
builder.append(posToGenerate[i][1]);
|
||||
builder.append(" ");
|
||||
@@ -135,6 +164,7 @@ public class PosToGenerateContainer
|
||||
builder.append('\n');
|
||||
}
|
||||
builder.append('\n');
|
||||
return builder.toString();
|
||||
return builder.toString();*/
|
||||
return " ";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,88 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.util.LevelPosUtil;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-18-2021
|
||||
*/
|
||||
public class PosToRenderContainer
|
||||
{
|
||||
private byte minDetail;
|
||||
public byte minDetail;
|
||||
private int size;
|
||||
private int regionPosX;
|
||||
private int regionPosZ;
|
||||
private int numberOfPosToRender;
|
||||
private int[][] posToRender;
|
||||
private int[] posToRender;
|
||||
/*TODO this population matrix could be converted to boolean to improve memory use*/
|
||||
private byte[][] population;
|
||||
|
||||
public PosToRenderContainer(byte minDetail)
|
||||
public PosToRenderContainer(byte minDetail, int regionPosX, int regionPosZ)
|
||||
{
|
||||
this.minDetail = minDetail;
|
||||
this.numberOfPosToRender = 0;
|
||||
posToRender = new int[1][4];
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
|
||||
this.regionPosX = regionPosX;
|
||||
this.regionPosZ = regionPosZ;
|
||||
this.size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
|
||||
posToRender = new int[size*size*3];
|
||||
population = new byte[size][size];
|
||||
}
|
||||
|
||||
public void addPosToRender(int[] levelPos)
|
||||
public void addPosToRender(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if(numberOfPosToRender >= posToRender.length)
|
||||
posToRender = Arrays.copyOf(posToRender, posToRender.length*2);
|
||||
posToRender[numberOfPosToRender] = levelPos;
|
||||
// When rapidly changing dimensions the bufferBuidler can cause this,
|
||||
// James isn't sure why, but this will prevent an exception at
|
||||
// the very least (while stilling logging the problem).
|
||||
if (numberOfPosToRender >= posToRender.length)
|
||||
{
|
||||
// This is might be due to dimensions having a different width
|
||||
// when first loading in
|
||||
ClientProxy.LOGGER.error("Unable to addPosToRender. numberOfPosToRender [" + numberOfPosToRender +"] detailLevel [" + detailLevel + "] Pos [" + posX + "," + posZ + "]");
|
||||
numberOfPosToRender++; // incrementing so we can see how many pos over the limit we would go
|
||||
return;
|
||||
}
|
||||
|
||||
//if(numberOfPosToRender >= posToRender.length)
|
||||
// posToRender = Arrays.copyOf(posToRender, posToRender.length*2);
|
||||
posToRender[numberOfPosToRender*3 + 0] = detailLevel;
|
||||
posToRender[numberOfPosToRender*3 + 1] = posX;
|
||||
posToRender[numberOfPosToRender*3 + 2] = posZ;
|
||||
numberOfPosToRender++;
|
||||
int[] newLevelPos = LevelPosUtil.getRegionModule(LevelPosUtil.convert(levelPos, minDetail));
|
||||
population[LevelPosUtil.getPosZ(newLevelPos)][LevelPosUtil.getPosZ(newLevelPos)] = (byte) (LevelPosUtil.getDetailLevel(levelPos) + 1);
|
||||
population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posX,minDetail))]
|
||||
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posZ,minDetail))] = (byte) (detailLevel + 1);
|
||||
}
|
||||
|
||||
public boolean contains(int[] levelPos)
|
||||
public boolean contains(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
int[] newLevelPos = LevelPosUtil.convert(LevelPosUtil.getRegionModule(levelPos), minDetail);
|
||||
return (population[LevelPosUtil.getPosZ(newLevelPos)][LevelPosUtil.getPosZ(newLevelPos)] == (LevelPosUtil.getDetailLevel(levelPos) + 1));
|
||||
if(LevelPosUtil.getRegion(detailLevel, posX) == regionPosX && LevelPosUtil.getRegion(detailLevel, posZ) == regionPosZ)
|
||||
{
|
||||
return (population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posX,minDetail))]
|
||||
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel,posZ,minDetail))] == (detailLevel + 1));
|
||||
}else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public void clear(byte minDetail, int regionPosX, int regionPosZ){
|
||||
this.numberOfPosToRender = 0;
|
||||
this.regionPosX = regionPosX;
|
||||
this.regionPosZ = regionPosZ;
|
||||
if(this.minDetail == minDetail)
|
||||
{
|
||||
Arrays.fill(posToRender, 0);
|
||||
for(int i = 0; i< population.length; i++)
|
||||
Arrays.fill(population[i], (byte) 0);
|
||||
}else{
|
||||
this.minDetail = minDetail;
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
|
||||
posToRender = new int[size*size*3];
|
||||
population = new byte[size][size];
|
||||
}
|
||||
}
|
||||
|
||||
public int getNumberOfPos()
|
||||
@@ -42,22 +90,34 @@ public class PosToRenderContainer
|
||||
return numberOfPosToRender;
|
||||
}
|
||||
|
||||
public int[] getNthPos(int n)
|
||||
public byte getNthDetailLevel(int n)
|
||||
{
|
||||
return posToRender[n];
|
||||
return (byte) posToRender[n*3 + 0];
|
||||
}
|
||||
public int getNthPosX(int n)
|
||||
{
|
||||
return posToRender[n*3 + 1];
|
||||
}
|
||||
public int getNthPosZ(int n)
|
||||
{
|
||||
return posToRender[n*3 + 2];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("To render ");
|
||||
builder.append(numberOfPosToRender);
|
||||
builder.append('\n');
|
||||
for(int i = 0; i < numberOfPosToRender; i++)
|
||||
{
|
||||
builder.append(posToRender[i][0]);
|
||||
builder.append(posToRender[i*3 + 0]);
|
||||
builder.append(" ");
|
||||
builder.append(posToRender[i][1]);
|
||||
builder.append(posToRender[i*3 + 1]);
|
||||
builder.append(" ");
|
||||
builder.append(posToRender[i][2]);
|
||||
builder.append(posToRender[i*3 + 2]);
|
||||
builder.append('\n');
|
||||
}
|
||||
builder.append('\n');
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import com.seibel.lod.util.*;
|
||||
|
||||
import javax.xml.crypto.Data;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SingleLevelContainer implements LevelContainer
|
||||
{
|
||||
public final byte detailLevel;
|
||||
public final int size;
|
||||
|
||||
public final long[][] dataContainer;
|
||||
|
||||
public SingleLevelContainer(byte detailLevel)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
dataContainer = new long[size][size];
|
||||
}
|
||||
|
||||
public void clear(int posX, int posZ)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
dataContainer[posX][posZ] = DataPointUtil.EMPTY_DATA;
|
||||
}
|
||||
|
||||
public boolean addData(long data, int posX, int posZ, int index)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
dataContainer[posX][posZ] = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean addSingleData(long newData, int posX, int posZ)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
dataContainer[posX][posZ] = newData;
|
||||
return true;
|
||||
}
|
||||
|
||||
public long getData(int posX, int posZ, int index)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
//Improve this using a thread map to long[]
|
||||
return dataContainer[posX][posZ];
|
||||
}
|
||||
|
||||
public long getSingleData(int posX, int posZ)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
//Improve this using a thread map to long[]
|
||||
return dataContainer[posX][posZ];
|
||||
}
|
||||
|
||||
public byte getDetailLevel()
|
||||
{
|
||||
return detailLevel;
|
||||
}
|
||||
|
||||
public LevelContainer expand()
|
||||
{
|
||||
return new SingleLevelContainer((byte) (getDetailLevel() - 1));
|
||||
}
|
||||
|
||||
public SingleLevelContainer(byte[] inputData)
|
||||
{
|
||||
int tempIndex;
|
||||
int index = 0;
|
||||
long newData;
|
||||
detailLevel = inputData[index];
|
||||
index++;
|
||||
size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
this.dataContainer = new long[size][size];
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
{
|
||||
newData = 0;
|
||||
if (inputData[index] == 0)
|
||||
index++;
|
||||
else if (inputData[index] == 3)
|
||||
{
|
||||
newData = 3;
|
||||
index++;
|
||||
} else if (index + 7 >= inputData.length)
|
||||
break;
|
||||
else
|
||||
{
|
||||
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
|
||||
index = index + 8;
|
||||
}
|
||||
dataContainer[x][z] = newData;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
|
||||
{
|
||||
//We reset the array
|
||||
long[] dataToMerge = ThreadMapUtil.getSingleUpdateArray();
|
||||
|
||||
int childPosX;
|
||||
int childPosZ;
|
||||
long data;
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
childPosX = 2 * posX + x;
|
||||
childPosZ = 2 * posZ + z;
|
||||
dataToMerge[2 * x + z] = lowerLevelContainer.getSingleData(childPosX, childPosZ);
|
||||
}
|
||||
}
|
||||
data = DataPointUtil.mergeSingleData(dataToMerge);
|
||||
addSingleData(data, posX, posZ);
|
||||
}
|
||||
|
||||
public int getMaxVerticalData()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean doesItExist(int posX, int posZ)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
//Improve this using a thread map to long[]
|
||||
return DataPointUtil.doesItExist(getSingleData(posX, posZ));
|
||||
}
|
||||
|
||||
public byte[] toDataString()
|
||||
{
|
||||
int index = 0;
|
||||
int tempIndex;
|
||||
byte[] tempData = ThreadMapUtil.getSaveContainer();
|
||||
if (tempData == null || tempData.length != (1 + (size * size * 8)))
|
||||
tempData = new byte[1 + (size * size * 8)];
|
||||
else
|
||||
Arrays.fill(tempData, (byte) 0);
|
||||
tempData[index] = detailLevel;
|
||||
index++;
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
{
|
||||
if (dataContainer[x][z] == 0)
|
||||
{
|
||||
tempData[index] = 0;
|
||||
index++;
|
||||
} else if (dataContainer[x][z] == 3)
|
||||
{
|
||||
tempData[index] = 3;
|
||||
index++;
|
||||
} else
|
||||
{
|
||||
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||
tempData[index + tempIndex] = (byte) (dataContainer[x][z] >>> (8 * tempIndex));
|
||||
index += 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Arrays.copyOfRange(tempData, 0, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(detailLevel);
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
|
||||
public int getMaxNumberOfLods()
|
||||
{
|
||||
return size * size * getMaxVerticalData();
|
||||
}
|
||||
|
||||
public int getMaxMemoryUse()
|
||||
{
|
||||
return getMaxNumberOfLods() * 2; //2 byte
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import com.seibel.lod.util.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class VerticalLevelContainer implements LevelContainer
|
||||
{
|
||||
|
||||
public final byte detailLevel;
|
||||
public final int size;
|
||||
public final int maxVerticalData;
|
||||
|
||||
public final long[] dataContainer;
|
||||
|
||||
public VerticalLevelContainer(byte detailLevel)
|
||||
{
|
||||
this.detailLevel = detailLevel;
|
||||
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
maxVerticalData = DetailDistanceUtil.getMaxVerticalData(detailLevel);
|
||||
dataContainer = new long[size * size * DetailDistanceUtil.getMaxVerticalData(detailLevel)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getDetailLevel()
|
||||
{
|
||||
return detailLevel;
|
||||
}
|
||||
|
||||
public void clear(int posX, int posZ){
|
||||
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
for(int verticalIndex = 0; verticalIndex < maxVerticalData; verticalIndex++){
|
||||
dataContainer[posX*size*maxVerticalData + posZ*maxVerticalData + verticalIndex] = DataPointUtil.EMPTY_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean addData(long data, int posX, int posZ, int verticalIndex){
|
||||
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
dataContainer[posX*size*maxVerticalData + posZ*maxVerticalData + verticalIndex] = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean addSingleData(long data, int posX, int posZ){
|
||||
return addData(data, posX, posZ, 0);
|
||||
}
|
||||
|
||||
public long getData(int posX, int posZ, int verticalIndex){
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
return dataContainer[posX*size*maxVerticalData + posZ*maxVerticalData + verticalIndex];
|
||||
}
|
||||
|
||||
public long getSingleData(int posX, int posZ){
|
||||
return getData(posX,posZ,0);
|
||||
}
|
||||
|
||||
public int getMaxVerticalData(){
|
||||
return maxVerticalData;
|
||||
}
|
||||
|
||||
public int getSize(){
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean doesItExist(int posX, int posZ){
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
return DataPointUtil.doesItExist(getSingleData(posX,posZ));
|
||||
}
|
||||
|
||||
public VerticalLevelContainer(byte[] inputData)
|
||||
{
|
||||
int tempIndex;
|
||||
int index = 0;
|
||||
int counter = -1;
|
||||
long newData;
|
||||
byte last = 0;
|
||||
detailLevel = inputData[index];
|
||||
index++;
|
||||
maxVerticalData = inputData[index];
|
||||
index++;
|
||||
size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
int x = size * size * maxVerticalData;
|
||||
this.dataContainer = new long[x];
|
||||
for ( int i = 0; i < x; i++)
|
||||
{
|
||||
newData = 0;
|
||||
if (counter > -1)
|
||||
{
|
||||
dataContainer[i] = last;
|
||||
if (last == 3)
|
||||
{ //skip rest of void chunk
|
||||
for (tempIndex = 1; tempIndex < maxVerticalData; tempIndex++) {
|
||||
dataContainer[i + tempIndex] = 0;
|
||||
}
|
||||
i += maxVerticalData - 1;
|
||||
}
|
||||
counter--;
|
||||
} else if ((inputData[index] & 0x3) == 0 || (inputData[index] & 0x3) == 3)
|
||||
{
|
||||
last = (byte)(inputData[index] & 0x3);
|
||||
//recover counter
|
||||
counter = (inputData[index] & 0x7c) >>> 2;
|
||||
tempIndex = 0;
|
||||
while ((inputData[index] & 0x80) == 0x80)
|
||||
{ //overflow bit is on
|
||||
index++;
|
||||
counter += (inputData[index] & 0x7f) << (5 + 7 * tempIndex);
|
||||
tempIndex++;
|
||||
}
|
||||
index++;
|
||||
//since loop expects from us to put some data in, we just make it rerun it with new counter;
|
||||
i--;
|
||||
} else if (index + 7 >= inputData.length)
|
||||
break;
|
||||
else {
|
||||
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
|
||||
index = index + 8;
|
||||
dataContainer[i] = newData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LevelContainer expand(){
|
||||
return new VerticalLevelContainer((byte) (getDetailLevel() - 1));
|
||||
}
|
||||
|
||||
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
|
||||
{
|
||||
//We reset the array
|
||||
long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray()[detailLevel];
|
||||
|
||||
if(dataToMerge == null || dataToMerge.length != 4*lowerLevelContainer.getMaxVerticalData())
|
||||
dataToMerge = new long[4 * lowerLevelContainer.getMaxVerticalData()];
|
||||
else
|
||||
Arrays.fill(dataToMerge, DataPointUtil.EMPTY_DATA);
|
||||
|
||||
int lowerMaxVertical = dataToMerge.length/4;
|
||||
int childPosX;
|
||||
int childPosZ;
|
||||
long[] data;
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
childPosX = 2 * posX + x;
|
||||
childPosZ = 2 * posZ + z;
|
||||
for(int verticalIndex = 0; verticalIndex < lowerMaxVertical; verticalIndex++)
|
||||
dataToMerge[(z*2+x)*lowerMaxVertical + verticalIndex] = lowerLevelContainer.getData(childPosX, childPosZ, verticalIndex);
|
||||
}
|
||||
}
|
||||
data = DataPointUtil.mergeMultiData(dataToMerge, lowerMaxVertical, getMaxVerticalData());
|
||||
|
||||
for(int verticalIndex = 0; (verticalIndex < data.length) && (verticalIndex < maxVerticalData); verticalIndex++)
|
||||
{
|
||||
addData(data[verticalIndex],
|
||||
posX,
|
||||
posZ,
|
||||
verticalIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toDataString()
|
||||
{
|
||||
int index = 0;
|
||||
int counter = -1;
|
||||
byte last = -1;
|
||||
int x = size * size * maxVerticalData;
|
||||
int tempIndex;
|
||||
byte[] tempData = ThreadMapUtil.getSaveContainer();
|
||||
long current;
|
||||
if(tempData == null || tempData.length != (2 + (x * 8)))
|
||||
tempData = new byte[2 + (x * 8)];
|
||||
else
|
||||
Arrays.fill(tempData, (byte) 0);
|
||||
|
||||
tempData[index] = detailLevel;
|
||||
index++;
|
||||
tempData[index] = (byte) maxVerticalData;
|
||||
index++;
|
||||
|
||||
for (int i = 0; i < x; i++)
|
||||
{
|
||||
current = dataContainer[i];
|
||||
if ((current & 0b11) == 0 || (current & 0b11) == 3)
|
||||
{
|
||||
current &= 0b11; //clean any garbage data after those two bits
|
||||
last = (byte) current;
|
||||
if (current == 3) //skip rest of void chunk
|
||||
i += maxVerticalData - 1;
|
||||
counter++;
|
||||
} else {
|
||||
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
|
||||
index += 8;
|
||||
}
|
||||
if (last != -1 && ( i == x - 1 || last != ((dataContainer[i + 1]) & 0b11)))
|
||||
{ //save compressed data if next is different or if we reached onf of the data
|
||||
tempData[index] = (byte)(0x7f & ((counter << 2) + last)); //save 5 bits of counter and compressed block
|
||||
|
||||
tempIndex = 0;
|
||||
while ((counter >>> (5 + 7 * tempIndex)) != 0) //there is more of that counter
|
||||
{
|
||||
tempData[index] = (byte)(tempData[index] | 0x80); //set overflow bit to true
|
||||
index++; // after setting overflow bit w can actually index++
|
||||
tempData[index] = (byte)(0x7f & (counter >>> (5 + 7 * tempIndex))); // save 7 bits of counter
|
||||
tempIndex++;
|
||||
}
|
||||
index++;
|
||||
last = -1;
|
||||
counter = -1;
|
||||
}
|
||||
}
|
||||
return Arrays.copyOfRange(tempData, 0, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
/*
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int size = (int) Math.pow(2, LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
stringBuilder.append(detailLevel);
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
for (int x = 0; x < size; x++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
{
|
||||
//Converting the dataToHex
|
||||
stringBuilder.append(Long.toHexString(dataContainer[x][z][0]));
|
||||
stringBuilder.append(DATA_DELIMITER);
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
*/
|
||||
return " ";
|
||||
}
|
||||
|
||||
public int getMaxNumberOfLods(){
|
||||
return size*size*getMaxVerticalData();
|
||||
}
|
||||
|
||||
public int getMaxMemoryUse(){
|
||||
return getMaxNumberOfLods() * 2; //2 byte
|
||||
}
|
||||
}
|
||||
@@ -21,27 +21,26 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodBuilder;
|
||||
import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker;
|
||||
import com.seibel.lod.builders.worldGeneration.LodWorldGenerator;
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DistanceCalculatorType;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.FogDrawOverride;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.enums.ShadingMode;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.LodWorld;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.render.LodRenderer;
|
||||
import com.seibel.lod.util.DataPointUtil;
|
||||
import com.seibel.lod.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import com.seibel.lod.util.ThreadMapUtil;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraftforge.client.event.InputEvent;
|
||||
import net.minecraftforge.event.TickEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
@@ -51,15 +50,18 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
/**
|
||||
* This handles all events sent to the client,
|
||||
* and is the starting point for most of this program.
|
||||
* and is the starting point for most of the mod.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 8-24-2021
|
||||
* @version 9-23-2021
|
||||
*/
|
||||
public class ClientProxy
|
||||
{
|
||||
public static final Logger LOGGER = LogManager.getLogger("LOD");
|
||||
|
||||
private boolean firstTimeSetupComplete = false;
|
||||
public static boolean drawLods = true;
|
||||
|
||||
private static LodWorld lodWorld = new LodWorld();
|
||||
private static LodBuilder lodBuilder = new LodBuilder();
|
||||
private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder();
|
||||
@@ -68,7 +70,7 @@ public class ClientProxy
|
||||
|
||||
private boolean configOverrideReminderPrinted = false;
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
private MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
|
||||
|
||||
|
||||
/**
|
||||
@@ -85,7 +87,7 @@ public class ClientProxy
|
||||
* to the LOD view distance
|
||||
*/
|
||||
private boolean recalculateWidths = false;
|
||||
|
||||
private DimensionType currentDimension = null;
|
||||
|
||||
public ClientProxy()
|
||||
{
|
||||
@@ -100,84 +102,112 @@ public class ClientProxy
|
||||
/**
|
||||
* Do any setup that is required to draw LODs
|
||||
* and then tell the LodRenderer to draw.
|
||||
* @param mcMatrixStack
|
||||
*/
|
||||
public void renderLods(float partialTicks)
|
||||
public void renderLods(MatrixStack mcMatrixStack, float partialTicks)
|
||||
{
|
||||
DetailDistanceUtil.updateSettings();
|
||||
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
viewDistanceChangedEvent();
|
||||
|
||||
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
|
||||
playerMoveEvent(lodDim);
|
||||
//System.out.println("memory needed " + lodDim.getMinMemoryNeeded() + " byte");
|
||||
//System.out.println(lodDim);
|
||||
|
||||
lodDim.treeCutter((int) mc.player.getX(), (int) mc.player.getZ());
|
||||
lodDim.treeGenerator((int) mc.player.getX(), (int) mc.player.getZ());
|
||||
|
||||
|
||||
// comment out when creating a release
|
||||
//applyConfigOverrides();
|
||||
applyConfigOverrides();
|
||||
|
||||
// clear any out of date objects
|
||||
mc.clearFrameObjectCache();
|
||||
|
||||
// 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 profiler 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"
|
||||
|
||||
|
||||
// these can't be set until after the buffers are built (in renderer.drawLODs)
|
||||
// otherwise the buffers may be set to the wrong size, or not changed at all
|
||||
previousChunkRenderDistance = mc.options.renderDistance;
|
||||
previousLodRenderDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get();
|
||||
try
|
||||
{
|
||||
// only run the first time setup once
|
||||
if (!firstTimeSetupComplete)
|
||||
{
|
||||
ThreadMapUtil.clearMaps();
|
||||
firstFrameSetup();
|
||||
}
|
||||
|
||||
if(mc.getCurrentDimension() != currentDimension)
|
||||
{
|
||||
currentDimension = mc.getCurrentDimension();
|
||||
reset();
|
||||
}
|
||||
|
||||
DetailDistanceUtil.updateSettings();
|
||||
if (mc == null || mc.getPlayer() == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
LodDimension lodDim = lodWorld.getLodDimension(mc.getCurrentDimension());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
viewDistanceChangedEvent();
|
||||
playerMoveEvent(lodDim);
|
||||
|
||||
lodDim.treeCutter((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
|
||||
lodDim.treeGenerator((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
|
||||
|
||||
|
||||
// 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 profiler to measure
|
||||
IProfiler profiler = mc.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
if(drawLods)
|
||||
{
|
||||
renderer.drawLODs(lodDim, mcMatrixStack, partialTicks, mc.getProfiler());
|
||||
}
|
||||
|
||||
profiler.pop(); // end LOD
|
||||
profiler.push("terrain"); // go back into "terrain"
|
||||
|
||||
|
||||
// these can't be set until after the buffers are built (in renderer.drawLODs)
|
||||
// otherwise the buffers may be set to the wrong size, or not changed at all
|
||||
previousChunkRenderDistance = mc.getRenderDistance();
|
||||
previousLodRenderDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("client proxy: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void applyConfigOverrides()
|
||||
{
|
||||
// remind the developer(s). that config override is active
|
||||
// remind the developer(s) that the config override is active
|
||||
if (!configOverrideReminderPrinted)
|
||||
{
|
||||
mc.player.sendMessage(new StringTextComponent("Debug settings enabled!"), mc.player.getUUID());
|
||||
//mc.getPlayer().sendMessage(new StringTextComponent("Debug settings enabled!"), mc.getPlayer().getUUID());
|
||||
|
||||
// this was changed just for the buggy pre-release, since the code was already here
|
||||
mc.getPlayer().sendMessage(new StringTextComponent("Warning: LOD mod 1.5 buggy pre-release"), mc.getPlayer().getUUID());
|
||||
mc.getPlayer().sendMessage(new StringTextComponent("Hic sunt dracones"), mc.getPlayer().getUUID()); // Here be dragons
|
||||
configOverrideReminderPrinted = true;
|
||||
}
|
||||
|
||||
//LodConfig.CLIENT.drawLODs.set(true);
|
||||
//LodConfig.CLIENT.debugMode.set(true);
|
||||
|
||||
LodConfig.CLIENT.graphics.maxDrawDetail.set(LodDetail.FULL);
|
||||
LodConfig.CLIENT.worldGenerator.maxGenerationDetail.set(LodDetail.FULL);
|
||||
// LodConfig.CLIENT.graphics.drawResolution.set(HorizontalResolution.BLOCK);
|
||||
// LodConfig.CLIENT.worldGenerator.generationResolution.set(HorizontalResolution.BLOCK);
|
||||
// requires a world restart?
|
||||
// LodConfig.CLIENT.worldGenerator.lodQualityMode.set(VerticalQuality.MULTI_LOD);
|
||||
|
||||
LodConfig.CLIENT.graphics.fogDistance.set(FogDistance.FAR);
|
||||
LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
|
||||
LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES);
|
||||
LodConfig.CLIENT.graphics.brightnessMultiplier.set(1.0);
|
||||
LodConfig.CLIENT.graphics.saturationMultiplier.set(1.0);
|
||||
// LodConfig.CLIENT.graphics.fogDistance.set(FogDistance.FAR);
|
||||
// LodConfig.CLIENT.graphics.fogDrawOverride.set(FogDrawOverride.ALWAYS_DRAW_FOG_FANCY);
|
||||
// LodConfig.CLIENT.graphics.shadingMode.set(ShadingMode.DARKEN_SIDES);
|
||||
// LodConfig.CLIENT.graphics.brightnessMultiplier.set(1.0);
|
||||
// LodConfig.CLIENT.graphics.saturationMultiplier.set(1.0);
|
||||
|
||||
// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
|
||||
// LodConfig.CLIENT.graphics.lodChunkRenderDistance.set(64);
|
||||
// LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
|
||||
// LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false);
|
||||
|
||||
// LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.set(2000); // 2000
|
||||
// LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.set(1000); // 1000
|
||||
// LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.set(5000); // 5000
|
||||
|
||||
LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
|
||||
LodConfig.CLIENT.graphics.lodChunkRenderDistance.set(256);
|
||||
LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
|
||||
LodConfig.CLIENT.graphics.lodQuality.set(3);
|
||||
LodConfig.CLIENT.worldGenerator.allowUnstableFeatureGeneration.set(false);
|
||||
|
||||
LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.set(2000); // 2000
|
||||
LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.set(1000); // 1000
|
||||
LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.set(50); // 5000
|
||||
|
||||
LodConfig.CLIENT.debugging.enableDebugKeybinding.set(true);
|
||||
// LodConfig.CLIENT.debugging.enableDebugKeybindings.set(true);
|
||||
// LodConfig.CLIENT.debugging.debugMode.set(DebugMode.SHOW_DETAIL);
|
||||
}
|
||||
|
||||
|
||||
@@ -188,10 +218,10 @@ public class ClientProxy
|
||||
@SubscribeEvent
|
||||
public void serverTickEvent(TickEvent.ServerTickEvent event)
|
||||
{
|
||||
if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded())
|
||||
if (mc == null || mc.getPlayer() == null || !lodWorld.getIsWorldLoaded())
|
||||
return;
|
||||
|
||||
LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType());
|
||||
LodDimension lodDim = lodWorld.getLodDimension(mc.getPlayer().level.dimensionType());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
@@ -214,6 +244,7 @@ public class ClientProxy
|
||||
@SubscribeEvent
|
||||
public void worldLoadEvent(WorldEvent.Load event)
|
||||
{
|
||||
DataPointUtil.worldHeight = event.getWorld().getHeight();
|
||||
// the player just loaded a new world/dimension
|
||||
lodWorld.selectWorld(LodUtil.getWorldID(event.getWorld()));
|
||||
// make sure the correct LODs are being rendered
|
||||
@@ -225,7 +256,6 @@ public class ClientProxy
|
||||
public void worldUnloadEvent(WorldEvent.Unload event)
|
||||
{
|
||||
// the player just unloaded a world/dimension
|
||||
|
||||
if (mc.getConnection().getLevel() == null)
|
||||
{
|
||||
// if this isn't done unfinished tasks may be left in the queue
|
||||
@@ -241,6 +271,12 @@ public class ClientProxy
|
||||
// breaking when changing worlds.
|
||||
renderer.destroyBuffers();
|
||||
recalculateWidths = true;
|
||||
|
||||
|
||||
// make sure the nulled objects are freed.
|
||||
// (this prevents a out of memory error when
|
||||
// changing worlds)
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,18 +297,22 @@ public class ClientProxy
|
||||
@SubscribeEvent
|
||||
public void onKeyInput(InputEvent.KeyInputEvent event)
|
||||
{
|
||||
if(LodConfig.CLIENT.debugging.enableDebugKeybinding.get()
|
||||
&& event.getKey() == GLFW.GLFW_KEY_F4 && event.getAction() == GLFW.GLFW_PRESS)
|
||||
if(LodConfig.CLIENT.debugging.enableDebugKeybindings.get()
|
||||
&& event.getKey() == GLFW.GLFW_KEY_F4 && event.getAction() == GLFW.GLFW_PRESS)
|
||||
{
|
||||
LodConfig.CLIENT.debugging.debugMode.set(LodConfig.CLIENT.debugging.debugMode.get().getNext());
|
||||
}
|
||||
|
||||
if(LodConfig.CLIENT.debugging.enableDebugKeybindings.get()
|
||||
&& event.getKey() == GLFW.GLFW_KEY_F6 && event.getAction() == GLFW.GLFW_PRESS)
|
||||
{
|
||||
drawLods = !drawLods;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// frame LOD events //
|
||||
//==================//
|
||||
//============//
|
||||
// LOD events //
|
||||
//============//
|
||||
|
||||
/**
|
||||
* Re-centers the given LodDimension if it needs to be.
|
||||
@@ -280,7 +320,7 @@ public class ClientProxy
|
||||
private void playerMoveEvent(LodDimension lodDim)
|
||||
{
|
||||
// make sure the dimension is centered
|
||||
RegionPos playerRegionPos = new RegionPos(mc.player.blockPosition());
|
||||
RegionPos playerRegionPos = new RegionPos(mc.getPlayer().blockPosition());
|
||||
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterX(), playerRegionPos.z - lodDim.getCenterZ());
|
||||
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
|
||||
{
|
||||
@@ -309,7 +349,7 @@ public class ClientProxy
|
||||
// update the dimensions to fit the new width
|
||||
lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
lodBuilder.defaultDimensionWidthInRegions = newWidth;
|
||||
renderer.setupBuffers(newWidth);
|
||||
renderer.setupBuffers(lodWorld.getLodDimension(mc.getClientWorld().dimensionType()));
|
||||
|
||||
recalculateWidths = false;
|
||||
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
|
||||
@@ -318,6 +358,26 @@ public class ClientProxy
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This event is called once during the first frame Minecraft renders in the world.
|
||||
*/
|
||||
public void firstFrameSetup()
|
||||
{
|
||||
// make sure the GlProxy is created before the LodBufferBuilder
|
||||
GlProxy.getInstance();
|
||||
|
||||
|
||||
|
||||
firstTimeSetupComplete = true;
|
||||
}
|
||||
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
renderer = new LodRenderer(lodBufferBuilder);
|
||||
LodNodeGenWorker.resetGenerator();
|
||||
ThreadMapUtil.clearMaps();
|
||||
}
|
||||
|
||||
//================//
|
||||
// public getters //
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
package com.seibel.lod.proxy;
|
||||
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GLCapabilities;
|
||||
import org.lwjgl.opengl.WGL;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
/**
|
||||
* A singleton that holds references to different openGL contexts
|
||||
* and GPU capabilities.
|
||||
*
|
||||
* <p>
|
||||
* Helpful OpenGL resources: <br><br>
|
||||
*
|
||||
* https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-AsynchronousBufferTransfers.pdf <br>
|
||||
* https://learnopengl.com/Advanced-OpenGL/Advanced-Data <br>
|
||||
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br><br>
|
||||
*
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 9-15-2021
|
||||
*/
|
||||
public class GlProxy
|
||||
{
|
||||
private static GlProxy instance = null;
|
||||
|
||||
public final long deviceContext;
|
||||
|
||||
public long minecraftGlContext;
|
||||
public GLCapabilities minecraftGlCapabilities;
|
||||
|
||||
public long lodBuilderGlContext;
|
||||
public GLCapabilities lodBuilderGlCapabilities;
|
||||
/** This is just used for debugging, hopefuly it can be removed once
|
||||
* the context switching is more stable. */
|
||||
public Thread lodBuilderOwnerThread = null;
|
||||
|
||||
/**
|
||||
* Does this computer's GPU support fancy fog?
|
||||
*/
|
||||
public final boolean fancyFogAvailable;
|
||||
|
||||
|
||||
|
||||
private GlProxy()
|
||||
{
|
||||
// getting Minecraft's context has to be done on the render thread,
|
||||
// where the GL context is
|
||||
if (!RenderSystem.isOnRenderThread())
|
||||
throw new IllegalStateException(GlProxy.class.getSimpleName() + " was created outside the render thread!");
|
||||
|
||||
|
||||
|
||||
|
||||
//============================//
|
||||
// create the builder context //
|
||||
//============================//
|
||||
|
||||
minecraftGlContext = WGL.wglGetCurrentContext();
|
||||
minecraftGlCapabilities = GL.getCapabilities();
|
||||
deviceContext = WGL.wglGetCurrentDC();
|
||||
|
||||
lodBuilderGlContext = WGL.wglCreateContext(deviceContext);
|
||||
if (!WGL.wglShareLists(minecraftGlContext, lodBuilderGlContext))
|
||||
throw new IllegalStateException("Unable to share lists between Minecraft and builder contexts.");
|
||||
if (!WGL.wglMakeCurrent(deviceContext, lodBuilderGlContext))
|
||||
throw new IllegalStateException("Unable to change OpenGL contexts! tried to change to [" + GlProxyContext.LOD_BUILDER.toString() + "] from [" + GlProxyContext.MINECRAFT.toString() + "]");
|
||||
lodBuilderGlCapabilities = GL.createCapabilities();
|
||||
WGL.wglMakeCurrent(deviceContext, 0L);
|
||||
|
||||
|
||||
// Since this is called on the render thread, make sure the Minecraft context is used in the end
|
||||
WGL.wglMakeCurrent(deviceContext, minecraftGlContext);
|
||||
|
||||
|
||||
|
||||
|
||||
//==================================//
|
||||
// get any GPU related capabilities //
|
||||
//==================================//
|
||||
|
||||
// see if this GPU can run fancy fog
|
||||
fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
|
||||
|
||||
if (!fancyFogAvailable)
|
||||
{
|
||||
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simple wrapper function to make switching contexts easier
|
||||
*
|
||||
* @throws IllegalStateException if unable to change to newContext. <br>
|
||||
* This expection should never be thrown if
|
||||
* switching to GlProxyContext.NONE
|
||||
*/
|
||||
public void setGlContext(GlProxyContext newContext)
|
||||
{
|
||||
GlProxyContext currentContext = getGlContext();
|
||||
|
||||
// we don't have to change the context, we're already there.
|
||||
if (currentContext == newContext)
|
||||
return;
|
||||
|
||||
|
||||
long contextPointer = 0L;
|
||||
GLCapabilities newGlCapabilities = null;
|
||||
switch(newContext)
|
||||
{
|
||||
case LOD_BUILDER:
|
||||
contextPointer = lodBuilderGlContext;
|
||||
newGlCapabilities = lodBuilderGlCapabilities;
|
||||
break;
|
||||
case MINECRAFT:
|
||||
contextPointer = minecraftGlContext;
|
||||
newGlCapabilities = minecraftGlCapabilities;
|
||||
break;
|
||||
case NONE:
|
||||
contextPointer = 0L; // equivalent to null
|
||||
newGlCapabilities = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
// should never happen, here to make the compiler happy
|
||||
}
|
||||
|
||||
if (!WGL.wglMakeCurrent(deviceContext, contextPointer))
|
||||
throw new IllegalStateException("Unable to change OpenGL contexts! tried to change to [" + newContext.toString() + "] from [" + currentContext.toString() + "] on thread: [" + Thread.currentThread().getName() + "] lod builder owner thread: " + (lodBuilderOwnerThread != null ? lodBuilderOwnerThread.getName() : "null"));
|
||||
|
||||
if (newContext == GlProxyContext.LOD_BUILDER)
|
||||
{
|
||||
lodBuilderOwnerThread = Thread.currentThread();
|
||||
}
|
||||
else if (newContext == GlProxyContext.NONE && currentContext == GlProxyContext.LOD_BUILDER)
|
||||
{
|
||||
lodBuilderOwnerThread = null;
|
||||
}
|
||||
|
||||
GL.setCapabilities(newGlCapabilities);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this thread's OpenGL context.
|
||||
*/
|
||||
public GlProxyContext getGlContext()
|
||||
{
|
||||
long currentContext = WGL.wglGetCurrentContext();
|
||||
if(currentContext == lodBuilderGlContext)
|
||||
{
|
||||
return GlProxyContext.LOD_BUILDER;
|
||||
}
|
||||
else if(currentContext == minecraftGlContext)
|
||||
{
|
||||
return GlProxyContext.MINECRAFT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GlProxyContext.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/** Minecraft, Alpha, Beta, None */
|
||||
public enum GlProxyContext
|
||||
{
|
||||
MINECRAFT,
|
||||
LOD_BUILDER,
|
||||
|
||||
/** used to un-bind threads */
|
||||
NONE,
|
||||
}
|
||||
|
||||
public static GlProxy getInstance()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new GlProxy();
|
||||
}catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
@@ -17,22 +17,21 @@
|
||||
*/
|
||||
package com.seibel.lod.render;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL15C;
|
||||
import org.lwjgl.opengl.NVFogDistance;
|
||||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.seibel.lod.builders.LodBufferBuilder;
|
||||
import com.seibel.lod.builders.LodBufferBuilder.VertexBuffersAndOffset;
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DebugMode;
|
||||
import com.seibel.lod.enums.DetailDropOff;
|
||||
import com.seibel.lod.enums.FogDistance;
|
||||
import com.seibel.lod.enums.FogDrawOverride;
|
||||
import com.seibel.lod.enums.FogQuality;
|
||||
@@ -40,26 +39,25 @@ import com.seibel.lod.handlers.ReflectionHandler;
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.NearFarFogSettings;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.proxy.ClientProxy;
|
||||
import com.seibel.lod.proxy.GlProxy;
|
||||
import com.seibel.lod.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.util.LevelPosUtil;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
||||
import net.minecraft.client.renderer.vertex.VertexBuffer;
|
||||
import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.potion.EffectInstance;
|
||||
import net.minecraft.potion.Effects;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
@@ -67,7 +65,7 @@ import net.minecraft.util.math.vector.Vector3f;
|
||||
* This is where LODs are draw to the world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-31-2021
|
||||
* @version 9-23-2021
|
||||
*/
|
||||
public class LodRenderer
|
||||
{
|
||||
@@ -89,24 +87,17 @@ public class LodRenderer
|
||||
* https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx
|
||||
*/
|
||||
public static final int MAX_ALOCATEABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
|
||||
|
||||
/**
|
||||
* Does this computer's GPU support fancy fog?
|
||||
*/
|
||||
private static Boolean fancyFogAvailable = null;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* If true the LODs colors will be replaced with
|
||||
* a checkerboard, this can be used for debugging.
|
||||
*/
|
||||
public DebugMode previousDebugMode = DebugMode.OFF;
|
||||
|
||||
private Minecraft mc;
|
||||
private MinecraftWrapper mc;
|
||||
private GameRenderer gameRender;
|
||||
private IProfiler profiler;
|
||||
private int farPlaneBlockDistance;
|
||||
private ReflectionHandler reflectionHandler;
|
||||
|
||||
|
||||
/**
|
||||
@@ -119,12 +110,18 @@ public class LodRenderer
|
||||
*/
|
||||
private VertexBuffer[][] vbos;
|
||||
public static final VertexFormat LOD_VERTEX_FORMAT = DefaultVertexFormats.POSITION_COLOR;
|
||||
|
||||
private ChunkPos vbosCenter = new ChunkPos(0,0);
|
||||
|
||||
/**
|
||||
* This is used to determine if the LODs should be regenerated
|
||||
*/
|
||||
private LevelPos previousPos = new LevelPos((byte) 0, 0, 0);
|
||||
private int[] previousPos = new int[]{0,0,0};
|
||||
|
||||
public NativeImage lightMap = null;
|
||||
|
||||
// these variables are used to determine if the buffers should be rebuilt
|
||||
private long prevDayTime = 0;
|
||||
private double prevBrightness = 0;
|
||||
private int prevRenderDistance = 0;
|
||||
private long prevPlayerPosTime = 0;
|
||||
private long prevVanillaChunkTime = 0;
|
||||
@@ -153,10 +150,9 @@ public class LodRenderer
|
||||
|
||||
public LodRenderer(LodBufferBuilder newLodNodeBufferBuilder)
|
||||
{
|
||||
mc = Minecraft.getInstance();
|
||||
gameRender = mc.gameRenderer;
|
||||
mc = MinecraftWrapper.INSTANCE;
|
||||
gameRender = mc.getGameRenderer();
|
||||
|
||||
reflectionHandler = new ReflectionHandler();
|
||||
lodBufferBuilder = newLodNodeBufferBuilder;
|
||||
}
|
||||
|
||||
@@ -166,9 +162,10 @@ public class LodRenderer
|
||||
* the async process of generating the Buffers that hold those LODs.
|
||||
*
|
||||
* @param lodDim The dimension to draw, if null doesn't replace the current dimension.
|
||||
* @param mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method
|
||||
* @param partialTicks how far into the current tick this method was called.
|
||||
*/
|
||||
public void drawLODs(LodDimension lodDim, float partialTicks, IProfiler newProfiler)
|
||||
public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, float partialTicks, IProfiler newProfiler)
|
||||
{
|
||||
if (lodDim == null)
|
||||
{
|
||||
@@ -184,20 +181,7 @@ public class LodRenderer
|
||||
|
||||
profiler = newProfiler;
|
||||
profiler.push("LOD setup");
|
||||
|
||||
|
||||
// only check the GPU capability's once
|
||||
if (fancyFogAvailable == null)
|
||||
{
|
||||
// see if this GPU can run fancy fog
|
||||
fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
|
||||
|
||||
if (!fancyFogAvailable)
|
||||
{
|
||||
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that fancy fog options will not be available.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
|
||||
// starting here...
|
||||
@@ -215,7 +199,7 @@ public class LodRenderer
|
||||
if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvaliable())
|
||||
{
|
||||
// generate the LODs on a separate thread to prevent stuttering or freezing
|
||||
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.player.blockPosition(), true);
|
||||
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayer().blockPosition(), true);
|
||||
|
||||
// the regen process has been started,
|
||||
// it will be done when lodBufferBuilder.newBuffersAvaliable()
|
||||
@@ -223,31 +207,28 @@ public class LodRenderer
|
||||
fullRegen = false;
|
||||
partialRegen = false;
|
||||
}
|
||||
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
|
||||
// ...ending here
|
||||
|
||||
// replace the buffers used to draw and build,
|
||||
// this is only done when the createLodBufferGenerationThread
|
||||
// has finished executing on a parallel thread.
|
||||
|
||||
if (lodBufferBuilder.newBuffersAvaliable())
|
||||
{
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===========================//
|
||||
// GL settings for rendering //
|
||||
//===========================//
|
||||
|
||||
// set the required open GL settings
|
||||
|
||||
|
||||
if (LodConfig.CLIENT.debugging.debugMode.get() == DebugMode.SHOW_DETAIL_WIREFRAME)
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
|
||||
else
|
||||
GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
|
||||
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
GL11.glEnable(GL11.GL_CULL_FACE);
|
||||
GL11.glEnable(GL11.GL_COLOR_MATERIAL);
|
||||
@@ -259,19 +240,24 @@ public class LodRenderer
|
||||
|
||||
// get the default projection matrix so we can
|
||||
// reset it after drawing the LODs
|
||||
float[] defaultProjMatrix = new float[16];
|
||||
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, defaultProjMatrix);
|
||||
|
||||
Matrix4f modelViewMatrix = generateModelViewMatrix(partialTicks);
|
||||
float[] mcProjMatrixRaw = new float[16];
|
||||
GL11.glGetFloatv(GL11.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
|
||||
Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw);
|
||||
// OpenGl outputs their matricies in col,row form instead of row,col
|
||||
// (or maybe vice versa I have no idea :P)
|
||||
mcProjectionMatrix.transpose();
|
||||
|
||||
Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcMatrixStack, partialTicks);
|
||||
|
||||
// required for setupFog and setupProjectionMatrix
|
||||
farPlaneBlockDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
|
||||
|
||||
setupProjectionMatrix(partialTicks);
|
||||
setupLighting(lodDim, partialTicks);
|
||||
|
||||
setupProjectionMatrix(mcProjectionMatrix, partialTicks);
|
||||
// commented out until we can add shaders to handle lighting
|
||||
//setupLighting(lodDim, partialTicks);
|
||||
|
||||
NearFarFogSettings fogSettings = determineFogSettings();
|
||||
|
||||
|
||||
// determine the current fog settings so they can be
|
||||
// reset after drawing the LODs
|
||||
float defaultFogStartDist = GL11.glGetFloat(GL11.GL_FOG_START);
|
||||
@@ -283,14 +269,16 @@ public class LodRenderer
|
||||
//===========//
|
||||
// rendering //
|
||||
//===========//
|
||||
|
||||
profiler.popPush("LOD draw");
|
||||
|
||||
|
||||
if (vbos != null)
|
||||
{
|
||||
Vector3d cameraDir = mc.cameraEntity.getLookAngle().normalize();
|
||||
cameraDir = mc.options.getCameraType().isMirrored() ? cameraDir.reverse() : cameraDir;
|
||||
|
||||
|
||||
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
|
||||
Vector3d cameraDir = new Vector3d(renderInfo.getLookVector());
|
||||
|
||||
boolean cullingDisabled = LodConfig.CLIENT.graphics.disableDirectionalCulling.get();
|
||||
|
||||
// used to determine what type of fog to render
|
||||
int halfWidth = vbos.length / 2;
|
||||
int quarterWidth = vbos.length / 4;
|
||||
@@ -300,7 +288,7 @@ public class LodRenderer
|
||||
for (int j = 0; j < vbos.length; j++)
|
||||
{
|
||||
RegionPos vboPos = new RegionPos(i + lodDim.getCenterX() - lodDim.getWidth() / 2, j + lodDim.getCenterZ() - lodDim.getWidth() / 2);
|
||||
if (RenderUtil.isRegionInViewFrustum(mc.cameraEntity.blockPosition(), cameraDir, vboPos.blockPos()))
|
||||
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(renderInfo.getBlockPosition(), cameraDir, vboPos.blockPos()))
|
||||
{
|
||||
if ((i > halfWidth - quarterWidth && i < halfWidth + quarterWidth) && (j > halfWidth - quarterWidth && j < halfWidth + quarterWidth))
|
||||
setupFog(fogSettings.near.distance, fogSettings.near.quality);
|
||||
@@ -313,7 +301,7 @@ public class LodRenderer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// cleanup //
|
||||
@@ -335,20 +323,18 @@ public class LodRenderer
|
||||
|
||||
// reset the projection matrix so anything drawn after
|
||||
// the LODs will use the correct projection matrix
|
||||
Matrix4f mvm = new Matrix4f(defaultProjMatrix);
|
||||
mvm.transpose();
|
||||
gameRender.resetProjectionMatrix(mvm);
|
||||
|
||||
gameRender.resetProjectionMatrix(mcProjectionMatrix);
|
||||
|
||||
// clear the depth buffer so anything drawn is drawn
|
||||
// over the LODs
|
||||
GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
|
||||
// end of internal LOD profiling
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This is where the actual drawing happens.
|
||||
*/
|
||||
@@ -356,14 +342,14 @@ public class LodRenderer
|
||||
{
|
||||
if (vbo == null)
|
||||
return;
|
||||
|
||||
vbo.bind();
|
||||
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
|
||||
// 0L is the starting pointer
|
||||
LOD_VERTEX_FORMAT.setupBufferState(0L);
|
||||
|
||||
|
||||
vbo.draw(modelViewMatrix, GL11.GL_QUADS);
|
||||
|
||||
VertexBuffer.unbind();
|
||||
|
||||
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
LOD_VERTEX_FORMAT.clearBufferState();
|
||||
}
|
||||
|
||||
@@ -381,12 +367,12 @@ public class LodRenderer
|
||||
RenderSystem.disableFog();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (fogDistance == FogDistance.NEAR_AND_FAR)
|
||||
{
|
||||
throw new IllegalArgumentException("setupFog doesn't accept the NEAR_AND_FAR fog distance.");
|
||||
}
|
||||
|
||||
|
||||
// determine the fog distance mode to use
|
||||
int glFogDistanceMode;
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
@@ -399,7 +385,7 @@ public class LodRenderer
|
||||
// fast fog (frustum distance based fog)
|
||||
glFogDistanceMode = NVFogDistance.GL_EYE_PLANE_ABSOLUTE_NV;
|
||||
}
|
||||
|
||||
|
||||
// the multipliers are percentages
|
||||
// of the regular view distance.
|
||||
if (fogDistance == FogDistance.FAR)
|
||||
@@ -408,7 +394,7 @@ public class LodRenderer
|
||||
// is because we are using fog backwards to how
|
||||
// it is normally used, with it hiding near objects
|
||||
// instead of far objects.
|
||||
|
||||
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
{
|
||||
// for more realistic fog when using FAR
|
||||
@@ -434,16 +420,16 @@ public class LodRenderer
|
||||
{
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
{
|
||||
RenderSystem.fogEnd(mc.options.renderDistance * 16 * 1.41f);
|
||||
RenderSystem.fogStart(mc.options.renderDistance * 16 * 1.6f);
|
||||
RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.41f);
|
||||
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.6f);
|
||||
}
|
||||
else if (fogQuality == FogQuality.FAST)
|
||||
{
|
||||
RenderSystem.fogEnd(mc.options.renderDistance * 16 * 1.0f);
|
||||
RenderSystem.fogStart(mc.options.renderDistance * 16 * 1.5f);
|
||||
RenderSystem.fogEnd(mc.getRenderDistance() * 16 * 1.0f);
|
||||
RenderSystem.fogStart(mc.getRenderDistance() * 16 * 1.5f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GL11.glEnable(GL11.GL_FOG);
|
||||
RenderSystem.enableFog();
|
||||
RenderSystem.setupNvFogDistance();
|
||||
@@ -466,8 +452,8 @@ public class LodRenderer
|
||||
// disable fog if Minecraft wasn't rendering fog
|
||||
// but we were
|
||||
if (!fogSettings.vanillaIsRenderingFog &&
|
||||
(fogSettings.near.quality != FogQuality.OFF ||
|
||||
fogSettings.far.quality != FogQuality.OFF))
|
||||
(fogSettings.near.quality != FogQuality.OFF ||
|
||||
fogSettings.far.quality != FogQuality.OFF))
|
||||
{
|
||||
GL11.glDisable(GL11.GL_FOG);
|
||||
}
|
||||
@@ -475,99 +461,93 @@ public class LodRenderer
|
||||
|
||||
|
||||
/**
|
||||
* Create the model view matrix to move the LODs
|
||||
* from object space into world space.
|
||||
* Translate the camera relative to the LodDimension's center,
|
||||
* this is done since all LOD buffers are created in world space
|
||||
* instead of object space.
|
||||
* (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
|
||||
* accuracy vs the model view matrix, which only uses floats)
|
||||
*/
|
||||
private Matrix4f generateModelViewMatrix(float partialTicks)
|
||||
private Matrix4f offsetTheModelViewMatrix(MatrixStack mcMatrixStack, float partialTicks)
|
||||
{
|
||||
// duplicate the last matrix
|
||||
mcMatrixStack.pushPose();
|
||||
|
||||
|
||||
// get all relevant camera info
|
||||
ActiveRenderInfo renderInfo = mc.gameRenderer.getMainCamera();
|
||||
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
|
||||
Vector3d projectedView = renderInfo.getPosition();
|
||||
|
||||
|
||||
// generate the model view matrix
|
||||
MatrixStack matrixStack = new MatrixStack();
|
||||
matrixStack.pushPose();
|
||||
// translate and rotate to the current camera location
|
||||
matrixStack.mulPose(Vector3f.XP.rotationDegrees(renderInfo.getXRot()));
|
||||
matrixStack.mulPose(Vector3f.YP.rotationDegrees(renderInfo.getYRot() + 180));
|
||||
matrixStack.translate(-projectedView.x, -projectedView.y, -projectedView.z);
|
||||
|
||||
return matrixStack.last().pose();
|
||||
|
||||
// translate the camera relative to the regions' center
|
||||
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
|
||||
// accuracy vs the model view matrix, which only uses floats)
|
||||
BlockPos bufferPos = vbosCenter.getWorldPosition();
|
||||
Vector3d eyePos = mc.getPlayer().getEyePosition(partialTicks);
|
||||
double xDiff = eyePos.x - bufferPos.getX();
|
||||
double zDiff = eyePos.z - bufferPos.getZ();
|
||||
mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff);
|
||||
|
||||
|
||||
// get the modified model view matrix
|
||||
Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose();
|
||||
// remove the lod ModelViewMatrix
|
||||
mcMatrixStack.popPose();
|
||||
|
||||
return lodModelViewMatrix;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* create a new projection matrix and send it over to the GPU
|
||||
* <br><br>
|
||||
* A lot of this code is copied from renderLevel (line 567)
|
||||
* in the GameRender class. The code copied is anything with
|
||||
* a matrixStack and is responsible for making sure the LOD
|
||||
* objects distort correctly relative to the rest of the world.
|
||||
* Distortions are caused by: standing in a nether portal,
|
||||
* nausea potion effect, walking bobbing.
|
||||
*
|
||||
*
|
||||
* @param currentProjectionMatrix this is Minecraft's current projection matrix
|
||||
* @param partialTicks how many ticks into the frame we are
|
||||
*/
|
||||
private void setupProjectionMatrix(float partialTicks)
|
||||
private void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float partialTicks)
|
||||
{
|
||||
// Note: if the LOD objects don't distort correctly
|
||||
// compared to regular minecraft terrain, make sure
|
||||
// all the transformations in renderWorld are here too
|
||||
|
||||
MatrixStack matrixStack = new MatrixStack();
|
||||
matrixStack.pushPose();
|
||||
|
||||
gameRender.bobHurt(matrixStack, partialTicks);
|
||||
if (this.mc.options.bobView)
|
||||
{
|
||||
gameRender.bobView(matrixStack, partialTicks);
|
||||
}
|
||||
|
||||
// potion and nausea effects
|
||||
float f = MathHelper.lerp(partialTicks, this.mc.player.oPortalTime, this.mc.player.portalTime) * this.mc.options.screenEffectScale * this.mc.options.screenEffectScale;
|
||||
if (f > 0.0F)
|
||||
{
|
||||
int i = this.mc.player.hasEffect(Effects.CONFUSION) ? 7 : 20;
|
||||
float f1 = 5.0F / (f * f + 5.0F) - f * 0.04F;
|
||||
f1 = f1 * f1;
|
||||
Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_OF_TWO / 2.0F, MathHelper.SQRT_OF_TWO / 2.0F);
|
||||
matrixStack.mulPose(vector3f.rotationDegrees((gameRender.tick + partialTicks) * i));
|
||||
matrixStack.scale(1.0F / f1, 1.0F, 1.0F);
|
||||
float f2 = -(gameRender.tick + partialTicks) * i;
|
||||
matrixStack.mulPose(vector3f.rotationDegrees(f2));
|
||||
}
|
||||
|
||||
|
||||
// this projection matrix allows us to see past the normal
|
||||
// world render distance
|
||||
Matrix4f projectionMatrix =
|
||||
Matrix4f.perspective(
|
||||
getFov(partialTicks, true),
|
||||
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
|
||||
// it is possible to see the near clip plane, but
|
||||
// you have to be flying quickly in spectator mode through ungenerated
|
||||
// terrain, so I don't think it is much of an issue.
|
||||
mc.options.renderDistance,
|
||||
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2);
|
||||
|
||||
// add the screen space distortions
|
||||
projectionMatrix.multiply(matrixStack.last().pose());
|
||||
gameRender.resetProjectionMatrix(projectionMatrix);
|
||||
return;
|
||||
// create the new projection matrix
|
||||
Matrix4f lodPoj =
|
||||
Matrix4f.perspective(
|
||||
getFov(partialTicks, true),
|
||||
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
|
||||
mc.getRenderDistance()/2,
|
||||
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH * 2 / 4);
|
||||
|
||||
// get Minecraft's un-edited projection matrix
|
||||
// (this is before it is zoomed, distorted, etc.)
|
||||
Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true);
|
||||
// true here means use "use fov setting" (probably)
|
||||
|
||||
|
||||
// this logic strips away the defaultMcProj matrix so we
|
||||
// can get the distortionMatrix, which represents all
|
||||
// transformations, zooming, distortions, etc. done
|
||||
// to Minecraft's Projection matrix
|
||||
Matrix4f defaultMcProjInv = defaultMcProj.copy();
|
||||
defaultMcProjInv.invert();
|
||||
|
||||
Matrix4f distortionMatrix = defaultMcProjInv.copy();
|
||||
distortionMatrix.multiply(currentProjectionMatrix);
|
||||
|
||||
|
||||
// edit the lod projection to match Minecraft's
|
||||
// (so the LODs line up with the real world)
|
||||
lodPoj.multiply(distortionMatrix);
|
||||
|
||||
// send the projection over to the GPU
|
||||
gameRender.resetProjectionMatrix(lodPoj);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* setup the lighting to be used for the LODs
|
||||
*/
|
||||
private void setupLighting(LodDimension lodDimension, float partialTicks)
|
||||
/*private void setupLighting(LodDimension lodDimension, float partialTicks)
|
||||
{
|
||||
// Determine if the player has night vision
|
||||
boolean playerHasNightVision = false;
|
||||
if (this.mc.player != null)
|
||||
if (this.mc.getPlayer() != null)
|
||||
{
|
||||
Iterator<EffectInstance> iterator = this.mc.player.getActiveEffects().iterator();
|
||||
Iterator<EffectInstance> iterator = this.mc.getPlayer().getActiveEffects().iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
EffectInstance instance = iterator.next();
|
||||
@@ -579,13 +559,15 @@ public class LodRenderer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.level.getSkyDarken(partialTicks) : 0.2f;
|
||||
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f;
|
||||
sunBrightness = playerHasNightVision ? 1.0f : sunBrightness;
|
||||
float gammaMultiplyer = (float) mc.options.gamma - 0.5f;
|
||||
float lightStrength = ((sunBrightness / 2f) - 0.2f) + (gammaMultiplyer * 0.3f);
|
||||
float gamma = (float) mc.getOptions().gamma - 0.0f;
|
||||
float dayEffect = (sunBrightness - 0.2f) * 1.25f;
|
||||
float lightStrength = (gamma * 0.34f - 0.01f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.2980392157f + 0.1647058824f
|
||||
float blueLightStrength = (gamma * 0.44f + 0.12f) * (1.0f - dayEffect) + dayEffect - 0.20f; //gamma * 0.4235294118f + 0.2784313725f
|
||||
|
||||
float[] lightAmbient = {lightStrength, lightStrength, blueLightStrength, 1.0f};
|
||||
|
||||
float lightAmbient[] = {lightStrength, lightStrength, lightStrength, 1.0f};
|
||||
|
||||
// can be used for debugging
|
||||
// if (partialTicks < 0.005)
|
||||
@@ -597,26 +579,14 @@ public class LodRenderer
|
||||
GL11.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
|
||||
|
||||
RenderSystem.enableLighting();
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Create all buffers that will be used.
|
||||
*/
|
||||
public void setupBuffers(int numbRegionsWide)
|
||||
public void setupBuffers(LodDimension lodDim)
|
||||
{
|
||||
// calculate the max amount of memory needed (in bytes)
|
||||
int bufferMemory = RenderUtil.getBufferMemoryForRegion();
|
||||
|
||||
// if the required memory is greater than the
|
||||
// MAX_ALOCATEABLE_DIRECT_MEMORY lower the lodChunkRadiusMultiplier
|
||||
// to fit.
|
||||
if (bufferMemory > MAX_ALOCATEABLE_DIRECT_MEMORY)
|
||||
{
|
||||
ClientProxy.LOGGER.warn("setupBuffers tried to allocate too much memory for the BufferBuilders."
|
||||
+ " It tried to allocate \"" + bufferMemory + "\" bytes, when \"" + MAX_ALOCATEABLE_DIRECT_MEMORY + "\" is the max.");
|
||||
}
|
||||
|
||||
lodBufferBuilder.setupBuffers(numbRegionsWide, bufferMemory);
|
||||
lodBufferBuilder.setupBuffers(lodDim);
|
||||
}
|
||||
|
||||
|
||||
@@ -636,13 +606,18 @@ public class LodRenderer
|
||||
|
||||
/**
|
||||
* Replace the current Vertex Buffers with the newly
|
||||
* created buffers from the lodBufferBuilder.
|
||||
* created buffers from the lodBufferBuilder. <br><br>
|
||||
*
|
||||
* For some reason this has to be called after the frame has been rendered,
|
||||
* otherwise visual stuttering/rubber banding may happen. I'm not sure why...
|
||||
*/
|
||||
private void swapBuffers()
|
||||
{
|
||||
// replace the drawable buffers with
|
||||
// the newly created buffers from the lodBufferBuilder
|
||||
vbos = lodBufferBuilder.getVertexBuffers();
|
||||
VertexBuffersAndOffset result = lodBufferBuilder.getVertexBuffers();
|
||||
vbos = result.vbos;
|
||||
vbosCenter = result.drawableCenterChunkPos;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -656,7 +631,7 @@ public class LodRenderer
|
||||
|
||||
private double getFov(float partialTicks, boolean useFovSetting)
|
||||
{
|
||||
return mc.gameRenderer.getFov(mc.gameRenderer.getMainCamera(), partialTicks, useFovSetting);
|
||||
return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting);
|
||||
}
|
||||
|
||||
|
||||
@@ -668,14 +643,11 @@ public class LodRenderer
|
||||
NearFarFogSettings fogSettings = new NearFarFogSettings();
|
||||
|
||||
|
||||
FogQuality quality = reflectionHandler.getFogQuality();
|
||||
FogQuality quality = ReflectionHandler.INSTANCE.getFogQuality();
|
||||
FogDrawOverride override = LodConfig.CLIENT.graphics.fogDrawOverride.get();
|
||||
|
||||
|
||||
if (quality == FogQuality.OFF)
|
||||
fogSettings.vanillaIsRenderingFog = false;
|
||||
else
|
||||
fogSettings.vanillaIsRenderingFog = true;
|
||||
fogSettings.vanillaIsRenderingFog = quality != FogQuality.OFF;
|
||||
|
||||
|
||||
// use any fog overrides the user may have set
|
||||
@@ -700,7 +672,7 @@ public class LodRenderer
|
||||
|
||||
|
||||
// only use fancy fog if the user's GPU can deliver
|
||||
if (!fancyFogAvailable && quality == FogQuality.FANCY)
|
||||
if (!GlProxy.getInstance().fancyFogAvailable && quality == FogQuality.FANCY)
|
||||
{
|
||||
quality = FogQuality.FAST;
|
||||
}
|
||||
@@ -770,65 +742,69 @@ public class LodRenderer
|
||||
|
||||
return fogSettings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Determines if the LODs should have a fullRegen or partialRegen
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void determineIfLodsShouldRegenerate(LodDimension lodDim)
|
||||
{
|
||||
|
||||
short renderDistance = (short) mc.options.renderDistance;
|
||||
short chunkRenderDistance = (short) mc.getRenderDistance();
|
||||
|
||||
int vanillaRenderedChunksWidth = chunkRenderDistance*2+2;
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// full regens //
|
||||
//=============//
|
||||
|
||||
// check if the view distance changed
|
||||
if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.lodChunkRenderDistance.get()
|
||||
|| mc.options.renderDistance != prevRenderDistance
|
||||
|| prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
|| mc.getRenderDistance() != prevRenderDistance
|
||||
|| prevFogDistance != LodConfig.CLIENT.graphics.fogDistance.get())
|
||||
{
|
||||
DetailDistanceUtil.updateSettings();
|
||||
fullRegen = true;
|
||||
previousPos.changeParameters((byte) 4, mc.player.xChunk, mc.player.zChunk);
|
||||
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
|
||||
prevFogDistance = LodConfig.CLIENT.graphics.fogDistance.get();
|
||||
prevRenderDistance = mc.options.renderDistance;
|
||||
//should use this when it's ready
|
||||
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
|
||||
prevRenderDistance = mc.getRenderDistance();
|
||||
}
|
||||
|
||||
|
||||
// did the user change the debug setting?
|
||||
if (LodConfig.CLIENT.debugging.debugMode.get() != previousDebugMode)
|
||||
{
|
||||
previousDebugMode = LodConfig.CLIENT.debugging.debugMode.get();
|
||||
fullRegen = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
long newTime = System.currentTimeMillis();
|
||||
|
||||
// check if the player has moved
|
||||
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get())
|
||||
|
||||
if(LodConfig.CLIENT.graphics.detailDropOff.get() == DetailDropOff.BY_CHUNK)
|
||||
{
|
||||
if (previousPos.detailLevel == 0
|
||||
|| mc.player.xChunk != previousPos.posX
|
||||
|| mc.player.zChunk != previousPos.posZ)
|
||||
// check if the player has moved
|
||||
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.buffers.bufferRebuildPlayerMoveTimeout.get())
|
||||
{
|
||||
fullRegen = true;
|
||||
previousPos.changeParameters((byte) 4, mc.player.xChunk, mc.player.zChunk);
|
||||
//should use this when it's ready
|
||||
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
|
||||
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|
||||
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
|
||||
|| mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos))
|
||||
{
|
||||
fullRegen = true;
|
||||
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
|
||||
}
|
||||
prevPlayerPosTime = newTime;
|
||||
}
|
||||
prevPlayerPosTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// partial regens //
|
||||
//================//
|
||||
|
||||
|
||||
|
||||
|
||||
// check if the vanilla rendered chunks changed
|
||||
if (newTime - prevVanillaChunkTime > LodConfig.CLIENT.buffers.bufferRebuildChunkChangeTimeout.get())
|
||||
{
|
||||
@@ -840,8 +816,8 @@ public class LodRenderer
|
||||
}
|
||||
prevVanillaChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// check if there is any newly generated terrain to show
|
||||
if (newTime - prevChunkTime > LodConfig.CLIENT.buffers.bufferRebuildLodChangeTimeout.get())
|
||||
{
|
||||
@@ -852,43 +828,54 @@ public class LodRenderer
|
||||
}
|
||||
prevChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// check if the lighting has changed
|
||||
if (mc.getClientWorld().getDayTime() - prevDayTime > 1000 || mc.getOptions().gamma != prevBrightness || lightMap == null)
|
||||
{
|
||||
fullRegen = true;
|
||||
lightMap = mc.getCurrentLightMap();
|
||||
prevBrightness = mc.getOptions().gamma;
|
||||
prevDayTime = mc.getClientWorld().getDayTime();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// LOD skipping //
|
||||
//==============//
|
||||
|
||||
|
||||
// determine which LODs should not be rendered close to the player
|
||||
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.player.blockPosition());
|
||||
int chunkX;
|
||||
int chunkZ;
|
||||
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition());
|
||||
int xIndex;
|
||||
int zIndex;
|
||||
for (ChunkPos pos : chunkPosToSkip)
|
||||
{
|
||||
chunkX = pos.x - mc.player.xChunk + renderDistance + 1;
|
||||
chunkZ = pos.z - mc.player.zChunk + renderDistance + 1;
|
||||
try
|
||||
xIndex = (pos.x - mc.getPlayer().xChunk) + (chunkRenderDistance + 1);
|
||||
zIndex = (pos.z - mc.getPlayer().zChunk) + (chunkRenderDistance + 1);
|
||||
|
||||
// sometimes we are given chunks that are outside the render distance,
|
||||
// This prevents index out of bounds exceptions
|
||||
if (xIndex >= 0 && zIndex >= 0
|
||||
&& xIndex < vanillaRenderedChunks.length
|
||||
&& zIndex < vanillaRenderedChunks.length)
|
||||
{
|
||||
if (!vanillaRenderedChunks[chunkX][chunkZ])
|
||||
if (!vanillaRenderedChunks[xIndex][zIndex])
|
||||
{
|
||||
vanillaRenderedChunks[chunkX][chunkZ] = true;
|
||||
vanillaRenderedChunks[xIndex][zIndex] = true;
|
||||
vanillaRenderedChunksChanged = true;
|
||||
lodDim.setToRegen(pos.getRegionX(), pos.getRegionZ());
|
||||
}
|
||||
}catch (Exception e){
|
||||
System.out.println(vanillaRenderedChunks.length);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if the player is high enough, draw all LODs
|
||||
if(chunkPosToSkip.isEmpty() && mc.player.position().y > 256)
|
||||
if(chunkPosToSkip.isEmpty() && mc.getPlayer().position().y > 256)
|
||||
{
|
||||
vanillaRenderedChunks = new boolean[renderDistance*2+2][renderDistance*2+2];
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
vanillaRenderedChunksChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,9 +19,11 @@ package com.seibel.lod.render;
|
||||
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.LodTemplate;
|
||||
import com.seibel.lod.objects.LodRegion;
|
||||
import com.seibel.lod.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
@@ -35,7 +37,7 @@ import net.minecraft.util.math.vector.Vector3d;
|
||||
*/
|
||||
public class RenderUtil
|
||||
{
|
||||
private static final Minecraft mc = Minecraft.getInstance();
|
||||
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
|
||||
|
||||
|
||||
/**
|
||||
@@ -44,28 +46,24 @@ public class RenderUtil
|
||||
*/
|
||||
public static boolean isChunkPosInLoadedArea(ChunkPos pos, ChunkPos center)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
return (pos.x >= center.x - mc.options.renderDistance
|
||||
&& pos.x <= center.x + mc.options.renderDistance)
|
||||
return (pos.x >= center.x - mc.getRenderDistance()
|
||||
&& pos.x <= center.x + mc.getRenderDistance())
|
||||
&&
|
||||
(pos.z >= center.z - mc.options.renderDistance
|
||||
&& pos.z <= center.z + mc.options.renderDistance);
|
||||
(pos.z >= center.z - mc.getRenderDistance()
|
||||
&& pos.z <= center.z + mc.getRenderDistance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given coordinate is in the loaded area of the world.
|
||||
* @param centerCoordinate the center of the loaded world
|
||||
*/
|
||||
public static boolean isCoordinateInLoadedArea(int i, int j, int centerCoordinate)
|
||||
public static boolean isCoordinateInLoadedArea(int x, int z, int centerCoordinate)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
return (i >= centerCoordinate - mc.options.renderDistance
|
||||
&& i <= centerCoordinate + mc.options.renderDistance)
|
||||
return (x >= centerCoordinate - mc.getRenderDistance()
|
||||
&& x <= centerCoordinate + mc.getRenderDistance())
|
||||
&&
|
||||
(j >= centerCoordinate - mc.options.renderDistance
|
||||
&& j <= centerCoordinate + mc.options.renderDistance);
|
||||
(z >= centerCoordinate - mc.getRenderDistance()
|
||||
&& z <= centerCoordinate + mc.getRenderDistance());
|
||||
}
|
||||
|
||||
|
||||
@@ -93,24 +91,23 @@ public class RenderUtil
|
||||
* it is possible (albeit unlikely) to have a buffer indexOutOfBounds exception
|
||||
* which is caused by the buffer not being big enough.
|
||||
*/
|
||||
public static int getBufferMemoryForRegion()
|
||||
public static int getBufferMemoryForRegion(LodRegion region)
|
||||
{
|
||||
// calculate the max amount of buffer memory needed (in bytes)
|
||||
return LodUtil.REGION_WIDTH_IN_CHUNKS * LodUtil.REGION_WIDTH_IN_CHUNKS *
|
||||
LodConfig.CLIENT.graphics.lodTemplate.get().getBufferMemoryForSingleLod();
|
||||
return region.getMinMemoryNeeded(LodConfig.CLIENT.graphics.lodTemplate.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maxViewDistanceMultiplier for the given LodTemplate
|
||||
* at the given LodDetail level.
|
||||
*/
|
||||
public static int getMaxRadiusMultiplierWithAvaliableMemory(LodTemplate lodTemplate, int detailLevel)
|
||||
/*public static int getMaxRadiusMultiplierWithAvaliableMemory(LodTemplate lodTemplate, int detailLevel)
|
||||
{
|
||||
int maxNumberOfLods = LodRenderer.MAX_ALOCATEABLE_DIRECT_MEMORY / lodTemplate.getBufferMemoryForSingleLod();
|
||||
int numbLodsWide = (int) Math.sqrt(maxNumberOfLods);
|
||||
|
||||
return numbLodsWide / (2 * mc.options.renderDistance);
|
||||
}
|
||||
return numbLodsWide / (2 * mc.getRenderDistance());
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,4 +67,48 @@ public class ColorUtil
|
||||
LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f),
|
||||
LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the given color as a HSV (Hue Saturation Value) color.
|
||||
*/
|
||||
public static int changeBrightness(int color, float brightness)
|
||||
{
|
||||
float[] hsv = Color.RGBtoHSB(getRed(color), getGreen(color), getBlue(color), null);
|
||||
return Color.getHSBColor(
|
||||
hsv[0], // hue
|
||||
hsv[1],
|
||||
brightness).getRGB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit the given color as a HSV (Hue Saturation Value) color.
|
||||
*/
|
||||
public static int changeBrightnessValue(int color, int brightnessColor)
|
||||
{
|
||||
float[] hsv = Color.RGBtoHSB(getRed(color), getGreen(color), getBlue(color), null);
|
||||
float brightness = Color.RGBtoHSB(getRed(brightnessColor), getGreen(brightnessColor), getBlue(brightnessColor), null)[2];
|
||||
return Color.getHSBColor(
|
||||
hsv[0], // hue
|
||||
hsv[1],
|
||||
brightness).getRGB();
|
||||
}
|
||||
public static int multiplyRGBcolors(int color1, int color2)
|
||||
{
|
||||
/**TODO FIX the alpha*/
|
||||
return 0xFF000000 | (((getRed(color1) * getRed(color2)) << 8) & 0xFF0000) | ((getGreen(color1) * getGreen(color2)) & 0xFF00) | (((getBlue(color1) * getBlue(color2)) >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
|
||||
public static String toString(int color)
|
||||
{
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(Integer.toHexString(getAlpha(color)));
|
||||
s.append(" ");
|
||||
s.append(Integer.toHexString(getRed(color)));
|
||||
s.append(" ");
|
||||
s.append(Integer.toHexString(getGreen(color)));
|
||||
s.append(" ");
|
||||
s.append(Integer.toHexString(getBlue(color)));
|
||||
return s.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,448 @@
|
||||
package com.seibel.lod.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
|
||||
public class DataPointUtil
|
||||
{
|
||||
/*
|
||||
|a |a |a |a |r |r |r |r |
|
||||
|
||||
|r |r |r |r |g |g |g |g |
|
||||
|
||||
|g |g |g |g |b |b |b |b |
|
||||
|
||||
|b |b |b |b |h |h |h |h |
|
||||
|
||||
|h |h |h |h |h |h |d |d |
|
||||
|
||||
|d |d |d |d |d |d |d |d |
|
||||
|
||||
|bl |bl |bl |bl |sl |sl |sl |sl |
|
||||
|
||||
|l |l |f |g |g |g |v |e |
|
||||
|
||||
|
||||
*/
|
||||
//To be used in the future for negative value
|
||||
//public final static int MIN_DEPTH = -64;
|
||||
//public final static int MIN_HEIGHT = -64;
|
||||
public final static int EMPTY_DATA = 0;
|
||||
public static int worldHeight = 256;
|
||||
|
||||
public final static int ALPHA_DOWNSIZE_SHIFT = 4;
|
||||
|
||||
//public final static int BLUE_COLOR_SHIFT = 0;
|
||||
//public final static int GREEN_COLOR_SHIFT = 8;
|
||||
//public final static int RED_COLOR_SHIFT = 16;
|
||||
//public final static int ALPHA_COLOR_SHIFT = 24;
|
||||
|
||||
public final static int BLUE_SHIFT = 36;
|
||||
public final static int GREEN_SHIFT = BLUE_SHIFT + 8;
|
||||
public final static int RED_SHIFT = BLUE_SHIFT + 16;
|
||||
public final static int ALPHA_SHIFT = BLUE_SHIFT + 24;
|
||||
|
||||
public final static int COLOR_SHIFT = 36;
|
||||
|
||||
public final static int HEIGHT_SHIFT = 26;
|
||||
public final static int DEPTH_SHIFT = 16;
|
||||
public final static int BLOCK_LIGHT_SHIFT = 12;
|
||||
public final static int SKY_LIGHT_SHIFT = 8;
|
||||
//public final static int LIGHTS_SHIFT = SKY_LIGHT_SHIFT;
|
||||
//public final static int VERTICAL_INDEX_SHIFT = 6;
|
||||
//public final static int FLAG_SHIFT = 5;
|
||||
public final static int GEN_TYPE_SHIFT = 2;
|
||||
public final static int VOID_SHIFT = 1;
|
||||
public final static int EXISTENCE_SHIFT = 0;
|
||||
|
||||
public final static long ALPHA_MASK = 0b1111;
|
||||
public final static long RED_MASK = 0b1111_1111;
|
||||
public final static long GREEN_MASK = 0b1111_1111;
|
||||
public final static long BLUE_MASK = 0b1111_1111;
|
||||
//public final static long COLOR_MASK = 0b11111111_11111111_11111111;
|
||||
public final static long HEIGHT_MASK = 0b11_1111_1111;
|
||||
public final static long DEPTH_MASK = 0b11_1111_1111;
|
||||
//public final static long LIGHTS_MASK = 0b1111_1111;
|
||||
public final static long BLOCK_LIGHT_MASK = 0b1111;
|
||||
public final static long SKY_LIGHT_MASK = 0b1111;
|
||||
//public final static long VERTICAL_INDEX_MASK = 0b11;
|
||||
//public final static long FLAG_MASK = 0b1;
|
||||
public final static long GEN_TYPE_MASK = 0b111;
|
||||
public final static long VOID_MASK = 1;
|
||||
public final static long EXISTENCE_MASK = 1;
|
||||
|
||||
|
||||
public static long createVoidDataPoint(int generationMode)
|
||||
{
|
||||
long dataPoint = 0;
|
||||
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
dataPoint += VOID_MASK << VOID_SHIFT;
|
||||
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
public static long createDataPoint(int height, int depth, int color, int lightSky, int lightBlock, int generationMode)
|
||||
{
|
||||
return createDataPoint(
|
||||
ColorUtil.getAlpha(color),
|
||||
ColorUtil.getRed(color),
|
||||
ColorUtil.getGreen(color),
|
||||
ColorUtil.getBlue(color),
|
||||
height, depth, lightSky, lightBlock, generationMode);
|
||||
}
|
||||
|
||||
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode)
|
||||
{
|
||||
long dataPoint = 0;
|
||||
dataPoint += ((alpha >>> ALPHA_DOWNSIZE_SHIFT) & ALPHA_MASK) << ALPHA_SHIFT;
|
||||
dataPoint += (red & RED_MASK) << RED_SHIFT;
|
||||
dataPoint += (green & GREEN_MASK) << GREEN_SHIFT;
|
||||
dataPoint += (blue & BLUE_MASK) << BLUE_SHIFT;
|
||||
dataPoint += (height & HEIGHT_MASK) << HEIGHT_SHIFT;
|
||||
dataPoint += (depth & DEPTH_MASK) << DEPTH_SHIFT;
|
||||
dataPoint += (lightBlock & BLOCK_LIGHT_MASK) << BLOCK_LIGHT_SHIFT;
|
||||
dataPoint += (lightSky & SKY_LIGHT_MASK) << SKY_LIGHT_SHIFT;
|
||||
dataPoint += (generationMode & GEN_TYPE_MASK) << GEN_TYPE_SHIFT;
|
||||
dataPoint += EXISTENCE_MASK << EXISTENCE_SHIFT;
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
public static short getHeight(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> HEIGHT_SHIFT) & HEIGHT_MASK);
|
||||
}
|
||||
|
||||
public static short getDepth(long dataPoint)
|
||||
{
|
||||
|
||||
return (short) ((dataPoint >>> DEPTH_SHIFT) & DEPTH_MASK);
|
||||
}
|
||||
|
||||
public static short getAlpha(long dataPoint)
|
||||
{
|
||||
return (short) (((dataPoint >>> ALPHA_SHIFT) & ALPHA_MASK) << ALPHA_DOWNSIZE_SHIFT);
|
||||
}
|
||||
|
||||
public static short getRed(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> RED_SHIFT) & RED_MASK);
|
||||
}
|
||||
|
||||
public static short getGreen(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> GREEN_SHIFT) & GREEN_MASK);
|
||||
}
|
||||
|
||||
public static short getBlue(long dataPoint)
|
||||
{
|
||||
return (short) ((dataPoint >>> BLUE_SHIFT) & BLUE_MASK);
|
||||
}
|
||||
|
||||
public static int getLightSky(long dataPoint)
|
||||
{
|
||||
return (int) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static int getLightBlock(long dataPoint)
|
||||
{
|
||||
return (int) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static byte getGenerationMode(long dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isItVoid(long dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> VOID_SHIFT) & VOID_MASK) == 1);
|
||||
}
|
||||
|
||||
public static boolean doesItExist(long dataPoint)
|
||||
{
|
||||
return (((dataPoint >>> EXISTENCE_SHIFT) & EXISTENCE_MASK) == 1);
|
||||
}
|
||||
|
||||
public static int getColor(long dataPoint)
|
||||
{
|
||||
//int color = getBlue(dataPoint) << BLUE_COLOR_SHIFT;
|
||||
//color += getRed(dataPoint) << BLUE_COLOR_SHIFT;
|
||||
return (int) (dataPoint >>> COLOR_SHIFT);
|
||||
}
|
||||
|
||||
public static int getLightColor(long dataPoint, NativeImage lightMap)
|
||||
{
|
||||
int lightBlock = getLightBlock(dataPoint);
|
||||
int lightSky = getLightSky(dataPoint);
|
||||
int color = lightMap.getPixelRGBA(lightBlock, lightSky);
|
||||
int red = ColorUtil.getBlue(color);
|
||||
int green = ColorUtil.getGreen(color);
|
||||
int blue = ColorUtil.getRed(color);
|
||||
|
||||
return ColorUtil.multiplyRGBcolors(getColor(dataPoint), ColorUtil.rgbToInt(red, green, blue));
|
||||
}
|
||||
|
||||
public static String toString(long dataPoint)
|
||||
{
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(getHeight(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getDepth(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getAlpha(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getRed(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getBlue(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getGreen(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getLightBlock(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getLightSky(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(getGenerationMode(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(isItVoid(dataPoint));
|
||||
s.append(" ");
|
||||
s.append(doesItExist(dataPoint));
|
||||
s.append('\n');
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public static long mergeSingleData(long[] dataToMerge)
|
||||
{
|
||||
int numberOfChildren = 0;
|
||||
|
||||
int tempAlpha = 0;
|
||||
int tempRed = 0;
|
||||
int tempGreen = 0;
|
||||
int tempBlue = 0;
|
||||
int tempHeight = Integer.MIN_VALUE;
|
||||
int tempDepth = Integer.MAX_VALUE;
|
||||
int tempLightBlock = 0;
|
||||
int tempLightSky = 0;
|
||||
byte tempGenMode = DistanceGenerationMode.SERVER.complexity;
|
||||
boolean allEmpty = true;
|
||||
boolean allVoid = true;
|
||||
for (long data : dataToMerge)
|
||||
{
|
||||
if (DataPointUtil.doesItExist(data))
|
||||
{
|
||||
allEmpty = false;
|
||||
if (!(DataPointUtil.isItVoid(data)))
|
||||
{
|
||||
numberOfChildren++;
|
||||
allVoid = false;
|
||||
tempAlpha += DataPointUtil.getAlpha(data);
|
||||
tempRed += DataPointUtil.getRed(data);
|
||||
tempGreen += DataPointUtil.getGreen(data);
|
||||
tempBlue += DataPointUtil.getBlue(data);
|
||||
tempHeight = Math.max(tempHeight, DataPointUtil.getHeight(data));
|
||||
tempDepth = Math.min(tempDepth, DataPointUtil.getDepth(data));
|
||||
tempLightBlock += DataPointUtil.getLightBlock(data);
|
||||
tempLightSky += DataPointUtil.getLightSky(data);
|
||||
}
|
||||
tempGenMode = (byte) Math.min(tempGenMode, DataPointUtil.getGenerationMode(data));
|
||||
} else
|
||||
{
|
||||
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
|
||||
}
|
||||
}
|
||||
|
||||
if (allEmpty)
|
||||
{
|
||||
//no child has been initialized
|
||||
return DataPointUtil.EMPTY_DATA;
|
||||
} else if (allVoid)
|
||||
{
|
||||
//all the children are void
|
||||
return DataPointUtil.createVoidDataPoint(tempGenMode);
|
||||
} else
|
||||
{
|
||||
//we have at least 1 child
|
||||
tempAlpha = tempAlpha / numberOfChildren;
|
||||
tempRed = tempRed / numberOfChildren;
|
||||
tempGreen = tempGreen / numberOfChildren;
|
||||
tempBlue = tempBlue / numberOfChildren;
|
||||
tempLightBlock = tempLightBlock / numberOfChildren;
|
||||
tempLightSky = tempLightSky / numberOfChildren;
|
||||
return DataPointUtil.createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, tempHeight, tempDepth, tempLightSky, tempLightBlock, tempGenMode);
|
||||
}
|
||||
}
|
||||
|
||||
public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData)
|
||||
{
|
||||
int size = dataToMerge.length / inputVerticalData;
|
||||
|
||||
//We initialise the arrays that are going to be used
|
||||
short[] projection = ThreadMapUtil.getProjectionShort();
|
||||
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth();
|
||||
long[] singleDataToMerge = ThreadMapUtil.getSingleAddDataToMerge();
|
||||
long[] dataPoint = ThreadMapUtil.verticalDataArray();
|
||||
|
||||
if (projection == null || projection.length != (worldHeight) / 16 + 1)
|
||||
projection = new short[(worldHeight) / 16 + 1];
|
||||
else
|
||||
Arrays.fill(projection, (short) 0);
|
||||
|
||||
if (heightAndDepth == null || heightAndDepth.length != (worldHeight + 1) * 2)
|
||||
heightAndDepth = new short[(worldHeight + 1) * 2];
|
||||
else
|
||||
Arrays.fill(heightAndDepth, (short) 0);
|
||||
|
||||
if (singleDataToMerge == null || singleDataToMerge.length != size)
|
||||
singleDataToMerge = new long[size];
|
||||
else
|
||||
Arrays.fill(singleDataToMerge, EMPTY_DATA);
|
||||
|
||||
if (dataPoint == null || dataPoint.length != worldHeight + 1)
|
||||
dataPoint = new long[worldHeight + 1];
|
||||
else
|
||||
Arrays.fill(dataPoint, EMPTY_DATA);
|
||||
|
||||
int genMode = DistanceGenerationMode.SERVER.complexity;
|
||||
boolean allEmpty = true;
|
||||
boolean allVoid = true;
|
||||
long singleData;
|
||||
|
||||
|
||||
short depth;
|
||||
short height;
|
||||
|
||||
//We collect the indexes of the data, ordered by the depth
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
for (int dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData))
|
||||
{
|
||||
genMode = Math.min(genMode, getGenerationMode(singleData));
|
||||
allEmpty = false;
|
||||
if (!isItVoid(singleData))
|
||||
{
|
||||
allVoid = false;
|
||||
depth = getDepth(singleData);
|
||||
height = getHeight(singleData);
|
||||
for (int y = depth; y <= height; y++)
|
||||
projection[y / 16] |= 1 << (y & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//We check if there is any data that's not empty or void
|
||||
if (allEmpty)
|
||||
{
|
||||
return dataPoint;
|
||||
}
|
||||
if (allVoid)
|
||||
{
|
||||
dataPoint[0] = createVoidDataPoint(genMode);
|
||||
return dataPoint;
|
||||
}
|
||||
//We extract the merged data
|
||||
int count = 0;
|
||||
int i = 0;
|
||||
int ii = 0;
|
||||
while (i < projection.length)
|
||||
{
|
||||
while (i < projection.length && projection[i] == 0) i++;
|
||||
if (i == projection.length)
|
||||
break; //we reached end of WORLD_HEIGHT and it's nothing more here
|
||||
while (ii < 15 && ((projection[i] >>> ii) & 1) == 0) ii++;
|
||||
if (ii >= 15 && ((projection[i] >>> ii) & 1) == 0) //there is nothing more in this chunk
|
||||
{
|
||||
ii = 0;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
depth = (short) (i * 16 + ii);
|
||||
|
||||
while (ii < 15 && ((projection[i] >>> ii) & 1) == 1) ii++;
|
||||
if (ii >= 15 && ((projection[i] >>> ii) & 1) == 1) //if end is not in this chunk
|
||||
{
|
||||
ii = 0;
|
||||
i++;
|
||||
while (i < projection.length && ~(projection[i]) == 0) i++; //check for big solid blocks
|
||||
if (i == projection.length) //solid to WORLD_HEIGHT
|
||||
{
|
||||
heightAndDepth[count * 2] = depth;
|
||||
heightAndDepth[count * 2 + 1] = (short) (worldHeight - 1);
|
||||
break;
|
||||
}
|
||||
while ((((projection[i] >>> ii) & 1) == 1)) ii++;
|
||||
}
|
||||
height = (short) (i * 16 + ii - 1);
|
||||
heightAndDepth[count * 2] = depth;
|
||||
heightAndDepth[count * 2 + 1] = height;
|
||||
count++;
|
||||
}
|
||||
|
||||
//we limit the vertical portion to maxVerticalData
|
||||
int j = 0;
|
||||
while (count > maxVerticalData)
|
||||
{
|
||||
ii = worldHeight;
|
||||
for (i = 0; i < count - 1; i++)
|
||||
{
|
||||
if (heightAndDepth[(i + 1) * 2] - heightAndDepth[i * 2 + 1] < ii)
|
||||
{
|
||||
ii = heightAndDepth[(i + 1) * 2] - heightAndDepth[i * 2 + 1];
|
||||
j = i;
|
||||
}
|
||||
}
|
||||
heightAndDepth[j * 2 + 1] = heightAndDepth[(j + 1) * 2 + 1];
|
||||
for (i = j + 1; i < count - 1; i++)
|
||||
{
|
||||
heightAndDepth[i * 2] = heightAndDepth[(i + 1) * 2];
|
||||
heightAndDepth[i * 2 + 1] = heightAndDepth[(i + 1) * 2 + 1];
|
||||
}
|
||||
//System.arraycopy(heightAndDepth,j + 1, heightAndDepth, j,count - j - 1);
|
||||
count--;
|
||||
}
|
||||
//As standard the vertical lods are ordered from top to bottom
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
depth = heightAndDepth[j * 2];
|
||||
height = heightAndDepth[j * 2 + 1];
|
||||
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
|
||||
break;
|
||||
for (int k = 0; k < size; k++)
|
||||
{
|
||||
singleDataToMerge[k] = 0;
|
||||
}
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
for (int dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData) && !isItVoid(singleData))
|
||||
{
|
||||
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|
||||
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
|
||||
{
|
||||
if (getHeight(singleData) > getHeight(singleDataToMerge[index]))
|
||||
{
|
||||
singleDataToMerge[index] = singleData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
long data = mergeSingleData(singleDataToMerge);
|
||||
|
||||
dataPoint[count - j - 1] = createDataPoint(height, depth, getColor(data), getLightSky(data), getLightBlock(data), getGenerationMode(data));
|
||||
}
|
||||
return dataPoint;
|
||||
}
|
||||
|
||||
public static long[] compress(long[] data, byte detailLevel)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -2,48 +2,59 @@ package com.seibel.lod.util;
|
||||
|
||||
import com.seibel.lod.config.LodConfig;
|
||||
import com.seibel.lod.enums.DistanceGenerationMode;
|
||||
import com.seibel.lod.enums.LodDetail;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import com.seibel.lod.enums.HorizontalResolution;
|
||||
|
||||
public class DetailDistanceUtil
|
||||
{
|
||||
private static double genMultiplier = 1.0;
|
||||
private static double treeGenMultiplier = 1.0;
|
||||
private static double treeCutMultiplier = 1.0;
|
||||
private static int minGenDetail = LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel;
|
||||
private static int minDrawDetail = Math.max(LodConfig.CLIENT.graphics.maxDrawDetail.get().detailLevel,LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel);
|
||||
private static int minGenDetail = LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel;
|
||||
private static int minDrawDetail = Math.max(LodConfig.CLIENT.graphics.drawResolution.get().detailLevel,LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel);
|
||||
private static int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
|
||||
private static int minDistance = 0;
|
||||
private static int maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 2;
|
||||
private static int base = 2;
|
||||
private static double logBase = Math.log(2);
|
||||
|
||||
private static LodDetail[] lodDetails = {
|
||||
LodDetail.FULL,
|
||||
LodDetail.HALF,
|
||||
LodDetail.QUAD,
|
||||
LodDetail.DOUBLE,
|
||||
LodDetail.SINGLE,
|
||||
LodDetail.SINGLE,
|
||||
LodDetail.SINGLE,
|
||||
LodDetail.SINGLE,
|
||||
LodDetail.SINGLE,
|
||||
LodDetail.SINGLE,
|
||||
LodDetail.SINGLE};
|
||||
private static int[] maxVerticalData = {
|
||||
4,
|
||||
4,
|
||||
4,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1};
|
||||
|
||||
|
||||
private static HorizontalResolution[] lodGenDetails = {
|
||||
HorizontalResolution.BLOCK,
|
||||
HorizontalResolution.TWO_BLOCKS,
|
||||
HorizontalResolution.FOUR_BLOCKS,
|
||||
HorizontalResolution.HALF_CHUNK,
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK,
|
||||
HorizontalResolution.CHUNK};
|
||||
|
||||
|
||||
|
||||
public static void updateSettings(){
|
||||
minGenDetail = LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel;
|
||||
minDrawDetail = Math.max(LodConfig.CLIENT.graphics.maxDrawDetail.get().detailLevel,LodConfig.CLIENT.worldGenerator.maxGenerationDetail.get().detailLevel);
|
||||
minGenDetail = LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel;
|
||||
minDrawDetail = Math.max(LodConfig.CLIENT.graphics.drawResolution.get().detailLevel,LodConfig.CLIENT.worldGenerator.generationResolution.get().detailLevel);
|
||||
maxDistance = LodConfig.CLIENT.graphics.lodChunkRenderDistance.get() * 16 * 2;
|
||||
}
|
||||
|
||||
public static int getDistanceRendering(int detail)
|
||||
public static int baseDistanceFunction(int detail)
|
||||
{
|
||||
int initial;
|
||||
int distance = 0;
|
||||
int distanceUnit = LodConfig.CLIENT.graphics.horizontalQuality.get().distanceUnit;
|
||||
if (detail <= minGenDetail)
|
||||
return minDistance;
|
||||
if (detail == maxDetail)
|
||||
@@ -52,30 +63,17 @@ public class DetailDistanceUtil
|
||||
return maxDistance;
|
||||
switch (LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.get())
|
||||
{
|
||||
case LINEAR:
|
||||
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
|
||||
return (detail * initial);
|
||||
case LINEAR:;
|
||||
return (detail * distanceUnit);
|
||||
default:
|
||||
case QUADRATIC:
|
||||
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
|
||||
return (int) (Math.pow(base, detail) * initial);
|
||||
case RENDER_DEPENDANT:
|
||||
int realRenderDistance = Minecraft.getInstance().options.renderDistance * 16;
|
||||
int border = 64;
|
||||
byte detailAtBorder = (byte) 4;
|
||||
if (detail > detailAtBorder)
|
||||
{
|
||||
return (detail * (border - realRenderDistance) / detailAtBorder + realRenderDistance);
|
||||
} else
|
||||
{
|
||||
return ((maxDetail - detail) * (maxDistance - border) / (maxDetail - detailAtBorder) + border);
|
||||
}
|
||||
return (int) (Math.pow(base, detail) * distanceUnit);
|
||||
}
|
||||
return distance;
|
||||
}
|
||||
|
||||
public static byte baseInverse(int distance, int minDetail)
|
||||
public static byte baseInverseFunction(int distance, int minDetail)
|
||||
{
|
||||
int initial;
|
||||
int distanceUnit = LodConfig.CLIENT.graphics.horizontalQuality.get().distanceUnit;
|
||||
byte detail = 0;
|
||||
if (distance == 0)
|
||||
detail = (byte) minDetail;
|
||||
@@ -84,60 +82,34 @@ public class DetailDistanceUtil
|
||||
switch (LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.get())
|
||||
{
|
||||
case LINEAR:
|
||||
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
|
||||
detail = (byte) Math.floorDiv(distance, initial);
|
||||
detail = (byte) Math.floorDiv(distance, distanceUnit);
|
||||
break;
|
||||
case QUADRATIC:
|
||||
initial = LodConfig.CLIENT.graphics.lodQuality.get() * 128;
|
||||
detail = (byte) (Math.log(Math.floorDiv(distance, initial))/logBase);
|
||||
break;
|
||||
case RENDER_DEPENDANT:
|
||||
detail = (byte) 9;
|
||||
detail = (byte) (Math.log(Math.floorDiv(distance, distanceUnit))/logBase);
|
||||
break;
|
||||
}
|
||||
return (byte) Math.min(detail, LodUtil.REGION_DETAIL_LEVEL);
|
||||
}
|
||||
|
||||
public static byte getDistanceRenderingInverse(int distance)
|
||||
public static byte getDrawDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverse(distance, minDrawDetail);
|
||||
return baseInverseFunction(distance, minDrawDetail);
|
||||
}
|
||||
|
||||
public static byte getDistanceGenerationInverse(int distance)
|
||||
public static byte getGenerationDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverse((int) (distance * genMultiplier), minGenDetail);
|
||||
return baseInverseFunction((int) (distance * genMultiplier), minGenDetail);
|
||||
}
|
||||
|
||||
public static byte getDistanceTreeCutInverse(int distance)
|
||||
public static byte getTreeCutDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverse((int) (distance * treeCutMultiplier), minGenDetail);
|
||||
return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail);
|
||||
}
|
||||
|
||||
|
||||
public static byte getDistanceTreeGenInverse(int distance)
|
||||
public static byte getTreeGenDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverse((int) (distance * treeGenMultiplier), minGenDetail);
|
||||
}
|
||||
|
||||
public static int getDistanceGeneration(int detail)
|
||||
{
|
||||
if (detail == maxDetail)
|
||||
return maxDistance;
|
||||
return (int) (getDistanceRendering(detail) * genMultiplier);
|
||||
}
|
||||
|
||||
public static int getDistanceTreeCut(int detail)
|
||||
{
|
||||
if (detail == maxDetail)
|
||||
return maxDistance;
|
||||
return (int) (getDistanceRendering(detail) * treeCutMultiplier);
|
||||
}
|
||||
|
||||
public static int getDistanceTreeGen(int detail)
|
||||
{
|
||||
if (detail == maxDetail)
|
||||
return maxDistance;
|
||||
return (int) (getDistanceRendering(detail) * treeGenMultiplier);
|
||||
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail);
|
||||
}
|
||||
|
||||
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
|
||||
@@ -147,17 +119,29 @@ public class DetailDistanceUtil
|
||||
|
||||
public static byte getLodDrawDetail(int detail)
|
||||
{
|
||||
return (byte) Math.max(detail, minDrawDetail);
|
||||
if (detail < minDrawDetail)
|
||||
{
|
||||
if(LodConfig.CLIENT.graphics.alwaysDrawAtMaxQuality.get())
|
||||
return getLodGenDetail(minDrawDetail).detailLevel;
|
||||
else
|
||||
return (byte) minDrawDetail;
|
||||
} else
|
||||
{
|
||||
if(LodConfig.CLIENT.graphics.alwaysDrawAtMaxQuality.get())
|
||||
return getLodGenDetail(detail).detailLevel;
|
||||
else
|
||||
return (byte) detail;
|
||||
}
|
||||
}
|
||||
|
||||
public static LodDetail getLodGenDetail(int detail)
|
||||
public static HorizontalResolution getLodGenDetail(int detail)
|
||||
{
|
||||
if (detail < minGenDetail)
|
||||
{
|
||||
return lodDetails[minGenDetail];
|
||||
return lodGenDetails[minGenDetail];
|
||||
} else
|
||||
{
|
||||
return lodDetails[detail];
|
||||
return lodGenDetails[detail];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,24 +150,19 @@ public class DetailDistanceUtil
|
||||
{
|
||||
if (detail < minGenDetail)
|
||||
{
|
||||
return lodDetails[minGenDetail].detailLevel;
|
||||
return lodGenDetails[minGenDetail].detailLevel;
|
||||
} else if (detail == maxDetail)
|
||||
{
|
||||
return LodUtil.REGION_DETAIL_LEVEL;
|
||||
} else
|
||||
{
|
||||
return lodDetails[detail].detailLevel;
|
||||
return lodGenDetails[detail].detailLevel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean regionInView(int playerPosX, int playerPosY, int playerPosZ, int xRot, int yRot, int fov, RegionPos regionPos)
|
||||
public static int getMaxVerticalData(int detail)
|
||||
{
|
||||
|
||||
//System.out.println(Math.floorMod((int) mc.player.xRot,360) + " " + Math.floorMod((int) mc.player.yRot,360) + " " + mc.options.fov);
|
||||
//System.out.println(mc.player.xRotO + " " + mc.player.yRotO);
|
||||
//mc.options.fov;
|
||||
return false;
|
||||
return maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+85
-47
@@ -1,9 +1,4 @@
|
||||
package com.seibel.lod.objects;
|
||||
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
|
||||
import java.util.Comparator;
|
||||
package com.seibel.lod.util;
|
||||
|
||||
public class LevelPosUtil
|
||||
{
|
||||
@@ -37,11 +32,29 @@ public class LevelPosUtil
|
||||
return new int[]{detailLevel, posX, posZ};
|
||||
}
|
||||
|
||||
public static int[] createLevelPos(byte detailLevel, int posX, int posZ, int distance)
|
||||
public static int convert(byte detailLevel, int pos, byte newDetailLevel)
|
||||
{
|
||||
return new int[]{detailLevel, posX, posZ, distance};
|
||||
int width;
|
||||
if (newDetailLevel >= detailLevel)
|
||||
{
|
||||
width = 1 << (newDetailLevel - detailLevel);
|
||||
return Math.floorDiv(pos, width);
|
||||
} else
|
||||
{
|
||||
width = 1 << (detailLevel - newDetailLevel);
|
||||
return pos * width;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getRegion(byte detailLevel, int pos)
|
||||
{
|
||||
return Math.floorDiv(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
|
||||
}
|
||||
|
||||
public static int getRegionModule(byte detailLevel, int pos)
|
||||
{
|
||||
return Math.floorMod(pos, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
|
||||
}
|
||||
|
||||
public static byte getDetailLevel(int[] levelPos)
|
||||
{
|
||||
@@ -105,22 +118,11 @@ public class LevelPosUtil
|
||||
return Math.floorDiv(getPosZ(levelPos), width);
|
||||
}
|
||||
|
||||
public static int getChunkPosX(int[] levelPos)
|
||||
public static int getChunkPos(byte detailLevel, int pos)
|
||||
{
|
||||
levelPos = convert(levelPos, LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
return getPosX(levelPos);
|
||||
return convert(detailLevel,pos, LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
}
|
||||
|
||||
public static int getChunkPosZ(int[] levelPos)
|
||||
{
|
||||
levelPos = convert(levelPos, LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
return getPosZ(levelPos);
|
||||
}
|
||||
|
||||
public static int maxDistance(int[] levelPos, int playerPosX, int playerPosZ)
|
||||
{
|
||||
return maxDistance(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos), playerPosX, playerPosZ);
|
||||
}
|
||||
|
||||
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
|
||||
{
|
||||
@@ -139,12 +141,24 @@ public class LevelPosUtil
|
||||
return maxDistance;
|
||||
}
|
||||
|
||||
|
||||
public static int minDistance(int[] levelPos, int playerPosX, int playerPosZ)
|
||||
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion)
|
||||
{
|
||||
return minDistance(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos), playerPosX, playerPosZ);
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
int startPosX = xRegion * 512 + posX * width;
|
||||
int startPosZ = zRegion * 512 + posZ * width;
|
||||
int endPosX = startPosX + width;
|
||||
int endPosZ = startPosZ + width;
|
||||
|
||||
int maxDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
|
||||
return maxDistance;
|
||||
}
|
||||
|
||||
|
||||
public static int minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
|
||||
{
|
||||
int width = 1 << detailLevel;
|
||||
@@ -181,40 +195,60 @@ public class LevelPosUtil
|
||||
}
|
||||
}
|
||||
|
||||
public static int compareDistance(int posX, int posZ, int[] first, int[] second)
|
||||
public static int minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion)
|
||||
{
|
||||
return Integer.compare(
|
||||
minDistance(first, posX, posZ),
|
||||
minDistance(second, posX, posZ));
|
||||
}
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
public static int compareDistance(int[] first, int[] second)
|
||||
{
|
||||
return Integer.compare(
|
||||
getDistance(first),
|
||||
getDistance(second));
|
||||
}
|
||||
int startPosX = xRegion * 512 + posX * width;
|
||||
int startPosZ = zRegion * 512 + posZ * width;
|
||||
int endPosX = startPosX + width;
|
||||
int endPosZ = startPosZ + width;
|
||||
|
||||
public static int compareLevelAndDistance(int[] first, int[] second)
|
||||
{
|
||||
int compareResult = Integer.compare(getDetailLevel(second), getDetailLevel(first));
|
||||
if (compareResult == 0)
|
||||
boolean inXArea = playerPosX >= startPosX && playerPosX <= endPosX;
|
||||
boolean inZArea = playerPosZ >= startPosZ && playerPosZ <= endPosZ;
|
||||
if (inXArea && inZArea)
|
||||
{
|
||||
compareResult = Integer.compare(
|
||||
getDistance(first),
|
||||
getDistance(second));
|
||||
return 0;
|
||||
} else if (inXArea)
|
||||
{
|
||||
return Math.min(
|
||||
Math.abs(playerPosZ - startPosZ),
|
||||
Math.abs(playerPosZ - endPosZ)
|
||||
);
|
||||
} else if (inZArea)
|
||||
{
|
||||
return Math.min(
|
||||
Math.abs(playerPosX - startPosX),
|
||||
Math.abs(playerPosX - endPosX)
|
||||
);
|
||||
} else
|
||||
{
|
||||
int minDistance = (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - startPosZ, 2));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - startPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - startPosZ, 2)));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(Math.pow(playerPosX - endPosX, 2) + Math.pow(playerPosZ - endPosZ, 2)));
|
||||
return minDistance;
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
|
||||
public static int compareLevelAndDistance(int posX, int posZ, int[] first, int[] second)
|
||||
public static int compareDistance(int firstDistance, int secondDistance)
|
||||
{
|
||||
int compareResult = Integer.compare(getDetailLevel(second), getDetailLevel(first));
|
||||
return Integer.compare(
|
||||
firstDistance,
|
||||
secondDistance);
|
||||
}
|
||||
|
||||
|
||||
public static int compareLevelAndDistance(byte firstDetail, int firstDistance, byte secondDetail, int secondDistance)
|
||||
{
|
||||
int compareResult = Integer.compare(
|
||||
secondDetail,
|
||||
firstDetail);
|
||||
if (compareResult == 0)
|
||||
{
|
||||
compareResult = Integer.compare(
|
||||
minDistance(first, posX, posZ),
|
||||
minDistance(second, posX, posZ));
|
||||
firstDistance,
|
||||
secondDistance);
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
@@ -223,4 +257,8 @@ public class LevelPosUtil
|
||||
{
|
||||
return (getDetailLevel(levelPos) + " " + getPosX(levelPos) + " " + getPosZ(levelPos));
|
||||
}
|
||||
public static String toString(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return (detailLevel + " " + posX + " " + posZ);
|
||||
}
|
||||
}
|
||||
@@ -23,11 +23,11 @@ import java.util.HashSet;
|
||||
|
||||
import com.seibel.lod.objects.LodDimension;
|
||||
import com.seibel.lod.objects.RegionPos;
|
||||
import com.seibel.lod.objects.LevelPos.LevelPos;
|
||||
import com.seibel.lod.wrappers.MinecraftWrapper;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher.CompiledChunk;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
@@ -43,11 +43,11 @@ import net.minecraft.world.server.ServerWorld;
|
||||
* This class holds methods and constants that may be used in multiple places.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-29-2021
|
||||
* @version 9-7-2021
|
||||
*/
|
||||
public class LodUtil
|
||||
{
|
||||
private static Minecraft mc = Minecraft.getInstance();
|
||||
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
|
||||
|
||||
|
||||
/** alpha used when drawing chunks in debug mode */
|
||||
@@ -62,7 +62,10 @@ public class LodUtil
|
||||
* and/or add a method to generate colors based on texture
|
||||
* issue #64 */
|
||||
public static final int STONE_COLOR_INT = LodUtil.colorToInt(new Color(150, 150, 150));
|
||||
|
||||
public static final int NETHERRACK_COLOR_INT = LodUtil.colorToInt(new Color(95, 38, 38));
|
||||
public static final int WARPED_NYLIUM_COLOR_INT = LodUtil.colorToInt(new Color(34, 94, 85));
|
||||
public static final int CRIMSON_NYLIUM_COLOR_INT = LodUtil.colorToInt(new Color(126, 27, 27));
|
||||
|
||||
/**
|
||||
* In order of nearest to farthest: <br>
|
||||
* Red, Orange, Yellow, Green, Cyan, Blue, Magenta, white, gray, black
|
||||
@@ -79,7 +82,9 @@ public class LodUtil
|
||||
|
||||
|
||||
public static final byte DETAIL_OPTIONS = 10;
|
||||
|
||||
|
||||
public static final short MAX_VERTICAL_DATA = 4;
|
||||
|
||||
/** measured in Blocks <br>
|
||||
* detail level 9 */
|
||||
public static final short REGION_WIDTH = 512;
|
||||
@@ -98,6 +103,9 @@ public class LodUtil
|
||||
/** If we ever need to use a heightmap for any reason, use this one. */
|
||||
public static final Heightmap.Type DEFAULT_HEIGHTMAP = Heightmap.Type.WORLD_SURFACE_WG;
|
||||
|
||||
/** This regex finds any characters that are invalid for use in a windows
|
||||
* (and by extension mac and linux) file path */
|
||||
public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\\\/:*?\\\"<>|]";
|
||||
|
||||
|
||||
|
||||
@@ -207,10 +215,7 @@ public class LodUtil
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = mc.getCurrentServer();
|
||||
return server.name + ", IP " +
|
||||
server.ip + ", GameVersion " +
|
||||
server.version.getString();
|
||||
return getServerId();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,15 +224,13 @@ public class LodUtil
|
||||
/**
|
||||
* If on single player this will return the name of the user's
|
||||
* world and the dimensional save folder, if in multiplayer
|
||||
* it will return the server name, game version, and dimension.<br>
|
||||
* it will return the server name, ip, game version, and dimension.<br>
|
||||
* <br>
|
||||
* This can be used to determine where to save files for a given
|
||||
* dimension.
|
||||
*/
|
||||
public static String getDimensionIDFromWorld(IWorld world)
|
||||
{
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
if(mc.hasSingleplayerServer())
|
||||
{
|
||||
// this will return the world save location
|
||||
@@ -245,14 +248,25 @@ public class LodUtil
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerData server = mc.getCurrentServer();
|
||||
return server.name + ", IP " +
|
||||
server.ip + ", GameVersion " +
|
||||
server.version.getString() + File.separatorChar
|
||||
+ "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar;
|
||||
return getServerId() + File.separatorChar + "dim_" + world.dimensionType().effectsLocation().getPath() + File.separatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the server name, IP and game version.
|
||||
*/
|
||||
public static String getServerId()
|
||||
{
|
||||
ServerData server = mc.getCurrentServer();
|
||||
|
||||
String serverName = server.name.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
|
||||
String serverIp = server.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
|
||||
String serverMcVersion = server.version.getString().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
|
||||
|
||||
String serverId = serverName + ", IP " + serverIp + ", GameVersion " + serverMcVersion;
|
||||
|
||||
return serverId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -311,39 +325,40 @@ public class LodUtil
|
||||
* Get a HashSet of all ChunkPos within the normal render distance
|
||||
* that should not be rendered.
|
||||
*/
|
||||
public static HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
|
||||
{
|
||||
int chunkRenderDist = mc.options.renderDistance;
|
||||
ChunkPos centerChunk = new ChunkPos(playerPos);
|
||||
|
||||
// skip chunks that are already going to be rendered by Minecraft
|
||||
HashSet<ChunkPos> posToSkip = getRenderedChunks();
|
||||
|
||||
// go through each chunk within the normal view distance
|
||||
for (int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++)
|
||||
{
|
||||
for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
|
||||
{
|
||||
LevelPos levelPos = new LevelPos(LodUtil.CHUNK_DETAIL_LEVEL, x, z);
|
||||
if (!lodDim.doesDataExist(levelPos.clone()))
|
||||
continue;
|
||||
|
||||
short[] data = lodDim.getData(levelPos);
|
||||
|
||||
short lodAverageHeight = data[0];
|
||||
if (playerPos.getY() <= lodAverageHeight)
|
||||
{
|
||||
// don't draw Lod's that are taller than the player
|
||||
// to prevent LODs being drawn on top of the player
|
||||
posToSkip.add(new ChunkPos(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return posToSkip;
|
||||
}
|
||||
public static HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
|
||||
{
|
||||
int chunkRenderDist = mc.getRenderDistance();
|
||||
ChunkPos centerChunk = new ChunkPos(playerPos);
|
||||
|
||||
/**
|
||||
// skip chunks that are already going to be rendered by Minecraft
|
||||
HashSet<ChunkPos> posToSkip = getRenderedChunks();
|
||||
|
||||
// go through each chunk within the normal view distance
|
||||
for (int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++)
|
||||
{
|
||||
for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
|
||||
{
|
||||
if (!lodDim.doesDataExist(LodUtil.CHUNK_DETAIL_LEVEL, x, z))
|
||||
continue;
|
||||
|
||||
long data = lodDim.getSingleData(LodUtil.CHUNK_DETAIL_LEVEL, x, z);
|
||||
|
||||
short lodAverageHeight = DataPointUtil.getHeight(data);
|
||||
|
||||
if (playerPos.getY() <= lodAverageHeight)
|
||||
{
|
||||
// don't draw Lod's that are taller than the player
|
||||
// to prevent LODs being drawn on top of the player
|
||||
posToSkip.add(new ChunkPos(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return posToSkip;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of all chunks that Minecraft
|
||||
* is going to render this frame. <br><br>
|
||||
* <p>
|
||||
@@ -355,22 +370,23 @@ public class LodUtil
|
||||
{
|
||||
HashSet<ChunkPos> loadedPos = new HashSet<>();
|
||||
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
// Wow those are some long names!
|
||||
|
||||
// go through every RenderInfo to get the compiled chunks
|
||||
for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : mc.levelRenderer.renderChunks)
|
||||
WorldRenderer renderer = mc.getLevelRenderer();
|
||||
for (WorldRenderer.LocalRenderInformationContainer worldrenderer$localrenderinformationcontainer : renderer.renderChunks)
|
||||
{
|
||||
if (!worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk().hasNoRenderableLayers())
|
||||
CompiledChunk compiledChunk = worldrenderer$localrenderinformationcontainer.chunk.getCompiledChunk();
|
||||
if (!compiledChunk.hasNoRenderableLayers())
|
||||
{
|
||||
// add the ChunkPos for every empty compiled chunk
|
||||
// add the ChunkPos for every rendered chunk
|
||||
BlockPos bpos = worldrenderer$localrenderinformationcontainer.chunk.getOrigin();
|
||||
|
||||
loadedPos.add(new ChunkPos(bpos));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return loadedPos;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.seibel.lod.util;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
public class ThreadMapUtil
|
||||
{
|
||||
public static final ConcurrentMap<String, long[]> threadSingleUpdateMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[][]> threadBuilderArrayMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[][]> threadBuilderVerticalArrayMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[]> threadVerticalAddDataMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, byte[]> saveContainer = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, short[]> projectionShortMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, short[]> heightAndDepthMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[]> singleDataToMergeMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, long[][]> verticalUpdate = new ConcurrentHashMap<>();
|
||||
|
||||
public static long[] getSingleUpdateArray()
|
||||
{
|
||||
if (!threadSingleUpdateMap.containsKey(Thread.currentThread().getName()) || (threadSingleUpdateMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
threadSingleUpdateMap.put(Thread.currentThread().getName(), new long[4]);
|
||||
}
|
||||
return threadSingleUpdateMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static long[][] getBuilderArray()
|
||||
{
|
||||
if (!threadBuilderArrayMap.containsKey(Thread.currentThread().getName()) || (threadBuilderArrayMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
long[][] array = new long[5][];
|
||||
threadBuilderArrayMap.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
return threadBuilderArrayMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static long[][] getBuilderVerticalArray()
|
||||
{
|
||||
if (!threadBuilderVerticalArrayMap.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
long[][] array = new long[5][];
|
||||
threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
return threadBuilderVerticalArrayMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static long[] verticalDataArray()
|
||||
{
|
||||
if (!threadVerticalAddDataMap.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
threadVerticalAddDataMap.put(Thread.currentThread().getName(), new long[0]);
|
||||
}
|
||||
return threadVerticalAddDataMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static short[] getProjectionShort(){
|
||||
if(!projectionShortMap.containsKey(Thread.currentThread().getName()) || (projectionShortMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
projectionShortMap.put(Thread.currentThread().getName(), new short[0]);
|
||||
}
|
||||
return projectionShortMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static short[] getHeightAndDepth(){
|
||||
if(!heightAndDepthMap.containsKey(Thread.currentThread().getName()) || (heightAndDepthMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
heightAndDepthMap.put(Thread.currentThread().getName(), new short[0]);
|
||||
}
|
||||
return heightAndDepthMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static byte[] getSaveContainer(){
|
||||
if(!saveContainer.containsKey(Thread.currentThread().getName()) || (saveContainer.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
saveContainer.put(Thread.currentThread().getName(), new byte[0]);
|
||||
}
|
||||
return saveContainer.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static long[][] getVerticalUpdateArray(){
|
||||
if(!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
long[][] array = new long[10][];
|
||||
verticalUpdate.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
return verticalUpdate.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static long[] getSingleAddDataToMerge(){
|
||||
if(!singleDataToMergeMap.containsKey(Thread.currentThread().getName()) || (singleDataToMergeMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
singleDataToMergeMap.put(Thread.currentThread().getName(), new long[0]);
|
||||
}
|
||||
return singleDataToMergeMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static void clearMaps(){
|
||||
threadSingleUpdateMap.clear();
|
||||
threadBuilderArrayMap.clear();
|
||||
threadBuilderVerticalArrayMap.clear();
|
||||
threadVerticalAddDataMap.clear();
|
||||
saveContainer.clear();
|
||||
projectionShortMap.clear();
|
||||
heightAndDepthMap.clear();
|
||||
singleDataToMergeMap.clear();
|
||||
verticalUpdate.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package com.seibel.lod.wrappers;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
|
||||
import com.seibel.lod.util.LodUtil;
|
||||
|
||||
import net.minecraft.client.GameSettings;
|
||||
import net.minecraft.client.MainWindow;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.player.ClientPlayerEntity;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.network.play.ClientPlayNetHandler;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.WorldRenderer;
|
||||
import net.minecraft.client.renderer.model.ModelManager;
|
||||
import net.minecraft.client.renderer.texture.NativeImage;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.world.DimensionType;
|
||||
|
||||
/**
|
||||
* A singleton that wraps the Minecraft class
|
||||
* to allow for easier movement between Minecraft versions.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 9-16-2021
|
||||
*/
|
||||
public class MinecraftWrapper
|
||||
{
|
||||
public static MinecraftWrapper INSTANCE = new MinecraftWrapper();
|
||||
|
||||
private Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
/** The lightmap for the current:
|
||||
* Time, dimension, brightness setting, etc. */
|
||||
private NativeImage lightMap = null;
|
||||
|
||||
private MinecraftWrapper()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* This should be called at the beginning of every frame to
|
||||
* clear any Minecraft data that becomes out of date after a frame. <br> <br>
|
||||
*
|
||||
* Lightmaps and other time sensitive objects fall in this category. <br> <br>
|
||||
*
|
||||
* This doesn't effect OpenGL objects in any way.
|
||||
*/
|
||||
public void clearFrameObjectCache()
|
||||
{
|
||||
lightMap = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// method wrappers //
|
||||
//=================//
|
||||
|
||||
public float getShading(Direction direction)
|
||||
{
|
||||
return mc.level.getShade(Direction.UP, true);
|
||||
}
|
||||
|
||||
public boolean hasSingleplayerServer()
|
||||
{
|
||||
return mc.hasSingleplayerServer();
|
||||
}
|
||||
|
||||
public DimensionType getCurrentDimension()
|
||||
{
|
||||
return mc.player.level.dimensionType();
|
||||
}
|
||||
|
||||
public String getCurrentDimensionId()
|
||||
{
|
||||
return LodUtil.getDimensionIDFromWorld(mc.level);
|
||||
}
|
||||
|
||||
/**
|
||||
* This texture changes every frame
|
||||
*/
|
||||
public NativeImage getCurrentLightMap()
|
||||
{
|
||||
// get the current lightMap if the cache is empty
|
||||
if (lightMap == null)
|
||||
{
|
||||
LightTexture tex = mc.gameRenderer.lightTexture();
|
||||
NativeImage lightPixels = tex.lightPixels;
|
||||
lightMap = lightPixels;
|
||||
}
|
||||
|
||||
// // hotswap this to true, then back to false to write a file
|
||||
// // (and not write a file every frame)
|
||||
// if (false)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // obviously change the filepath to somewhere on your PC
|
||||
// lightPixels.writeToFile(new File("C:\\Users\\James Seibel\\Desktop\\image.png"));
|
||||
// }
|
||||
// catch(Exception e)
|
||||
// {
|
||||
// e.printStackTrace();
|
||||
// }
|
||||
// }
|
||||
|
||||
return lightMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color int at the given pixel coordinates
|
||||
* from the current lightmap.
|
||||
*
|
||||
* @param u x location in texture space
|
||||
* @param v z location in texture space
|
||||
*/
|
||||
public int getColorIntFromLightMap(int u, int v)
|
||||
{
|
||||
if (lightMap == null)
|
||||
{
|
||||
// make sure the lightMap is up to date
|
||||
getCurrentLightMap();
|
||||
}
|
||||
|
||||
return lightMap.getPixelRGBA(u, v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Color at the given pixel coordinates
|
||||
* from the current lightmap.
|
||||
*
|
||||
* @param u x location in texture space
|
||||
* @param v z location in texture space
|
||||
*/
|
||||
public Color getColorFromLightMap(int u, int v)
|
||||
{
|
||||
return LodUtil.intToColor(lightMap.getPixelRGBA(u, v));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// Simple gets //
|
||||
//=============//
|
||||
|
||||
public ClientPlayerEntity getPlayer()
|
||||
{
|
||||
return mc.player;
|
||||
}
|
||||
|
||||
public GameSettings getOptions()
|
||||
{
|
||||
return mc.options;
|
||||
}
|
||||
|
||||
public ModelManager getModelManager()
|
||||
{
|
||||
return mc.getModelManager();
|
||||
}
|
||||
|
||||
public ClientWorld getClientWorld()
|
||||
{
|
||||
return mc.level;
|
||||
}
|
||||
|
||||
/** Measured in chunks */
|
||||
public int getRenderDistance()
|
||||
{
|
||||
return mc.options.renderDistance;
|
||||
}
|
||||
|
||||
public File getGameDirectory()
|
||||
{
|
||||
return mc.gameDirectory;
|
||||
}
|
||||
|
||||
public IProfiler getProfiler()
|
||||
{
|
||||
return mc.getProfiler();
|
||||
}
|
||||
|
||||
public ClientPlayNetHandler getConnection()
|
||||
{
|
||||
return mc.getConnection();
|
||||
}
|
||||
|
||||
public GameRenderer getGameRenderer()
|
||||
{
|
||||
return mc.gameRenderer;
|
||||
}
|
||||
|
||||
public Entity getCameraEntity()
|
||||
{
|
||||
return mc.cameraEntity;
|
||||
}
|
||||
|
||||
public MainWindow getWindow()
|
||||
{
|
||||
return mc.getWindow();
|
||||
}
|
||||
|
||||
public float getSkyDarken(float partialTicks)
|
||||
{
|
||||
return mc.level.getSkyDarken(partialTicks);
|
||||
}
|
||||
|
||||
public IntegratedServer getSingleplayerServer()
|
||||
{
|
||||
return mc.getSingleplayerServer();
|
||||
}
|
||||
|
||||
public ServerData getCurrentServer()
|
||||
{
|
||||
return mc.getCurrentServer();
|
||||
}
|
||||
|
||||
public WorldRenderer getLevelRenderer()
|
||||
{
|
||||
return mc.levelRenderer;
|
||||
}
|
||||
}
|
||||
@@ -44,6 +44,13 @@ public net.minecraft.world.lighting.WorldLightManager field_215576_a # blockEngi
|
||||
public net.minecraft.world.lighting.WorldLightManager field_215577_b # skyEngine
|
||||
public net.minecraft.world.gen.feature.Feature field_236290_a_ # configuredCodec
|
||||
|
||||
# used for uploading vertex buffers off the render thread
|
||||
public net.minecraft.client.renderer.vertex.VertexBuffer field_177365_a # id
|
||||
public net.minecraft.client.renderer.vertex.VertexBuffer field_177363_b # format
|
||||
public net.minecraft.client.renderer.vertex.VertexBuffer field_177364_c # vertexCount
|
||||
|
||||
# used for accessing the lightmap
|
||||
public net.minecraft.client.renderer.LightTexture field_205111_b # lightPixels
|
||||
|
||||
|
||||
#=====================#
|
||||
|
||||
@@ -24,7 +24,7 @@ modId="lod" #mandatory
|
||||
#// The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
|
||||
#//${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
|
||||
#// see the associated build.gradle script for how to populate this completely automatically during a build
|
||||
version="a1.4" #mandatory
|
||||
version="a1.5.0-pre" #mandatory
|
||||
|
||||
#// A display name for the mod
|
||||
displayName="LOD" #mandatory
|
||||
@@ -42,7 +42,7 @@ logoFile="logo.png" #optional
|
||||
credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional
|
||||
|
||||
#// A text field displayed in the mod UI
|
||||
authors="James Seibel and Leonardo Amato" #optional
|
||||
authors="James Seibel, Leonardo Amato, and Cola" #optional
|
||||
|
||||
#// The description text for the mod (multi line!) (#mandatory)
|
||||
description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.'''
|
||||
Reference in New Issue
Block a user