Fixing stuff towards successful builds. Now stuck on thinking how the world gen stuff should return and apply the gen chunk to data though...
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
package com.seibel.lod.core.a7.datatype.column;
|
||||
|
||||
import com.seibel.lod.core.a7.datatype.LodDataSource;
|
||||
import com.seibel.lod.core.a7.datatype.full.FullDataSource;
|
||||
import com.seibel.lod.core.a7.datatype.transform.FullToColumnTransformer;
|
||||
import com.seibel.lod.core.a7.level.IClientLevel;
|
||||
import com.seibel.lod.core.a7.datatype.LodRenderSource;
|
||||
import com.seibel.lod.core.a7.datatype.RenderSourceLoader;
|
||||
@@ -27,7 +29,9 @@ public class ColumnRenderLoader extends RenderSourceLoader {
|
||||
|
||||
@Override
|
||||
public LodRenderSource createRender(LodDataSource dataSource, IClientLevel level) {
|
||||
//TODO
|
||||
if (dataSource instanceof FullDataSource) {
|
||||
return FullToColumnTransformer.transformFullDataToColumnData(level, (FullDataSource) dataSource);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
+15
-16
@@ -3,11 +3,12 @@ package com.seibel.lod.core.a7.datatype.column.render;
|
||||
import com.seibel.lod.core.a7.datatype.column.ColumnRenderSource;
|
||||
import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView;
|
||||
import com.seibel.lod.core.a7.level.IClientLevel;
|
||||
import com.seibel.lod.core.a7.pos.DhBlockPos2D;
|
||||
import com.seibel.lod.core.a7.render.a7LodRenderer;
|
||||
import com.seibel.lod.core.a7.util.UncheckedInterruptedException;
|
||||
import com.seibel.lod.core.a7.render.RenderBuffer;
|
||||
import com.seibel.lod.core.config.Config;
|
||||
import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.CubicLodTemplate;
|
||||
import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodQuadBuilder;
|
||||
import com.seibel.lod.core.enums.ELodDirection;
|
||||
import com.seibel.lod.core.enums.config.EGpuUploadMethod;
|
||||
@@ -15,6 +16,7 @@ import com.seibel.lod.core.enums.rendering.EDebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.EGLProxyContext;
|
||||
import com.seibel.lod.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.objects.DHBlockPos;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderProgram;
|
||||
import com.seibel.lod.core.render.objects.GLVertexBuffer;
|
||||
@@ -45,8 +47,10 @@ public class ColumnRenderBuffer extends RenderBuffer {
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000;
|
||||
GLVertexBuffer[] vbos;
|
||||
public final DHBlockPos pos;
|
||||
|
||||
public ColumnRenderBuffer() {
|
||||
public ColumnRenderBuffer(DHBlockPos pos) {
|
||||
this.pos = pos;
|
||||
vbos = new GLVertexBuffer[0];
|
||||
}
|
||||
|
||||
@@ -67,7 +71,7 @@ public class ColumnRenderBuffer extends RenderBuffer {
|
||||
int size = bb.limit() - bb.position();
|
||||
try {
|
||||
vbo.bind();
|
||||
vbo.uploadBuffer(bb, size/LodUtil.LOD_VERTEX_FORMAT.getByteSize(), method, LodBufferBuilderFactory.FULL_SIZED_BUFFER);
|
||||
vbo.uploadBuffer(bb, size/LodUtil.LOD_VERTEX_FORMAT.getByteSize(), method, FULL_SIZED_BUFFER);
|
||||
} catch (Exception e) {
|
||||
vbos[i-1] = null;
|
||||
vbo.close();
|
||||
@@ -136,22 +140,14 @@ public class ColumnRenderBuffer extends RenderBuffer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean render(LodRenderProgram shaderProgram) {
|
||||
public boolean render(a7LodRenderer renderContext) {
|
||||
boolean hasRendered = false;
|
||||
renderContext.setupOffset(pos);
|
||||
for (GLVertexBuffer vbo : vbos) {
|
||||
if (vbo == null) continue;
|
||||
if (vbo.getVertexCount() == 0) continue;
|
||||
hasRendered = true;
|
||||
vbo.bind();
|
||||
shaderProgram.bindVertexBuffer(vbo.getId());
|
||||
//FIXME: Fix this! Need to pass this down
|
||||
if (/*LodRenderer.ENABLE_IBO*/ true) {
|
||||
GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount()/4)*6,
|
||||
GL32.GL_INT //FIXME: Fix this! Need to pass this as argument down the chains!
|
||||
, 0);
|
||||
} else {
|
||||
GL32.glDrawArrays(GL32.GL_TRIANGLES, 0, vbo.getVertexCount());
|
||||
}
|
||||
renderContext.drawVbo(vbo);
|
||||
//LodRenderer.tickLogger.info("Vertex buffer: {}", vbo);
|
||||
}
|
||||
return hasRendered;
|
||||
@@ -164,7 +160,7 @@ public class ColumnRenderBuffer extends RenderBuffer {
|
||||
for (GLVertexBuffer b : vbos) {
|
||||
if (b == null) continue;
|
||||
statsMap.incStat("VBOs");
|
||||
if (b.getSize() == LodBufferBuilderFactory.FULL_SIZED_BUFFER) {
|
||||
if (b.getSize() == FULL_SIZED_BUFFER) {
|
||||
statsMap.incStat("FullsizedVBOs");
|
||||
}
|
||||
if (b.getSize() == 0) GL_LOGGER.warn("VBO with size 0");
|
||||
@@ -211,7 +207,10 @@ public class ColumnRenderBuffer extends RenderBuffer {
|
||||
EGpuUploadMethod method = GLProxy.getInstance().getGpuUploadMethod();
|
||||
EGLProxyContext oldContext = glProxy.getGlContext();
|
||||
glProxy.setGlContext(EGLProxyContext.LOD_BUILDER);
|
||||
ColumnRenderBuffer buffer = usedBuffer!=null ? usedBuffer : new ColumnRenderBuffer();
|
||||
ColumnRenderBuffer buffer = usedBuffer!=null ? usedBuffer :
|
||||
new ColumnRenderBuffer(
|
||||
new DHBlockPos(data.sectionPos.getCorner().getCorner(), clientLevel.getMinY())
|
||||
);
|
||||
try {
|
||||
buffer.uploadBuffer(builder, method);
|
||||
EVENT_LOGGER.trace("RenderRegion end Upload @ {}", data.sectionPos);
|
||||
|
||||
@@ -5,7 +5,7 @@ import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
@@ -77,5 +77,27 @@ public class IdBiomeBlockStateMap {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
//TODO: Serialization & Deserialization
|
||||
void serialize(OutputStream os) {
|
||||
try (DataOutputStream dos = new DataOutputStream(os)) {
|
||||
dos.writeInt(entries.size());
|
||||
for (Entry e : entries) {
|
||||
dos.writeUTF(e.serialize());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
static IdBiomeBlockStateMap deserialize(InputStream is) {
|
||||
try (DataInputStream dis = new DataInputStream(is)) {
|
||||
int size = dis.readInt();
|
||||
IdBiomeBlockStateMap map = new IdBiomeBlockStateMap();
|
||||
for (int i = 0; i < size; i++) {
|
||||
map.entries.add(Entry.deserialize(dis.readUTF()));
|
||||
}
|
||||
return map;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.seibel.lod.core.config.Config;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.render.a7LodRenderer;
|
||||
import com.seibel.lod.core.a7.render.a7LodRenderer;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
|
||||
@@ -11,7 +11,7 @@ import com.seibel.lod.core.config.Config;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.render.a7LodRenderer;
|
||||
import com.seibel.lod.core.a7.render.a7LodRenderer;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.seibel.lod.core.a7.level;
|
||||
|
||||
import com.seibel.lod.core.a7.datatype.full.ChunkSizedData;
|
||||
import com.seibel.lod.core.a7.pos.DhBlockPos2D;
|
||||
import com.seibel.lod.core.a7.pos.DhSectionPos;
|
||||
import com.seibel.lod.core.a7.util.FileScanner;
|
||||
import com.seibel.lod.core.a7.save.io.file.LocalDataFileHandler;
|
||||
import com.seibel.lod.core.a7.save.structure.LocalSaveStructure;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.objects.DHChunkPos;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@@ -52,6 +56,13 @@ public class DhServerLevel implements IServerLevel {
|
||||
public void doWorldGen() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void submitChunkData(DHChunkPos chunkPos, ChunkSizedData data) {
|
||||
DhSectionPos sectionPos = new DhSectionPos((byte)4, chunkPos.x, chunkPos.z);
|
||||
dataFileHandler.write(sectionPos, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILevelWrapper getLevelWrapper() {
|
||||
return level;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.seibel.lod.core.a7.level;
|
||||
|
||||
import com.seibel.lod.core.a7.datatype.full.ChunkSizedData;
|
||||
|
||||
public interface IServerLevel extends ILevel {
|
||||
void serverTick();
|
||||
void doWorldGen();
|
||||
void submitChunkData(ChunkSizedData data);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package com.seibel.lod.core.a7.render;
|
||||
|
||||
import com.seibel.lod.core.render.LodRenderProgram;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.StatsMap;
|
||||
|
||||
public abstract class RenderBuffer implements AutoCloseable
|
||||
@@ -31,7 +32,7 @@ public abstract class RenderBuffer implements AutoCloseable
|
||||
// ========== Called by render thread ==========
|
||||
/* Called on... well... rendering.
|
||||
* Return false if nothing rendered. (Optional) */
|
||||
public abstract boolean render(LodRenderProgram shaderProgram);
|
||||
public abstract boolean render(a7LodRenderer renderContext);
|
||||
|
||||
// ========== Called by any thread. (thread safe) ==========
|
||||
|
||||
@@ -47,6 +48,14 @@ public abstract class RenderBuffer implements AutoCloseable
|
||||
* thread), or by others when the object is not being used. (not in build,
|
||||
* upload, or render state). */
|
||||
public abstract void close();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static final int DEFAULT_MEMORY_ALLOCATION = (LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 3) * 8;
|
||||
public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4;
|
||||
public static final int MAX_QUADS_PER_BUFFER = (1024 * 1024 * 1) / QUADS_BYTE_SIZE;
|
||||
public static final int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public class RenderBufferHandler {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
public void render(LodRenderProgram renderContext) {
|
||||
public void render(a7LodRenderer renderContext) {
|
||||
RenderBuffer buff = renderBufferSlot.get();
|
||||
if (buff != null) {
|
||||
buff.render(renderContext);
|
||||
@@ -109,7 +109,7 @@ public class RenderBufferHandler {
|
||||
renderBufferNodes = new MovableGridRingList<>(referenceList.getHalfSize(), center);
|
||||
}
|
||||
|
||||
public void render(LodRenderProgram renderContext) {
|
||||
public void render(a7LodRenderer renderContext) {
|
||||
//TODO: This might get locked by update() causing move() call. Is there a way to avoid this?
|
||||
// Maybe dupe the base list and use atomic swap on render? Or is this not worth it?
|
||||
//TODO: Directional culling
|
||||
|
||||
+19
-5
@@ -17,11 +17,10 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.render;
|
||||
package com.seibel.lod.core.a7.render;
|
||||
|
||||
import com.seibel.lod.core.a7.level.IClientLevel;
|
||||
import com.seibel.lod.core.config.Config;
|
||||
import com.seibel.lod.core.builders.lodBuilding.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.config.types.ConfigEntry;
|
||||
import com.seibel.lod.core.enums.rendering.EDebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.EFogColorMode;
|
||||
@@ -29,11 +28,14 @@ import com.seibel.lod.core.handlers.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.lod.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.lod.core.logging.ConfigBasedSpamLogger;
|
||||
import com.seibel.lod.core.objects.DHBlockPos;
|
||||
import com.seibel.lod.core.a7.render.RenderBufferHandler;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.objects.math.Vec3d;
|
||||
import com.seibel.lod.core.objects.math.Vec3f;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodFogConfig;
|
||||
import com.seibel.lod.core.render.LodRenderProgram;
|
||||
import com.seibel.lod.core.render.objects.GLState;
|
||||
import com.seibel.lod.core.render.objects.GLVertexBuffer;
|
||||
import com.seibel.lod.core.render.objects.QuadElementBuffer;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
@@ -66,6 +68,18 @@ public class a7LodRenderer
|
||||
public static final long DRAW_LAG_SPIKE_THRESHOLD_NS = TimeUnit.NANOSECONDS.convert(20, TimeUnit.MILLISECONDS);
|
||||
|
||||
public static final boolean ENABLE_IBO = true;
|
||||
|
||||
public void setupOffset(DHBlockPos pos) {
|
||||
shaderProgram.setModelPos(new Vec3f(pos.x, pos.y, pos.z));
|
||||
}
|
||||
|
||||
public void drawVbo(GLVertexBuffer vbo) {
|
||||
vbo.bind();
|
||||
shaderProgram.bindVertexBuffer(vbo.getId());
|
||||
GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount()/4)*6,
|
||||
quadIBO.getType(), 0);
|
||||
}
|
||||
|
||||
public static class LagSpikeCatcher {
|
||||
long timer = System.nanoTime();
|
||||
public LagSpikeCatcher() {}
|
||||
@@ -219,7 +233,7 @@ public class a7LodRenderer
|
||||
int drawCount = 0;
|
||||
|
||||
//TODO: Directional culling
|
||||
bufferHandler.render(shaderProgram);
|
||||
bufferHandler.render(this);
|
||||
|
||||
//if (drawCall==0)
|
||||
// tickLogger.info("DrawCall Count: {}", drawCount);
|
||||
@@ -267,7 +281,7 @@ public class a7LodRenderer
|
||||
shaderProgram = new LodRenderProgram(LodFogConfig.generateFogConfig());
|
||||
if (ENABLE_IBO) {
|
||||
quadIBO = new QuadElementBuffer();
|
||||
quadIBO.reserve(LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER);
|
||||
quadIBO.reserve(RenderBuffer.MAX_QUADS_PER_BUFFER);
|
||||
}
|
||||
EVENT_LOGGER.info("Renderer setup complete");
|
||||
}
|
||||
-1
@@ -20,7 +20,6 @@
|
||||
package com.seibel.lod.core.api.external.methods.config.client;
|
||||
|
||||
import com.seibel.lod.core.api.external.items.interfaces.config.IDhApiConfig;
|
||||
import com.seibel.lod.core.api.external.methods.config.objects.enums.*;
|
||||
import com.seibel.lod.core.api.external.items.enums.config.*;
|
||||
import com.seibel.lod.core.api.implementation.objects.GenericEnumConverter;
|
||||
import com.seibel.lod.core.api.implementation.objects.RenderModeEnabledConverter;
|
||||
|
||||
-1
@@ -20,7 +20,6 @@
|
||||
package com.seibel.lod.core.api.external.methods.config.client;
|
||||
|
||||
import com.seibel.lod.core.api.external.items.interfaces.config.IDhApiConfig;
|
||||
import com.seibel.lod.core.api.external.methods.config.objects.enums.*;
|
||||
import com.seibel.lod.core.api.external.items.enums.config.*;
|
||||
import com.seibel.lod.core.api.implementation.objects.GenericEnumConverter;
|
||||
import com.seibel.lod.core.api.implementation.wrappers.DhApiConfig;
|
||||
|
||||
@@ -1,569 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.lodBuilding;
|
||||
|
||||
#if ABC
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.core.config.Config;
|
||||
import com.seibel.lod.core.enums.ELodDirection;
|
||||
import com.seibel.lod.core.enums.config.EBlocksToAvoid;
|
||||
import com.seibel.lod.core.enums.config.EDistanceGenerationMode;
|
||||
import com.seibel.lod.core.handlers.dependencyInjection.SingletonHandler;
|
||||
import com.seibel.lod.core.logging.ConfigBasedLogger;
|
||||
import com.seibel.lod.core.objects.DHBlockPos;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.LodWorld;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockDetailWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
/**
|
||||
* This object is in charge of creating Lod related objects.
|
||||
*
|
||||
* @author Cola
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 12-11-2021
|
||||
*/
|
||||
@SuppressWarnings("GrazieInspection")
|
||||
public class LodBuilder
|
||||
{
|
||||
private static final IMinecraftClientWrapper MC = SingletonHandler.get(IMinecraftClientWrapper.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
public static final ConfigBasedLogger EVENT_LOGGER = new ConfigBasedLogger(LogManager.getLogger(LodBuilder.class),
|
||||
() -> Config.Client.Advanced.Debugging.DebugSwitch.logLodBuilderEvent.get());
|
||||
|
||||
/** This cannot be final! Different world have different height, and in menu, this causes Null Exceptions*/
|
||||
//public static final short MIN_WORLD_HEIGHT = MC.getWrappedClientWorld().getMinHeight();
|
||||
public static short MIN_WORLD_HEIGHT = 0; // Currently modified in EventApi.onWorldLoaded(...)
|
||||
/** Minecraft's max light value */
|
||||
public static final short DEFAULT_MAX_LIGHT = 15;
|
||||
|
||||
//public static final ExecutorService lodGenThreadPool = Executors.newFixedThreadPool(8, new ThreadFactoryBuilder().setNameFormat("Lod-Builder-%d").build());
|
||||
|
||||
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(
|
||||
new LodThreadFactory(this.getClass().getSimpleName(), Thread.NORM_PRIORITY-1));
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* How wide LodDimensions should be in regions <br>
|
||||
* Is automatically set before the first frame in ClientProxy.
|
||||
*/
|
||||
public int defaultDimensionWidthInRegions = 1;
|
||||
|
||||
//public static final boolean useExperimentalLighting = true;
|
||||
|
||||
|
||||
|
||||
|
||||
public LodBuilder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim, boolean genAll)
|
||||
{
|
||||
// Block change event
|
||||
generateLodNodeAsync(chunk, lodWorld, dim, EDistanceGenerationMode.FULL, true, genAll, ()->{},
|
||||
()->{generateLodNodeAsync(chunk,lodWorld,dim, genAll);});
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim,
|
||||
EDistanceGenerationMode generationMode, boolean override, boolean genAll, Runnable endCallback, Runnable retryCallback)
|
||||
{
|
||||
if (lodWorld == null || lodWorld.getIsWorldNotLoaded()) {
|
||||
endCallback.run();
|
||||
return;
|
||||
}
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
// given a valid chunk object
|
||||
if (chunk == null) {
|
||||
endCallback.run();
|
||||
return;
|
||||
}
|
||||
|
||||
Runnable thread = () ->
|
||||
{
|
||||
boolean retryNeeded = false;
|
||||
try
|
||||
{
|
||||
// we need a loaded client world in order to
|
||||
// get the textures for blocks
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
return;
|
||||
|
||||
// don't try to generate LODs if the user isn't in the world anymore
|
||||
// (this happens a lot when the user leaves a world/server)
|
||||
if (!MC.hasSinglePlayerServer() && !MC.connectedToServer())
|
||||
return;
|
||||
|
||||
// make sure the dimension exists
|
||||
// if not, it prob means that player left
|
||||
LodDimension lodDim = lodWorld.getLodDimension(dim);
|
||||
if (lodDim == null) return;
|
||||
|
||||
retryNeeded = !generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode), override, genAll);
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
EVENT_LOGGER.error("LodBuilder Thread Uncaught Exception: ", e);
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
} finally {
|
||||
if (!retryNeeded)
|
||||
endCallback.run();
|
||||
else
|
||||
retryCallback.run();
|
||||
}
|
||||
};
|
||||
lodGenThreadPool.execute(thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LodNode for a chunk in the given world.
|
||||
* @throws IllegalArgumentException thrown if either the chunk or world is null.
|
||||
*/
|
||||
public boolean generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk, LodBuilderConfig config, boolean override, boolean genAll)
|
||||
{
|
||||
try {
|
||||
if (chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
LodRegion region = lodDim.getRegion(chunk.getRegionPosX(), chunk.getRegionPosZ());
|
||||
if (region == null)
|
||||
return false;
|
||||
// this happens if a LOD is generated after the user leaves the world.
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
return false;
|
||||
if (!canGenerateLodFromChunk(chunk))
|
||||
return false;
|
||||
|
||||
|
||||
// generate the LODs
|
||||
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte)0);
|
||||
long[] data = new long[maxVerticalData*16*16];
|
||||
boolean isAllVoid = true;
|
||||
|
||||
if (!config.quickFillWithVoid) {
|
||||
for (int i = 0; i < 16*16; i++)
|
||||
{
|
||||
int subX = i/16;
|
||||
int subZ = i%16;
|
||||
writeVerticalData(data, i*maxVerticalData, maxVerticalData, chunk, config, subX, subZ);
|
||||
isAllVoid &= DataPointUtil.isVoid(data[i*maxVerticalData]);
|
||||
if (!DataPointUtil.doesItExist(data[i*maxVerticalData]))
|
||||
throw new RuntimeException("writeVerticalData result: Datapoint does not exist at "+ chunk.getMinX()+subX +", "+ chunk.getMinZ()+subZ);
|
||||
if (DataPointUtil.getGenerationMode(data[i*maxVerticalData]) != config.distanceGenerationMode.complexity)
|
||||
throw new RuntimeException("writeVerticalData result: Datapoint invalid at "+ chunk.getMinX()+subX +", "+ chunk.getMinZ()+subZ);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 16*16; i++)
|
||||
{
|
||||
data[i*maxVerticalData] = DataPointUtil.createVoidDataPoint(config.distanceGenerationMode.complexity);
|
||||
}
|
||||
}
|
||||
if (isAllVoid) EVENT_LOGGER.debug("The chunk {} is completely void.", chunk);
|
||||
|
||||
// This MUST be done after the data is generated, to ensure that during the generation, the data is valid.
|
||||
if (!canGenerateLodFromChunk(chunk)) // TODO Why are we calling this again? - James
|
||||
return false; // Answer: Because concurrency change may cause the chunk to have invalid data, like light.
|
||||
|
||||
if (genAll) {
|
||||
return writeAllLodNodeData(lodDim, region, chunk.getChunkPosX(), chunk.getChunkPosZ(), data, config, override);
|
||||
} else {
|
||||
return writePartialLodNodeData(lodDim, region, chunk.getChunkPosX(), chunk.getChunkPosZ(), data, config, override);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
EVENT_LOGGER.error("LodBuilder encountered an error on building lod: ", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public static boolean canGenerateLodFromChunk(IChunkWrapper chunk)
|
||||
{
|
||||
return chunk != null && chunk.isLightCorrect() && chunk.doesNearbyChunksExist();
|
||||
}
|
||||
|
||||
private boolean writeAllLodNodeData(LodDimension lodDim, LodRegion region, int chunkX, int chunkZ,
|
||||
long[] data, LodBuilderConfig config, boolean override)
|
||||
{
|
||||
region.isWriting.incrementAndGet();
|
||||
try {
|
||||
if (region.getMinDetailLevel()!= 0) {
|
||||
if (!LodUtil.checkRamUsage(0.05, 16)) {
|
||||
EVENT_LOGGER.debug("LodBuilder: Not enough RAM available for loading files to build lods! Returning...");
|
||||
return false;
|
||||
}
|
||||
|
||||
LodRegion newRegion = lodDim.getRegionFromFile(region, (byte)0, region.getVerticalQuality());
|
||||
if (region!=newRegion)
|
||||
throw new RuntimeException();
|
||||
}
|
||||
//ApiShared.LOGGER.info("Generate chunk: {}, {} ({}, {}) at genMode {}",
|
||||
// chunk.getChunkPosX(), chunk.getChunkPosZ(), chunk.getMinX(), chunk.getMinZ(), config.distanceGenerationMode);
|
||||
region.addChunkOfData((byte)0, chunkX*16, chunkZ*16, 16, 16, data, data.length/16/16, override);
|
||||
region.regenerateLodFromArea((byte)0, chunkX*16, chunkZ*16, 16, 16);
|
||||
|
||||
if (!region.doesDataExist((byte)0, chunkX*16, chunkZ*16, config.distanceGenerationMode))
|
||||
throw new RuntimeException("data at detail 0 is still null after writes to it!");
|
||||
if (!region.doesDataExist(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ, config.distanceGenerationMode))
|
||||
throw new RuntimeException("data at chunk detail level is still null after writes to it!");
|
||||
} finally {
|
||||
region.isWriting.decrementAndGet();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean writePartialLodNodeData(LodDimension lodDim, LodRegion region, int chunkX, int chunkZ,
|
||||
long[] data, LodBuilderConfig config, boolean override)
|
||||
{
|
||||
region.isWriting.incrementAndGet();
|
||||
try {
|
||||
byte targetLevel = region.getMinDetailLevel();
|
||||
int vertQual = DetailDistanceUtil.getMaxVerticalData(targetLevel);
|
||||
int lodCount = (targetLevel >= LodUtil.CHUNK_DETAIL_LEVEL) ?
|
||||
1 : 1 << (LodUtil.CHUNK_DETAIL_LEVEL - targetLevel);
|
||||
if (targetLevel != 0) {
|
||||
int lodWidth = 16/lodCount;
|
||||
int inputVertQual = data.length/16/16;
|
||||
long[] mergedData = new long[vertQual*lodCount*lodCount];
|
||||
for (int subX=0; subX<lodCount; subX++) {
|
||||
for (int subZ=0; subZ<lodCount; subZ++) {
|
||||
long[] toBeMerged = DataPointUtil.extractDataArray(
|
||||
data, 16, 16, subX*lodWidth, subZ*lodWidth, lodWidth, lodWidth);
|
||||
if(toBeMerged.length != lodWidth*lodWidth*inputVertQual) throw new RuntimeException();
|
||||
long[] merged = DataPointUtil.mergeMultiData(toBeMerged, inputVertQual, vertQual);
|
||||
if (merged.length != vertQual) throw new RuntimeException();
|
||||
if (!DataPointUtil.doesItExist(merged[0]) ||
|
||||
DataPointUtil.getGenerationMode(merged[0]) != config.distanceGenerationMode.complexity)
|
||||
throw new RuntimeException();
|
||||
System.arraycopy(merged, 0, mergedData, (subZ+subX*lodCount)*vertQual, vertQual);
|
||||
}
|
||||
}
|
||||
data = mergedData;
|
||||
}
|
||||
if (lodCount*lodCount*vertQual != data.length) throw new RuntimeException();
|
||||
for (int i=0; i<data.length; i+=vertQual) {
|
||||
if (!DataPointUtil.doesItExist(data[i]) ||
|
||||
DataPointUtil.getGenerationMode(data[i]) != config.distanceGenerationMode.complexity) {
|
||||
EVENT_LOGGER.error("NULL data at {}, detail {}, vertQual {}, lodCount {}, chunkPos [{},{}]\n"
|
||||
+ "Data: {}",
|
||||
i, targetLevel, vertQual, lodCount, chunkX, chunkZ, DataPointUtil.toString(data[i]));
|
||||
throw new RuntimeException("Null data!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//ApiShared.LOGGER.info("Generate chunk: {}, {} ({}, {}) at genMode {}",
|
||||
// chunk.getChunkPosX(), chunk.getChunkPosZ(), chunk.getMinX(), chunk.getMinZ(), config.distanceGenerationMode);
|
||||
if (targetLevel != region.getMinDetailLevel()) {
|
||||
//Concurrency issues happened.
|
||||
throw new ConcurrentModificationException("Min detail level changed while writing data");
|
||||
}
|
||||
region.addChunkOfData(targetLevel,
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel),
|
||||
lodCount, lodCount, data, vertQual, override);
|
||||
region.regenerateLodFromArea(targetLevel,
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel),
|
||||
lodCount, lodCount);
|
||||
|
||||
if (!region.doesDataExist(targetLevel,
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, targetLevel),
|
||||
LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, chunkZ, targetLevel),
|
||||
config.distanceGenerationMode))
|
||||
throw new RuntimeException("data at detail "+ targetLevel+" is still null after writes to it!");
|
||||
} catch (Exception e) {
|
||||
EVENT_LOGGER.error("LodBuilder encountered an error on writePartialLodNodeData: ", e);
|
||||
} finally {
|
||||
region.isWriting.decrementAndGet();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** creates a vertical DataPoint */
|
||||
private void writeVerticalData(long[] data, int dataOffset, int maxVerticalData,
|
||||
IChunkWrapper chunk, LodBuilderConfig config, int chunkSubPosX, int chunkSubPosZ)
|
||||
{
|
||||
|
||||
int totalVerticalData = (chunk.getHeight());
|
||||
long[] dataToMerge = new long[totalVerticalData];
|
||||
|
||||
boolean hasCeiling = MC.getWrappedClientWorld().getDimensionType().hasCeiling();
|
||||
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
|
||||
byte generation = config.distanceGenerationMode.complexity;
|
||||
int count = 0;
|
||||
// FIXME: This yAbs is just messy!
|
||||
int x = chunk.getMinX() + chunkSubPosX;
|
||||
int z = chunk.getMinZ() + chunkSubPosZ;
|
||||
int y = chunk.getMaxY(x, z);
|
||||
|
||||
boolean topBlock = true;
|
||||
if (y < chunk.getMinBuildHeight())
|
||||
dataToMerge[0] = DataPointUtil.createVoidDataPoint(generation);
|
||||
int maxConnectedLods = Config.Client.Graphics.Quality.verticalQuality.get().maxVerticalData[0];
|
||||
while (y >= chunk.getMinBuildHeight()) {
|
||||
int height = determineHeightPointFrom(chunk, config, x, y, z);
|
||||
// If the lod is at the default height, it must be void data
|
||||
if (height < chunk.getMinBuildHeight()) {
|
||||
if (topBlock) dataToMerge[0] = DataPointUtil.createVoidDataPoint(generation);
|
||||
break;
|
||||
}
|
||||
y = height - 1;
|
||||
// We search light on above air block
|
||||
int depth = determineBottomPointFrom(chunk, config, x, y, z,
|
||||
count < maxConnectedLods && (!hasCeiling || !topBlock));
|
||||
if (hasCeiling && topBlock)
|
||||
y = depth;
|
||||
int light = getLightValue(chunk, x, y, z, hasCeiling, hasSkyLight, topBlock);
|
||||
int color = generateLodColor(chunk, config, x, y, z);
|
||||
int lightBlock = light & 0b1111;
|
||||
int lightSky = (light >> 4) & 0b1111;
|
||||
dataToMerge[count] = DataPointUtil.createDataPoint(height-chunk.getMinBuildHeight(), depth-chunk.getMinBuildHeight(),
|
||||
color, lightSky, lightBlock, generation);
|
||||
topBlock = false;
|
||||
y = depth - 1;
|
||||
count++;
|
||||
}
|
||||
long[] result = DataPointUtil.mergeMultiData(dataToMerge, totalVerticalData, maxVerticalData);
|
||||
if (result.length != maxVerticalData) throw new ArrayIndexOutOfBoundsException();
|
||||
System.arraycopy(result, 0, data, dataOffset, maxVerticalData);
|
||||
}
|
||||
|
||||
public static final ELodDirection[] DIRECTIONS = new ELodDirection[] {
|
||||
ELodDirection.UP,
|
||||
ELodDirection.DOWN,
|
||||
ELodDirection.WEST,
|
||||
ELodDirection.EAST,
|
||||
ELodDirection.NORTH,
|
||||
ELodDirection.SOUTH };
|
||||
|
||||
private boolean hasCliffFace(IChunkWrapper chunk, int x, int y, int z) {
|
||||
for (ELodDirection dir : DIRECTIONS) {
|
||||
IBlockDetailWrapper block = chunk.getBlockDetailAtFace(x, y, z, dir);
|
||||
if (block == null || !block.hasFaceCullingFor(ELodDirection.OPPOSITE_DIRECTIONS[dir.ordinal()]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
* Used when creating a vertical LOD.
|
||||
*/
|
||||
private int determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig builderConfig, int xAbs, int yAbs, int zAbs, boolean strictEdge)
|
||||
{
|
||||
int depth = chunk.getMinBuildHeight();
|
||||
IBlockDetailWrapper currentBlockDetail = null;
|
||||
if (strictEdge)
|
||||
{
|
||||
IBlockDetailWrapper blockAbove = chunk.getBlockDetail(xAbs, yAbs + 1, zAbs);
|
||||
if (blockAbove != null && Config.Client.WorldGenerator.tintWithAvoidedBlocks.get() && !blockAbove.shouldRender(Config.Client.WorldGenerator.blocksToAvoid.get()))
|
||||
{ // The above block is skipped. Lets use its skipped color for current block
|
||||
currentBlockDetail = blockAbove;
|
||||
}
|
||||
if (currentBlockDetail == null) currentBlockDetail = chunk.getBlockDetail(xAbs, yAbs, zAbs);
|
||||
}
|
||||
|
||||
for (int y = yAbs - 1; y >= chunk.getMinBuildHeight(); y--)
|
||||
{
|
||||
IBlockDetailWrapper nextBlock = chunk.getBlockDetail(xAbs, y, zAbs);
|
||||
if (isLayerValidLodPoint(nextBlock)) {
|
||||
if (!strictEdge) continue;
|
||||
if (currentBlockDetail.equals(nextBlock)) continue;
|
||||
if (!hasCliffFace(chunk, xAbs, y, zAbs)) continue;
|
||||
}
|
||||
depth = (y + 1);
|
||||
break;
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/** Find the highest valid point from the Top */
|
||||
private int determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs)
|
||||
{
|
||||
//TODO find a way to skip bottom of the world
|
||||
int height = chunk.getMinBuildHeight()-1;
|
||||
for (int y = yAbs; y >= chunk.getMinBuildHeight(); y--)
|
||||
{
|
||||
if (isLayerValidLodPoint(chunk, xAbs, y, zAbs))
|
||||
{
|
||||
height = (y + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// =====================//
|
||||
// constructor helpers //
|
||||
// =====================//
|
||||
|
||||
/**
|
||||
* Generate the color for the given chunk using biome water color, foliage
|
||||
* color, and grass color.
|
||||
*/
|
||||
private int generateLodColor(IChunkWrapper chunk, LodBuilderConfig builderConfig, int x, int y, int z)
|
||||
{
|
||||
int colorInt;
|
||||
if (builderConfig.useBiomeColors)
|
||||
{
|
||||
// I have no idea why I need to bit shift to the right, but
|
||||
// if I don't the biomes don't show up correctly.
|
||||
colorInt = chunk.getBiome(x, y, z).getColorForBiome(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are skipping non-full and non-solid blocks that means we ignore
|
||||
// snow, flowers, etc. Get the above block so we can still get the color
|
||||
// of the snow, flower, etc. that may be above this block
|
||||
colorInt = 0;
|
||||
if (chunk.blockPosInsideChunk(x, y+1, z)) {
|
||||
IBlockDetailWrapper blockAbove = chunk.getBlockDetail(x, y+1, z);
|
||||
if (blockAbove != null && Config.Client.WorldGenerator.tintWithAvoidedBlocks.get() && !blockAbove.shouldRender(Config.Client.WorldGenerator.blocksToAvoid.get()))
|
||||
{ // The above block is skipped. Lets use its skipped color for current block
|
||||
colorInt = blockAbove.getAndResolveFaceColor(null, chunk, new DHBlockPos(x, y+1, z));
|
||||
}
|
||||
}
|
||||
|
||||
// override this block's color if there was a block above this
|
||||
// and we were avoiding non-full/non-solid blocks
|
||||
if (colorInt == 0) {
|
||||
IBlockDetailWrapper detail = chunk.getBlockDetail(x, y, z);
|
||||
colorInt = detail.getAndResolveFaceColor(null, chunk, new DHBlockPos(x, y, z));
|
||||
}
|
||||
}
|
||||
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
/** Gets the light value for the given block position */
|
||||
private int getLightValue(IChunkWrapper chunk, int x, int y, int z, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
|
||||
{
|
||||
int skyLight;
|
||||
int blockLight;
|
||||
|
||||
int blockBrightness = chunk.getEmittedBrightness(x, y, z);
|
||||
// get the air block above or below this block
|
||||
if (hasCeiling && topBlock)
|
||||
y--;
|
||||
else
|
||||
y++;
|
||||
|
||||
blockLight = chunk.getBlockLight(x, y, z);
|
||||
skyLight = hasSkyLight ? chunk.getSkyLight(x, y, z) : 0;
|
||||
|
||||
if (blockLight == -1 || skyLight == -1)
|
||||
{
|
||||
|
||||
ILevelWrapper world = MC.getWrappedServerWorld();
|
||||
|
||||
if (world != null)
|
||||
{
|
||||
// server world sky light (always accurate)
|
||||
blockLight = world.getBlockLight(x, y, z);
|
||||
|
||||
if (topBlock && !hasCeiling && hasSkyLight)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
skyLight = hasSkyLight ? world.getSkyLight(x, y, z) : 0;
|
||||
|
||||
if (!topBlock && skyLight == 15)
|
||||
{
|
||||
// we are on predicted terrain, and we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
skyLight = 12;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
world = MC.getWrappedClientWorld();
|
||||
if (world == null)
|
||||
{
|
||||
blockLight = 0;
|
||||
skyLight = 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
// client world sky light (almost never accurate)
|
||||
blockLight = world.getBlockLight(x, y, z);
|
||||
// estimate what the lighting should be
|
||||
if (hasSkyLight || !hasCeiling)
|
||||
{
|
||||
if (topBlock)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
{
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(x, y, z);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
|
||||
{
|
||||
// we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
skyLight = 12;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blockLight = LodUtil.clamp(0, Math.max(blockLight, blockBrightness), DEFAULT_MAX_LIGHT);
|
||||
return blockLight + (skyLight << 4);
|
||||
}
|
||||
|
||||
/** Is the block at the given blockPos a valid LOD point? */
|
||||
private boolean isLayerValidLodPoint(IBlockDetailWrapper blockDetail)
|
||||
{
|
||||
EBlocksToAvoid avoid = Config.Client.WorldGenerator.blocksToAvoid.get();
|
||||
return blockDetail != null && blockDetail.shouldRender(avoid);
|
||||
}
|
||||
|
||||
/** Is the block at the given blockPos a valid LOD point? */
|
||||
private boolean isLayerValidLodPoint(IChunkWrapper chunk, int x, int y, int z)
|
||||
{
|
||||
EBlocksToAvoid avoid = Config.Client.WorldGenerator.blocksToAvoid.get();
|
||||
IBlockDetailWrapper block = chunk.getBlockDetail(x, y, z);
|
||||
return block != null && block.shouldRender(avoid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
+17
-78
@@ -25,6 +25,7 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.seibel.lod.core.a7.render.RenderBuffer;
|
||||
import com.seibel.lod.core.enums.ELodDirection;
|
||||
import com.seibel.lod.core.enums.ELodDirection.Axis;
|
||||
import com.seibel.lod.core.enums.config.EGpuUploadMethod;
|
||||
@@ -34,6 +35,8 @@ import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
//TODO: Recheck this class for refactoring
|
||||
|
||||
/**
|
||||
* Used to create the quads before they are converted to renderable buffers.
|
||||
*
|
||||
@@ -94,80 +97,16 @@ public class LodQuadBuilder
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
};
|
||||
public static final int[][][] DIRECTION_VERTEX_QUAD = new int[][][]
|
||||
{
|
||||
// X,Z //
|
||||
{ // UP
|
||||
{ 1, 0 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 0, 1 }, // 2
|
||||
|
||||
{ 1, 0 }, // 0
|
||||
{ 0, 1 }, // 2
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
{ // DOWN
|
||||
{ 0, 0 }, // 0
|
||||
{ 0, 1 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 1 }, // 2
|
||||
{ 1, 0 }, // 3
|
||||
},
|
||||
|
||||
// X,Y //
|
||||
{ // NORTH
|
||||
{ 0, 0 }, // 0
|
||||
{ 0, 1 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 1 }, // 2
|
||||
{ 1, 0 }, // 3
|
||||
},
|
||||
{ // SOUTH
|
||||
{ 1, 0 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 0, 1 }, // 2
|
||||
|
||||
{ 1, 0 }, // 0
|
||||
{ 0, 1 }, // 2
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
|
||||
// Z,Y //
|
||||
{ // WEST
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 0 }, // 1
|
||||
{ 1, 1 }, // 2
|
||||
|
||||
{ 0, 0 }, // 0
|
||||
{ 1, 1 }, // 2
|
||||
{ 0, 1 }, // 3
|
||||
},
|
||||
{ // EAST
|
||||
{ 0, 1 }, // 0
|
||||
{ 1, 1 }, // 1
|
||||
{ 1, 0 }, // 2
|
||||
|
||||
{ 0, 1 }, // 0
|
||||
{ 1, 0 }, // 2
|
||||
{ 0, 0 }, // 3
|
||||
},
|
||||
};
|
||||
|
||||
private int premergeCount = 0;
|
||||
private final boolean useIBO;
|
||||
|
||||
public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean useIBO)
|
||||
public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow)
|
||||
{
|
||||
for (int i = 0; i < 6; i++)
|
||||
quads[i] = new ArrayList<>();
|
||||
|
||||
this.skipQuadsWithZeroSkylight = enableSkylightCulling;
|
||||
this.skyLightCullingBelow = skyLightCullingBelow;
|
||||
this.useIBO = useIBO;
|
||||
}
|
||||
|
||||
|
||||
@@ -259,9 +198,9 @@ public class LodQuadBuilder
|
||||
bb.put(a);
|
||||
}
|
||||
|
||||
private static void putQuad(boolean useIBO, ByteBuffer bb, BufferQuad quad)
|
||||
private static void putQuad(ByteBuffer bb, BufferQuad quad)
|
||||
{
|
||||
int[][] quadBase = useIBO ? DIRECTION_VERTEX_IBO_QUAD[quad.direction.ordinal()] : DIRECTION_VERTEX_QUAD[quad.direction.ordinal()];
|
||||
int[][] quadBase = DIRECTION_VERTEX_IBO_QUAD[quad.direction.ordinal()];
|
||||
short widthEastWest = quad.widthEastWest;
|
||||
short widthNorthSouth = quad.widthNorthSouthOrUpDown;
|
||||
Axis axis = quad.direction.getAxis();
|
||||
@@ -366,7 +305,7 @@ public class LodQuadBuilder
|
||||
{
|
||||
return new Iterator<ByteBuffer>()
|
||||
{
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(LodBufferBuilderFactory.FULL_SIZED_BUFFER)
|
||||
final ByteBuffer bb = ByteBuffer.allocateDirect(RenderBuffer.FULL_SIZED_BUFFER)
|
||||
.order(ByteOrder.nativeOrder());
|
||||
int dir = skipEmpty(0);
|
||||
int quad = 0;
|
||||
@@ -392,7 +331,7 @@ public class LodQuadBuilder
|
||||
return null;
|
||||
}
|
||||
bb.clear();
|
||||
bb.limit(LodBufferBuilderFactory.FULL_SIZED_BUFFER);
|
||||
bb.limit(RenderBuffer.FULL_SIZED_BUFFER);
|
||||
while (bb.hasRemaining() && dir < 6)
|
||||
{
|
||||
writeData();
|
||||
@@ -411,7 +350,7 @@ public class LodQuadBuilder
|
||||
{
|
||||
break;
|
||||
}
|
||||
putQuad(useIBO, bb, quads[dir].get(i));
|
||||
putQuad(bb, quads[dir].get(i));
|
||||
}
|
||||
|
||||
if (i >= quads[dir].size())
|
||||
@@ -450,26 +389,26 @@ public class LodQuadBuilder
|
||||
}
|
||||
|
||||
int numOfQuads = _countRemainingQuads();
|
||||
if (numOfQuads > LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER)
|
||||
numOfQuads = LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER;
|
||||
if (numOfQuads > RenderBuffer.MAX_QUADS_PER_BUFFER)
|
||||
numOfQuads = RenderBuffer.MAX_QUADS_PER_BUFFER;
|
||||
if (numOfQuads == 0)
|
||||
{
|
||||
vbo.setVertexCount(0);
|
||||
return false;
|
||||
}
|
||||
ByteBuffer bb = vbo.mapBuffer(numOfQuads * LodBufferBuilderFactory.QUADS_BYTE_SIZE, method,
|
||||
LodBufferBuilderFactory.FULL_SIZED_BUFFER);
|
||||
ByteBuffer bb = vbo.mapBuffer(numOfQuads * RenderBuffer.QUADS_BYTE_SIZE, method,
|
||||
RenderBuffer.FULL_SIZED_BUFFER);
|
||||
if (bb == null)
|
||||
throw new NullPointerException("mapBuffer returned null");
|
||||
bb.clear();
|
||||
bb.limit(numOfQuads * LodBufferBuilderFactory.QUADS_BYTE_SIZE);
|
||||
bb.limit(numOfQuads * RenderBuffer.QUADS_BYTE_SIZE);
|
||||
while (bb.hasRemaining() && dir < 6)
|
||||
{
|
||||
writeData(bb);
|
||||
}
|
||||
bb.rewind();
|
||||
vbo.unmapBuffer();
|
||||
vbo.setVertexCount(useIBO ? numOfQuads*4 : numOfQuads*6);
|
||||
vbo.setVertexCount(numOfQuads*4);
|
||||
return dir < 6;
|
||||
}
|
||||
|
||||
@@ -494,7 +433,7 @@ public class LodQuadBuilder
|
||||
{
|
||||
break;
|
||||
}
|
||||
putQuad(useIBO, bb, quads[dir].get(i));
|
||||
putQuad(bb, quads[dir].get(i));
|
||||
}
|
||||
|
||||
if (i >= quads[dir].size())
|
||||
@@ -525,7 +464,7 @@ public class LodQuadBuilder
|
||||
/** Returns how many Buffers will be needed to render everything in this builder. */
|
||||
public int getCurrentNeededVertexBufferCount()
|
||||
{
|
||||
return LodUtil.ceilDiv(getCurrentQuadsCount(), LodBufferBuilderFactory.MAX_QUADS_PER_BUFFER);
|
||||
return LodUtil.ceilDiv(getCurrentQuadsCount(), RenderBuffer.MAX_QUADS_PER_BUFFER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,8 @@ import java.nio.file.Path;
|
||||
* @version 2022-5-26
|
||||
*/
|
||||
public class ConfigFileHandling {
|
||||
public static final Path ConfigPath = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).getInstallationDirectory().toPath().resolve("config").resolve(ModInfo.NAME+".toml");
|
||||
public static final Path ConfigPath = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class)
|
||||
.getInstallationDirectory().toPath().resolve("config").resolve(ModInfo.NAME+".toml");
|
||||
|
||||
/** Saves the config to the file */
|
||||
public static void saveToFile() {
|
||||
|
||||
+1
-1
@@ -147,7 +147,7 @@ public class DependencyInjector<BindableType extends IBindable>
|
||||
* @see #get(Class, boolean)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends BindableType> T get(Class<? extends BindableType> interfaceClass) throws ClassCastException
|
||||
public <T extends BindableType> T get(Class<T> interfaceClass) throws ClassCastException
|
||||
{
|
||||
return (T) getInternalLogic(interfaceClass, false).get(0);
|
||||
}
|
||||
|
||||
+2
-2
@@ -103,7 +103,7 @@ public class OverrideInjector
|
||||
*
|
||||
* @see DependencyInjector#get(Class, boolean)
|
||||
*/
|
||||
public <T extends IDhApiOverrideable> T get(Class<? extends IDhApiOverrideable> interfaceClass) throws ClassCastException
|
||||
public <T extends IDhApiOverrideable> T get(Class<T> interfaceClass) throws ClassCastException
|
||||
{
|
||||
T value = primaryInjector.get(interfaceClass);
|
||||
if (value == null)
|
||||
@@ -126,7 +126,7 @@ public class OverrideInjector
|
||||
*
|
||||
* @see DependencyInjector#get(Class, boolean)
|
||||
*/
|
||||
public <T extends IDhApiOverrideable> T get(Class<? extends IDhApiOverrideable> interfaceClass, EApiOverridePriority overridePriority) throws ClassCastException
|
||||
public <T extends IDhApiOverrideable> T get(Class<T> interfaceClass, EApiOverridePriority overridePriority) throws ClassCastException
|
||||
{
|
||||
T value;
|
||||
if (overridePriority == EApiOverridePriority.PRIMARY)
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package com.seibel.lod.core.objects;
|
||||
|
||||
import com.seibel.lod.core.a7.pos.DhBlockPos2D;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class DHBlockPos {
|
||||
@@ -56,6 +58,10 @@ public class DHBlockPos {
|
||||
this(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
public DHBlockPos(DhBlockPos2D pos, int y) {
|
||||
this(pos.x, y, pos.z);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public int getX()
|
||||
{
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod (formerly the LOD Mod),
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2022 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.objects;
|
||||
|
||||
#if abc
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.seibel.lod.core.api.internal.InternalApiShared;
|
||||
import com.seibel.lod.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* Holds a levelPos that needs to be rendered.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-18-2021
|
||||
*/
|
||||
public class PosToRenderContainer
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
|
||||
public byte minDetail;
|
||||
private int regionPosX;
|
||||
private int regionPosZ;
|
||||
private int numberOfPosToRender;
|
||||
private int[] posToRender;
|
||||
private byte[][] population;
|
||||
|
||||
static class LodPos {
|
||||
byte detail;
|
||||
int posX;
|
||||
int posZ;
|
||||
}
|
||||
|
||||
private LodPos[] lodPosList;
|
||||
|
||||
public PosToRenderContainer(byte minDetail, int regionPosX, int regionPosZ)
|
||||
{
|
||||
this.minDetail = minDetail;
|
||||
this.numberOfPosToRender = 0;
|
||||
this.regionPosX = regionPosX;
|
||||
this.regionPosZ = regionPosZ;
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
|
||||
posToRender = new int[size * size * 3];
|
||||
population = new byte[size][size];
|
||||
lodPosList = new LodPos[size * size];
|
||||
}
|
||||
|
||||
public void addPosToRender(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
// When rapidly changing dimensions the bufferBuilder 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
|
||||
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);
|
||||
lodPosList[numberOfPosToRender] = new LodPos();
|
||||
lodPosList[numberOfPosToRender].detail = detailLevel;
|
||||
lodPosList[numberOfPosToRender].posX = posX;
|
||||
lodPosList[numberOfPosToRender].posZ = posZ;
|
||||
//posToRender[numberOfPosToRender * 3] = detailLevel;
|
||||
//posToRender[numberOfPosToRender * 3 + 1] = posX;
|
||||
//posToRender[numberOfPosToRender * 3 + 2] = posZ;
|
||||
numberOfPosToRender++;
|
||||
population[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posX, minDetail))]
|
||||
[LevelPosUtil.getRegionModule(minDetail, LevelPosUtil.convert(detailLevel, posZ, minDetail))] = (byte) (detailLevel + 1);
|
||||
}
|
||||
|
||||
public boolean contains(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
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;
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - minDetail);
|
||||
if (this.minDetail == minDetail)
|
||||
{
|
||||
Arrays.fill(posToRender, 0);
|
||||
for (byte[] bytes : population)
|
||||
Arrays.fill(bytes, (byte) 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.minDetail = minDetail;
|
||||
posToRender = new int[size * size * 3];
|
||||
population = new byte[size][size];
|
||||
}
|
||||
lodPosList = new LodPos[size * size];
|
||||
}
|
||||
|
||||
public int getNumberOfPos()
|
||||
{
|
||||
return numberOfPosToRender;
|
||||
}
|
||||
|
||||
public byte getNthDetailLevel(int n)
|
||||
{
|
||||
return lodPosList[n].detail;
|
||||
//return (byte) posToRender[n * 3];
|
||||
}
|
||||
|
||||
public int getNthPosX(int n)
|
||||
{
|
||||
return lodPosList[n].posX;
|
||||
//return posToRender[n * 3 + 1];
|
||||
}
|
||||
|
||||
public int getNthPosZ(int n)
|
||||
{
|
||||
return lodPosList[n].posZ;
|
||||
//return posToRender[n * 3 + 2];
|
||||
}
|
||||
|
||||
public void sort() {
|
||||
Arrays.sort(lodPosList, 0, numberOfPosToRender,
|
||||
(a,b) -> {
|
||||
if (a.detail != b.detail) return a.detail - b.detail;
|
||||
if (a.posX != b.posX) return a.posX - b.posX;
|
||||
return a.posZ - b.posZ;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@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 * 3]);
|
||||
builder.append(" ");
|
||||
builder.append(posToRender[i * 3 + 1]);
|
||||
builder.append(" ");
|
||||
builder.append(posToRender[i * 3 + 2]);
|
||||
builder.append('\n');
|
||||
}
|
||||
builder.append('\n');
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -318,17 +318,7 @@ public class LodRegion {
|
||||
}
|
||||
return (byte) Math.max(getMinDetailLevel(), renderLevel);
|
||||
}
|
||||
|
||||
public void getPosToRender(PosToRenderContainer posToRender, int playerPosX,
|
||||
int playerPosZ)
|
||||
{
|
||||
// use FAR_FIRST on local worlds and NEAR_FIRST on servers
|
||||
EGenerationPriority generationPriority = getResolvedGenerationPriority();
|
||||
|
||||
EDropoffQuality dropoffQuality = CONFIG.client().graphics().quality().getResolvedDropoffQuality();
|
||||
|
||||
getPosToRender(posToRender, playerPosX, playerPosZ, generationPriority, dropoffQuality);
|
||||
}
|
||||
|
||||
private EGenerationPriority getResolvedGenerationPriority() {
|
||||
EGenerationPriority priority = Config.Client.WorldGenerator.generationPriority.get();
|
||||
@@ -346,140 +336,7 @@ public class LodRegion {
|
||||
return dropoffQuality;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will fill the posToRender array with all levelPos that are
|
||||
* render-able.
|
||||
* <p>
|
||||
* TODO why don't we return the posToRender, it would make this easier to
|
||||
* understand
|
||||
*/
|
||||
private void getPosToRender(PosToRenderContainer posToRender, int playerPosX, int playerPosZ,
|
||||
EGenerationPriority priority, EDropoffQuality dropoffQuality) {
|
||||
double minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerPosX, playerPosZ);
|
||||
byte targetLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
|
||||
if (targetLevel <= dropoffQuality.fastModeSwitch) {
|
||||
getPosToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ,
|
||||
priority);
|
||||
} else {
|
||||
// FarModeSwitchLevel or above is the level where a giant block of lod is not acceptable even if not all child data exist.
|
||||
double centerDistance = LevelPosUtil.centerDistance(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ, playerPosX, playerPosZ);
|
||||
targetLevel = DetailDistanceUtil.getDetailLevelFromDistance(centerDistance);
|
||||
byte farModeSwitchLevel = (priority == EGenerationPriority.NEAR_FIRST) ? 0 :
|
||||
calculateFarModeSwitch(targetLevel);
|
||||
if (priority == EGenerationPriority.FAR_FIRST) farModeSwitchLevel = 8;
|
||||
getPosToRenderFlat(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, targetLevel, farModeSwitchLevel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will fill the posToRender array with all levelPos that are
|
||||
* render-able.
|
||||
* <p>
|
||||
* TODO why don't we return the posToRender, it would make this easier to
|
||||
* understand TODO this needs some more comments, James was only able to figure
|
||||
* out part of it
|
||||
*/
|
||||
private void getPosToRender(PosToRenderContainer posToRender, byte detailLevel, int offsetPosX, int offsetPosZ, int playerPosX,
|
||||
int playerPosZ, EGenerationPriority priority) {
|
||||
// equivalent to 2^(...)
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
|
||||
// calculate the LevelPos that are in range
|
||||
double minDistance = LevelPosUtil.minDistance(detailLevel, offsetPosX + regionPosX*size, offsetPosZ + regionPosZ*size, playerPosX, playerPosZ);
|
||||
byte minLevel = DetailDistanceUtil.getDetailLevelFromDistance(minDistance);
|
||||
// FarModeSwitchLevel or above is the level where a giant block of lod is not acceptable even if not all child data exist.
|
||||
byte farModeSwitchLevel = (priority == EGenerationPriority.NEAR_FIRST) ? 0 : calculateFarModeSwitch(minLevel);
|
||||
if (priority == EGenerationPriority.FAR_FIRST) farModeSwitchLevel = 8;
|
||||
|
||||
if (detailLevel <= minLevel) {
|
||||
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
|
||||
} else // case where (detailLevel > desiredLevel)
|
||||
{
|
||||
int childPosX = (offsetPosX + regionPosX*size) * 2;
|
||||
int childPosZ = (offsetPosZ + regionPosZ*size) * 2;
|
||||
byte childDetailLevel = (byte) (detailLevel - 1);
|
||||
|
||||
if (detailLevel > farModeSwitchLevel) {
|
||||
// Giant block is not acceptable. So leave empty void if data doesn't exist.
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, EDistanceGenerationMode.NONE)) {
|
||||
getPosToRender(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, playerPosX,
|
||||
playerPosZ, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Giant block is acceptable. So use this level lod if not all child data exist.
|
||||
int childrenCount = 0;
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, EDistanceGenerationMode.NONE)) {
|
||||
childrenCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If all the four children exist go deeper
|
||||
if (childrenCount == 4) {
|
||||
for (int x = 0; x <= 1; x++)
|
||||
for (int z = 0; z <= 1; z++)
|
||||
getPosToRender(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, playerPosX,
|
||||
playerPosZ, priority);
|
||||
} else {
|
||||
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will fill the posToRender array with all levelPos that are render-able,
|
||||
* but the entire region try to use the same detail level.
|
||||
*/
|
||||
private void getPosToRenderFlat(PosToRenderContainer posToRender, byte detailLevel, int offsetPosX, int offsetPosZ,
|
||||
byte targetLevel, byte farModeSwitchLevel) {
|
||||
// equivalent to 2^(...)
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
if (detailLevel == targetLevel) {
|
||||
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
|
||||
} else // case where (detailLevel > desiredLevel)
|
||||
{
|
||||
int childPosX = (offsetPosX + regionPosX*size) * 2;
|
||||
int childPosZ = (offsetPosZ + regionPosZ*size) * 2;
|
||||
byte childDetailLevel = (byte) (detailLevel - 1);
|
||||
|
||||
if (detailLevel > farModeSwitchLevel) {
|
||||
// Giant block is not acceptable. So leave empty void if data doesn't exist.
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, EDistanceGenerationMode.NONE)) {
|
||||
getPosToRenderFlat(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, targetLevel, farModeSwitchLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Giant block is acceptable. So use this level lod if not all child data exist.
|
||||
int childrenCount = 0;
|
||||
for (int x = 0; x <= 1; x++) {
|
||||
for (int z = 0; z <= 1; z++) {
|
||||
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z, EDistanceGenerationMode.RENDERABLE)) {
|
||||
childrenCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If all the four children exist go deeper
|
||||
if (childrenCount == 4) {
|
||||
for (int x = 0; x <= 1; x++)
|
||||
for (int z = 0; z <= 1; z++)
|
||||
getPosToRenderFlat(posToRender, childDetailLevel, offsetPosX*2 + x, offsetPosZ*2 + z, targetLevel, farModeSwitchLevel);
|
||||
} else {
|
||||
posToRender.addPosToRender(detailLevel, offsetPosX + regionPosX * size, offsetPosZ + regionPosZ * size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static final class LevelPos {
|
||||
public final byte detail;
|
||||
public final int posX;
|
||||
|
||||
Reference in New Issue
Block a user