Update core
This commit is contained in:
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core;
|
||||
|
||||
/**
|
||||
* This file is similar to mcmod.info
|
||||
* <br>
|
||||
* If you are looking at this mod's source code and don't
|
||||
* know where to start.
|
||||
* Go to the api/lod package (folder) and take a look at the ClientApi.java file,
|
||||
* Pretty much all of the mod stems from there.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public final class ModInfo
|
||||
{
|
||||
public static final String ID = "lod";
|
||||
/** The internal mod name */
|
||||
public static final String NAME = "DistantHorizons";
|
||||
/** Human readable version of NAME */
|
||||
public static final String READABLE_NAME = "Distant Horizons";
|
||||
public static final String API = "LodAPI";
|
||||
public static final String VERSION = "a1.5.3";
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.api;
|
||||
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.objects.lod.LodWorld;
|
||||
|
||||
/**
|
||||
* This stores objects and variables that
|
||||
* are shared between the different Core api classes.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class ApiShared
|
||||
{
|
||||
public ApiShared INSTANCE = new ApiShared();
|
||||
|
||||
public static final LodBufferBuilderFactory lodBufferBuilderFactory = new LodBufferBuilderFactory();
|
||||
public static final LodWorld lodWorld = new LodWorld();
|
||||
public static final LodBuilder lodBuilder = new LodBuilder();
|
||||
|
||||
/** Used to determine if the LODs should be regenerated */
|
||||
public static int previousChunkRenderDistance = 0;
|
||||
/** Used to determine if the LODs should be regenerated */
|
||||
public static int previousLodRenderDistance = 0;
|
||||
|
||||
|
||||
|
||||
private ApiShared()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.api;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.builders.worldGeneration.LodGenWorker;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
|
||||
/**
|
||||
* This holds the methods that should be called
|
||||
* by the host mod loader (Fabric, Forge, etc.).
|
||||
* Specifically for the client.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class ClientApi
|
||||
{
|
||||
public static final ClientApi INSTANCE = new ClientApi();
|
||||
public static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME);
|
||||
|
||||
public static LodRenderer renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final EventApi EVENT_API = EventApi.INSTANCE;
|
||||
|
||||
/**
|
||||
* there is some setup that should only happen once,
|
||||
* once this is true that setup has completed
|
||||
*/
|
||||
private boolean firstTimeSetupComplete = false;
|
||||
private boolean configOverrideReminderPrinted = false;
|
||||
|
||||
|
||||
|
||||
private ClientApi()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void renderLods(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks)
|
||||
{
|
||||
// comment out when creating a release
|
||||
applyConfigOverrides();
|
||||
|
||||
// clear any out of date objects
|
||||
MC.clearFrameObjectCache();
|
||||
|
||||
try
|
||||
{
|
||||
// only run the first time setup once
|
||||
if (!firstTimeSetupComplete)
|
||||
firstFrameSetup();
|
||||
|
||||
|
||||
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
|
||||
return;
|
||||
|
||||
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
DetailDistanceUtil.updateSettings();
|
||||
EVENT_API.viewDistanceChangedEvent();
|
||||
EVENT_API.playerMoveEvent(lodDim);
|
||||
|
||||
lodDim.cutRegionNodesAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
|
||||
lodDim.expandOrLoadRegionsAsync(MC.getPlayerBlockPos().getX(), MC.getPlayerBlockPos().getZ());
|
||||
|
||||
|
||||
|
||||
if (CONFIG.client().advanced().debugging().getDrawLods())
|
||||
{
|
||||
// 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
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("LOD");
|
||||
|
||||
|
||||
ClientApi.renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, 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
|
||||
ApiShared.previousChunkRenderDistance = MC_RENDER.getRenderDistance();
|
||||
ApiShared.previousLodRenderDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error("client proxy: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/** used in a development environment to change settings on the fly */
|
||||
private void applyConfigOverrides()
|
||||
{
|
||||
// remind the developer(s) that the config override is active
|
||||
if (!configOverrideReminderPrinted)
|
||||
{
|
||||
MC.sendChatMessage(ModInfo.READABLE_NAME + " experimental build " + ModInfo.VERSION);
|
||||
MC.sendChatMessage("You are running a unsupported version of the mod!");
|
||||
MC.sendChatMessage("Here be dragons!");
|
||||
|
||||
configOverrideReminderPrinted = true;
|
||||
}
|
||||
|
||||
// CONFIG.client().worldGenerator().setDistanceGenerationMode(DistanceGenerationMode.SURFACE);
|
||||
|
||||
// CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(GpuUploadMethod.BUFFER_STORAGE);
|
||||
|
||||
|
||||
|
||||
CONFIG.client().advanced().debugging().setDebugKeybindingsEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// Lod maintenance //
|
||||
//=================//
|
||||
|
||||
/** 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 needs it
|
||||
GLProxy.getInstance();
|
||||
|
||||
firstTimeSetupComplete = true;
|
||||
}
|
||||
|
||||
/** this method reset some static data every time we change world */
|
||||
private void resetMod()
|
||||
{
|
||||
// TODO when should this be used?
|
||||
ThreadMapUtil.clearMaps();
|
||||
LodGenWorker.restartExecutorService();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.api;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This holds the methods that should be called
|
||||
* by the host mod loader (Fabric, Forge, etc.).
|
||||
* Specifically server and client events.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class EventApi
|
||||
{
|
||||
public static final EventApi INSTANCE = new EventApi();
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
/**
|
||||
* can be set if we want to recalculate variables related
|
||||
* to the LOD view distance
|
||||
*/
|
||||
private boolean recalculateWidths = false;
|
||||
|
||||
|
||||
private EventApi()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// tick events //
|
||||
//=============//
|
||||
|
||||
public void serverTickEvent()
|
||||
{
|
||||
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
|
||||
return;
|
||||
|
||||
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
|
||||
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ClientApi.renderer, ApiShared.lodBuilder);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// world events //
|
||||
//==============//
|
||||
|
||||
public void chunkLoadEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType)
|
||||
{
|
||||
ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType, DistanceGenerationMode.FULL);
|
||||
}
|
||||
|
||||
public void worldSaveEvent()
|
||||
{
|
||||
ApiShared.lodWorld.saveAllDimensions();
|
||||
}
|
||||
|
||||
/** This is also called when a new dimension loads */
|
||||
public void worldLoadEvent(IWorldWrapper world)
|
||||
{
|
||||
DataPointUtil.worldHeight = world.getHeight();
|
||||
//LodNodeGenWorker.restartExecutorService();
|
||||
//ThreadMapUtil.clearMaps();
|
||||
|
||||
// the player just loaded a new world/dimension
|
||||
ApiShared.lodWorld.selectWorld(LodUtil.getWorldID(world));
|
||||
|
||||
// make sure the correct LODs are being rendered
|
||||
// (if this isn't done the previous world's LODs may be drawn)
|
||||
ClientApi.renderer.regenerateLODsNextFrame();
|
||||
}
|
||||
|
||||
public void worldUnloadEvent()
|
||||
{
|
||||
// the player just unloaded a world/dimension
|
||||
ThreadMapUtil.clearMaps();
|
||||
|
||||
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
{
|
||||
// the player just left the server
|
||||
|
||||
// TODO should "resetMod()" be called here? -James
|
||||
|
||||
// if this isn't done unfinished tasks may be left in the queue
|
||||
// preventing new LodChunks form being generated
|
||||
//LodNodeGenWorker.restartExecutorService(); // TODO why was this commented out? -James
|
||||
//ThreadMapUtil.clearMaps();
|
||||
|
||||
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
|
||||
ApiShared.lodWorld.deselectWorld();
|
||||
|
||||
|
||||
// prevent issues related to the buffer builder
|
||||
// breaking when changing worlds.
|
||||
ClientApi.renderer.destroyBuffers();
|
||||
recalculateWidths = true;
|
||||
ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||
|
||||
|
||||
// make sure the nulled objects are freed.
|
||||
// (this prevents an out of memory error when
|
||||
// changing worlds)
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
||||
public void blockChangeEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType)
|
||||
{
|
||||
// recreate the LOD where the blocks were changed
|
||||
ApiShared.lodBuilder.generateLodNodeAsync(chunk, ApiShared.lodWorld, dimType);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// Misc Events //
|
||||
//=============//
|
||||
|
||||
public void onKeyInput(int key, int keyAction)
|
||||
{
|
||||
if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled())
|
||||
{
|
||||
if (key == GLFW.GLFW_KEY_F4 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
|
||||
}
|
||||
|
||||
if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Re-centers the given LodDimension if it needs to be. */
|
||||
public void playerMoveEvent(LodDimension lodDim)
|
||||
{
|
||||
// make sure the dimension is centered
|
||||
RegionPos playerRegionPos = new RegionPos(MC.getPlayerBlockPos());
|
||||
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterRegionPosX(), playerRegionPos.z - lodDim.getCenterRegionPosZ());
|
||||
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
|
||||
{
|
||||
ApiShared.lodWorld.saveAllDimensions();
|
||||
lodDim.move(worldRegionOffset);
|
||||
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
|
||||
}
|
||||
}
|
||||
|
||||
/** Re-sizes all LodDimensions if they need to be. */
|
||||
public void viewDistanceChangedEvent()
|
||||
{
|
||||
// calculate how wide the dimension(s) should be in regions
|
||||
int chunksWide;
|
||||
if (MC.getWrappedClientWorld().getDimensionType().hasCeiling())
|
||||
chunksWide = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
|
||||
else
|
||||
chunksWide = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 2 + 1;
|
||||
|
||||
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||
// make sure we have an odd number of regions
|
||||
newWidth += (newWidth & 1) == 0 ? 1 : 2;
|
||||
|
||||
// do the dimensions need to change in size?
|
||||
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
|
||||
{
|
||||
ApiShared.lodWorld.saveAllDimensions();
|
||||
|
||||
// update the dimensions to fit the new width
|
||||
ApiShared.lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
ApiShared.lodBuilder.defaultDimensionWidthInRegions = newWidth;
|
||||
ClientApi.renderer.setupBuffers(ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension()));
|
||||
|
||||
recalculateWidths = false;
|
||||
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
|
||||
}
|
||||
DetailDistanceUtil.updateSettings();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-992
@@ -1,992 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.bufferBuilding;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GL45;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.config.VanillaOverdraw;
|
||||
import com.seibel.lod.core.enums.rendering.GLProxyContext;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.PosToRenderContainer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.objects.opengl.LodVertexBuffer;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
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.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
* This object creates the buffers that are
|
||||
* rendered by the LodRenderer.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
*/
|
||||
public class LodBufferBuilderFactory
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
/** The thread used to generate new LODs off the main thread. */
|
||||
public static final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilderFactory.class.getSimpleName() + " - main"));
|
||||
/** The threads used to generate buffers. */
|
||||
public static final ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfBufferBuilderThreads(), new ThreadFactoryBuilder().setNameFormat("Buffer-Builder-%d").build());
|
||||
|
||||
/**
|
||||
* When uploading to a buffer that is too small,
|
||||
* recreate it this many times bigger than the upload payload
|
||||
*/
|
||||
public static final double BUFFER_EXPANSION_MULTIPLIER = 1.5;
|
||||
|
||||
/**
|
||||
* When buffers are first created they are allocated to this size (in Bytes).
|
||||
* This size will be too small, more than likely. The buffers will be expanded
|
||||
* when need be to fit the larger sizes.
|
||||
*/
|
||||
public static final int DEFAULT_MEMORY_ALLOCATION = 1024;
|
||||
|
||||
public static int skyLightPlayer = 15;
|
||||
|
||||
/**
|
||||
* How many buffers there are for the given region. <Br>
|
||||
* This is done because some regions may require more memory than
|
||||
* can be directly allocated, so we split the regions into smaller sections. <Br>
|
||||
* This keeps track of those sections.
|
||||
*/
|
||||
public volatile int[][] numberOfBuffersPerRegion;
|
||||
|
||||
/** Stores the vertices when building the VBOs */
|
||||
public volatile LodBufferBuilder[][][] buildableBuffers;
|
||||
|
||||
/** The OpenGL IDs of the storage buffers used by the buildableVbos */
|
||||
public int[][][] buildableStorageBufferIds;
|
||||
/** The OpenGL IDs of the storage buffers used by the drawableVbos */
|
||||
public int[][][] drawableStorageBufferIds;
|
||||
|
||||
/** Used when building new VBOs */
|
||||
public volatile LodVertexBuffer[][][] buildableVbos;
|
||||
/** VBOs that are sent over to the LodNodeRenderer */
|
||||
public volatile LodVertexBuffer[][][] 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 final ReentrantLock bufferLock = new ReentrantLock();
|
||||
|
||||
private volatile Box[][] boxCache;
|
||||
private volatile PosToRenderContainer[][] setsToRender;
|
||||
private volatile RegionPos center;
|
||||
|
||||
/**
|
||||
* This is the ChunkPosWrapper the player was at the last time the buffers were built.
|
||||
* IE the center of the buffers last time they were built
|
||||
*/
|
||||
private volatile AbstractChunkPosWrapper drawableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
|
||||
private volatile AbstractChunkPosWrapper buildableCenterChunkPos = WRAPPER_FACTORY.createChunkPos();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public LodBufferBuilderFactory()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a thread to asynchronously generate LOD buffers
|
||||
* centered around the given camera X and Z.
|
||||
* <br>
|
||||
* This method will write to the drawable near and far buffers.
|
||||
* <br>
|
||||
* After the buildable buffers have been generated they must be
|
||||
* swapped with the drawable buffers in the LodRenderer to be drawn.
|
||||
*/
|
||||
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
|
||||
AbstractBlockPosWrapper 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;
|
||||
|
||||
|
||||
Thread thread = new Thread(() -> generateLodBuffersThread(renderer, lodDim, playerBlockPos, fullRegen));
|
||||
|
||||
mainGenThread.execute(thread);
|
||||
}
|
||||
|
||||
// this was pulled out as a separate method so that it could be
|
||||
// more easily edited by hot swapping. Because, As far as James is aware
|
||||
// you can't hot swap lambda expressions.
|
||||
private void generateLodBuffersThread(LodRenderer renderer, LodDimension lodDim,
|
||||
AbstractBlockPosWrapper playerBlockPos, boolean fullRegen)
|
||||
{
|
||||
bufferLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
// round the player's block position down to the nearest chunk BlockPos
|
||||
AbstractChunkPosWrapper playerChunkPos = WRAPPER_FACTORY.createChunkPos(playerBlockPos);
|
||||
AbstractBlockPosWrapper playerBlockPosRounded = playerChunkPos.getWorldPosition();
|
||||
|
||||
|
||||
//long startTime = System.currentTimeMillis();
|
||||
|
||||
ArrayList<Callable<Boolean>> nodeToRenderThreads = new ArrayList<>(lodDim.getWidth() * lodDim.getWidth());
|
||||
|
||||
startBuffers(fullRegen, lodDim);
|
||||
|
||||
|
||||
RegionPos playerRegionPos = new RegionPos(playerChunkPos);
|
||||
if (center == null)
|
||||
center = playerRegionPos;
|
||||
|
||||
if (setsToRender == null)
|
||||
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;
|
||||
|
||||
|
||||
//================================//
|
||||
// create the nodeToRenderThreads //
|
||||
//================================//
|
||||
|
||||
skyLightPlayer = MC.getWrappedClientWorld().getSkyLight(playerBlockPos);
|
||||
|
||||
for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++)
|
||||
{
|
||||
for (int zRegion = 0; zRegion < lodDim.getWidth(); zRegion++)
|
||||
{
|
||||
if (lodDim.doesRegionNeedBufferRegen(xRegion, zRegion) || fullRegen)
|
||||
{
|
||||
RegionPos regionPos = new RegionPos(
|
||||
xRegion + lodDim.getCenterRegionPosX() - lodDim.getWidth() / 2,
|
||||
zRegion + lodDim.getCenterRegionPosZ() - lodDim.getWidth() / 2);
|
||||
|
||||
// local position in the vbo and bufferBuilder arrays
|
||||
LodBufferBuilder[] currentBuffers = 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 (currentBuffers == null || !currentBuffers[0].building())
|
||||
return;
|
||||
|
||||
byte minDetail = region.getMinDetailLevel();
|
||||
|
||||
|
||||
final int xR = xRegion;
|
||||
final int zR = zRegion;
|
||||
|
||||
//we create the Callable to use for the buffer builder creation
|
||||
Callable<Boolean> dataToRenderThread = () ->
|
||||
{
|
||||
//Variable initialization
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
int xAdj;
|
||||
int zAdj;
|
||||
int bufferIndex;
|
||||
boolean posNotInPlayerChunk;
|
||||
boolean adjPosInPlayerChunk;
|
||||
Box box = ThreadMapUtil.getBox();
|
||||
boolean[] adjShadeDisabled = ThreadMapUtil.getAdjShadeDisabledArray();
|
||||
|
||||
// determine how many LODs we can stack vertically
|
||||
int maxVerticalData = DetailDistanceUtil.getMaxVerticalData((byte) 0);
|
||||
|
||||
//we get or create the map that will contain the adj data
|
||||
Map<LodDirection, long[]> adjData = ThreadMapUtil.getAdjDataArray(maxVerticalData);
|
||||
|
||||
//previous setToRender cache
|
||||
if (setsToRender[xR][zR] == null)
|
||||
setsToRender[xR][zR] = new PosToRenderContainer(minDetail, regionPos.x, regionPos.z);
|
||||
|
||||
|
||||
//We ask the lod dimension which block we have to render given the player position
|
||||
PosToRenderContainer posToRender = setsToRender[xR][zR];
|
||||
posToRender.clear(minDetail, regionPos.x, regionPos.z);
|
||||
|
||||
lodDim.getPosToRender(
|
||||
posToRender,
|
||||
regionPos,
|
||||
playerBlockPosRounded.getX(),
|
||||
playerBlockPosRounded.getZ());
|
||||
|
||||
|
||||
|
||||
// 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++)
|
||||
{
|
||||
bufferIndex = index % currentBuffers.length;
|
||||
detailLevel = posToRender.getNthDetailLevel(index);
|
||||
posX = posToRender.getNthPosX(index);
|
||||
posZ = posToRender.getNthPosZ(index);
|
||||
|
||||
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
|
||||
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
|
||||
|
||||
//We don't want to render this fake block if
|
||||
//The block is inside the render distance with, is not bigger than a chunk and is positioned in a chunk set as vanilla rendered
|
||||
//
|
||||
//The block is in the player chunk or in a chunk adjacent to the player
|
||||
if(isThisPositionGoingToBeRendered(detailLevel, posX, posZ, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//we check if the block to render is not in player chunk
|
||||
posNotInPlayerChunk = !(chunkXdist == 0 && chunkZdist == 0);
|
||||
|
||||
// We extract the adj data in the four cardinal direction
|
||||
|
||||
// we first reset the adjShadeDisabled. This is used to disable the shade on the border when we have transparent block like water or glass
|
||||
// to avoid having a "darker border" underground
|
||||
Arrays.fill(adjShadeDisabled, false);
|
||||
|
||||
//We check every adj block in each direction
|
||||
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
{
|
||||
|
||||
xAdj = posX + Box.DIRECTION_NORMAL_MAP.get(lodDirection).x;
|
||||
zAdj = posZ + Box.DIRECTION_NORMAL_MAP.get(lodDirection).z;
|
||||
long data;
|
||||
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.getX();
|
||||
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.getZ();
|
||||
adjPosInPlayerChunk = (chunkXdist == 0 && chunkZdist == 0);
|
||||
|
||||
//If the adj block is rendered in the same region and with same detail
|
||||
// and is positioned in a place that is not going to be rendered by vanilla game
|
||||
// then we can set this position as adj
|
||||
// We avoid cases where the adjPosition is in player chunk while the position is not
|
||||
// to always have a wall underwater
|
||||
if(posToRender.contains(detailLevel, xAdj, zAdj)
|
||||
&& !isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance)
|
||||
&& !(posNotInPlayerChunk && adjPosInPlayerChunk))
|
||||
{
|
||||
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, xAdj, zAdj); verticalIndex++)
|
||||
{
|
||||
data = lodDim.getData(detailLevel, xAdj, zAdj, verticalIndex);
|
||||
adjShadeDisabled[Box.DIRECTION_INDEX.get(lodDirection)] = false;
|
||||
adjData.get(lodDirection)[verticalIndex] = data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Otherwise, we check if this position is
|
||||
data = lodDim.getSingleData(detailLevel, xAdj, zAdj);
|
||||
|
||||
adjData.get(lodDirection)[0] = DataPointUtil.EMPTY_DATA;
|
||||
|
||||
if ((isThisPositionGoingToBeRendered(detailLevel, xAdj, zAdj, playerChunkPos, vanillaRenderedChunks, gameChunkRenderDistance) || (posNotInPlayerChunk && adjPosInPlayerChunk))
|
||||
&& !DataPointUtil.isVoid(data))
|
||||
{
|
||||
adjShadeDisabled[Box.DIRECTION_INDEX.get(lodDirection)] = DataPointUtil.getAlpha(data) < 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// We render every vertical lod present in this position
|
||||
// We only stop when we find a block that is void or non existing block
|
||||
long data;
|
||||
for (int verticalIndex = 0; verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ); verticalIndex++)
|
||||
{
|
||||
|
||||
//we get the above block as adj UP
|
||||
if (verticalIndex > 0)
|
||||
adjData.get(LodDirection.UP)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex - 1);
|
||||
else
|
||||
adjData.get(LodDirection.UP)[0] = DataPointUtil.EMPTY_DATA;
|
||||
|
||||
|
||||
//we get the below block as adj DOWN
|
||||
if (verticalIndex < lodDim.getMaxVerticalData(detailLevel, posX, posZ) - 1)
|
||||
adjData.get(LodDirection.DOWN)[0] = lodDim.getData(detailLevel, posX, posZ, verticalIndex + 1);
|
||||
else
|
||||
adjData.get(LodDirection.DOWN)[0] = DataPointUtil.EMPTY_DATA;
|
||||
|
||||
//We extract the data to render
|
||||
data = lodDim.getData(detailLevel, posX, posZ, verticalIndex);
|
||||
|
||||
//If the data is not renderable (Void or non existing) we stop since there is no data left in this position
|
||||
if (DataPointUtil.isVoid(data) || !DataPointUtil.doesItExist(data))
|
||||
break;
|
||||
|
||||
//We send the call to create the vertices
|
||||
CONFIG.client().graphics().advancedGraphics().getLodTemplate().template.addLodToBuffer(currentBuffers[bufferIndex], playerBlockPosRounded, data, adjData,
|
||||
detailLevel, posX, posZ, box, renderer.previousDebugMode, adjShadeDisabled);
|
||||
}
|
||||
|
||||
} // for pos to in list to render
|
||||
// the thread executed successfully
|
||||
return true;
|
||||
};
|
||||
|
||||
nodeToRenderThreads.add(dataToRenderThread);
|
||||
|
||||
}
|
||||
} // region z
|
||||
} // region z
|
||||
|
||||
|
||||
//long executeStart = System.currentTimeMillis();
|
||||
// wait for all threads to finish
|
||||
List<Future<Boolean>> futuresBuffer = bufferBuilderThreads.invokeAll(nodeToRenderThreads);
|
||||
for (Future<Boolean> future : futuresBuffer)
|
||||
{
|
||||
// the future will be false if its thread failed
|
||||
if (!future.get())
|
||||
{
|
||||
ClientApi.LOGGER.warn("LodBufferBuilder ran into trouble and had to start over.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
//long executeEnd = System.currentTimeMillis();
|
||||
|
||||
|
||||
//long endTime = System.currentTimeMillis();
|
||||
//long buildTime = endTime - startTime;
|
||||
//long executeTime = executeEnd - executeStart;
|
||||
|
||||
// ClientProxy.LOGGER.info("Thread Build time: " + buildTime + " ms" + '\n' +
|
||||
// "thread execute time: " + executeTime + " ms");
|
||||
|
||||
// mark that the buildable buffers as ready to swap
|
||||
switchVbos = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" ran into trouble: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// clean up any potentially open resources
|
||||
if (buildableBuffers != null)
|
||||
closeBuffers(fullRegen, lodDim);
|
||||
|
||||
try
|
||||
{
|
||||
// upload the new buffers
|
||||
uploadBuffers(fullRegen, lodDim);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.warn("\"LodNodeBufferBuilder.generateLodBuffersAsync\" was unable to upload the buffers to the GPU: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// regardless of whether we were able to successfully create
|
||||
// the buffers, we are done generating.
|
||||
generatingBuffers = false;
|
||||
bufferLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, AbstractChunkPosWrapper playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
|
||||
|
||||
|
||||
// skip any chunks that Minecraft is going to render
|
||||
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.getX();
|
||||
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.getZ();
|
||||
|
||||
// check if the chunk is on the border
|
||||
boolean isItBorderPos;
|
||||
if (CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw() == VanillaOverdraw.BORDER)
|
||||
isItBorderPos = LodUtil.isBorderChunk(vanillaRenderedChunks, chunkXdist + gameChunkRenderDistance + 1, chunkZdist + gameChunkRenderDistance + 1);
|
||||
else
|
||||
isItBorderPos = false;
|
||||
|
||||
|
||||
//boolean smallRenderDistance = gameChunkRenderDistance <= LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW;
|
||||
|
||||
// get the positions that will be rendered
|
||||
|
||||
return (gameChunkRenderDistance >= Math.abs(chunkXdist)
|
||||
&& gameChunkRenderDistance >= Math.abs(chunkZdist)
|
||||
&& detailLevel <= LodUtil.CHUNK_DETAIL_LEVEL
|
||||
&& vanillaRenderedChunks[chunkXdist + gameChunkRenderDistance + 1][chunkZdist + gameChunkRenderDistance + 1])
|
||||
&& (!isItBorderPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===============================//
|
||||
// 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(LodDimension lodDimension)
|
||||
{
|
||||
bufferLock.lock();
|
||||
int numbRegionsWide = lodDimension.getWidth();
|
||||
long regionMemoryRequired;
|
||||
int numberOfBuffers;
|
||||
|
||||
GLProxy glProxy = GLProxy.getInstance();
|
||||
GLProxyContext oldContext = glProxy.getGlContext();
|
||||
glProxy.setGlContext(GLProxyContext.LOD_BUILDER);
|
||||
|
||||
|
||||
previousRegionWidth = numbRegionsWide;
|
||||
numberOfBuffersPerRegion = new int[numbRegionsWide][numbRegionsWide];
|
||||
buildableBuffers = new LodBufferBuilder[numbRegionsWide][numbRegionsWide][];
|
||||
|
||||
buildableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][];
|
||||
drawableVbos = new LodVertexBuffer[numbRegionsWide][numbRegionsWide][];
|
||||
|
||||
if (glProxy.bufferStorageSupported)
|
||||
{
|
||||
buildableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
|
||||
drawableStorageBufferIds = new int[numbRegionsWide][numbRegionsWide][];
|
||||
}
|
||||
|
||||
for (int x = 0; x < numbRegionsWide; x++)
|
||||
{
|
||||
for (int z = 0; z < numbRegionsWide; z++)
|
||||
{
|
||||
regionMemoryRequired = DEFAULT_MEMORY_ALLOCATION;
|
||||
|
||||
// if the memory required is greater than the max buffer
|
||||
// capacity, divide the memory across multiple buffers
|
||||
if (regionMemoryRequired > LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY)
|
||||
{
|
||||
numberOfBuffers = (int) regionMemoryRequired / LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY + 1;
|
||||
|
||||
// TODO shouldn't this be determined with regionMemoryRequired?
|
||||
// always allocating the max memory is a bit expensive isn't it?
|
||||
regionMemoryRequired = LodUtil.MAX_ALLOCATABLE_DIRECT_MEMORY;
|
||||
numberOfBuffersPerRegion[x][z] = numberOfBuffers;
|
||||
buildableBuffers[x][z] = new LodBufferBuilder[numberOfBuffers];
|
||||
buildableVbos[x][z] = new LodVertexBuffer[numberOfBuffers];
|
||||
drawableVbos[x][z] = new LodVertexBuffer[numberOfBuffers];
|
||||
|
||||
if (glProxy.bufferStorageSupported)
|
||||
{
|
||||
buildableStorageBufferIds[x][z] = new int[numberOfBuffers];
|
||||
drawableStorageBufferIds[x][z] = new int[numberOfBuffers];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we only need one buffer for this region
|
||||
numberOfBuffersPerRegion[x][z] = 1;
|
||||
buildableBuffers[x][z] = new LodBufferBuilder[1];
|
||||
buildableVbos[x][z] = new LodVertexBuffer[1];
|
||||
drawableVbos[x][z] = new LodVertexBuffer[1];
|
||||
|
||||
if (glProxy.bufferStorageSupported)
|
||||
{
|
||||
buildableStorageBufferIds[x][z] = new int[1];
|
||||
drawableStorageBufferIds[x][z] = new int[1];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < numberOfBuffersPerRegion[x][z]; i++)
|
||||
{
|
||||
buildableBuffers[x][z][i] = new LodBufferBuilder((int) regionMemoryRequired);
|
||||
|
||||
buildableVbos[x][z][i] = new LodVertexBuffer();
|
||||
drawableVbos[x][z][i] = new LodVertexBuffer();
|
||||
|
||||
|
||||
// create the initial mapped buffers (system memory)
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableVbos[x][z][i].id);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableVbos[x][z][i].id);
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, GL15.GL_DYNAMIC_DRAW);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
|
||||
if (glProxy.bufferStorageSupported)
|
||||
{
|
||||
// create the buffer storage (GPU memory)
|
||||
buildableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, buildableStorageBufferIds[x][z][i]);
|
||||
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0); // the 0 flag means to create the storage in the GPUs memory
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
drawableStorageBufferIds[x][z][i] = GL15.glGenBuffers();
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, drawableStorageBufferIds[x][z][i]);
|
||||
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, regionMemoryRequired, 0);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glProxy.setGlContext(oldContext);
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sets the buffers and Vbos to null, forcing them to be recreated <br>
|
||||
* and destroys any bound OpenGL objects. <br><br>
|
||||
* <p>
|
||||
* May have to wait for the bufferLock to open.
|
||||
*/
|
||||
public void destroyBuffers()
|
||||
{
|
||||
bufferLock.lock();
|
||||
|
||||
|
||||
// destroy the buffer storages if they aren't already
|
||||
if (buildableStorageBufferIds != null)
|
||||
{
|
||||
for (int x = 0; x < buildableStorageBufferIds.length; x++)
|
||||
{
|
||||
for (int z = 0; z < buildableStorageBufferIds.length; z++)
|
||||
{
|
||||
for (int i = 0; i < buildableStorageBufferIds[x][z].length; i++)
|
||||
{
|
||||
int buildableId = buildableStorageBufferIds[x][z][i];
|
||||
int drawableId = drawableStorageBufferIds[x][z][i];
|
||||
|
||||
// make sure the buffers are deleted in a openGL context
|
||||
GLProxy.getInstance().recordOpenGlCall(() ->
|
||||
{
|
||||
GL15.glDeleteBuffers(buildableId);
|
||||
GL15.glDeleteBuffers(drawableId);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildableStorageBufferIds = null;
|
||||
drawableStorageBufferIds = null;
|
||||
|
||||
|
||||
|
||||
|
||||
// destroy the VBOs if they aren't already
|
||||
if (buildableVbos != null)
|
||||
{
|
||||
for (int i = 0; i < buildableVbos.length; i++)
|
||||
{
|
||||
for (int j = 0; j < buildableVbos.length; j++)
|
||||
{
|
||||
for (int k = 0; k < buildableVbos[i][j].length; k++)
|
||||
{
|
||||
int buildableId;
|
||||
int drawableId;
|
||||
|
||||
// variables passed into a lambda expression
|
||||
// need to be effectively final, so we have
|
||||
// to use an else statement here
|
||||
if (buildableVbos[i][j][k] != null)
|
||||
buildableId = buildableVbos[i][j][k].id;
|
||||
else
|
||||
buildableId = 0;
|
||||
|
||||
if (drawableVbos[i][j][k] != null)
|
||||
drawableId = drawableVbos[i][j][k].id;
|
||||
else
|
||||
drawableId = 0;
|
||||
|
||||
|
||||
GLProxy.getInstance().recordOpenGlCall(() ->
|
||||
{
|
||||
if (buildableId != 0)
|
||||
GL15.glDeleteBuffers(buildableId);
|
||||
if (drawableId != 0)
|
||||
GL15.glDeleteBuffers(drawableId);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildableVbos = null;
|
||||
drawableVbos = null;
|
||||
|
||||
|
||||
// these don't contain any OpenGL objects, so
|
||||
// they don't require any special clean-up
|
||||
buildableBuffers = 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.doesRegionNeedBufferRegen(x, z))
|
||||
{
|
||||
for (int i = 0; i < buildableBuffers[x][z].length; i++)
|
||||
{
|
||||
// for some reason BufferBuilder.vertexCounts
|
||||
// isn't reset unless this is called, which can cause
|
||||
// a false indexOutOfBoundsException
|
||||
buildableBuffers[x][z][i].discard();
|
||||
|
||||
buildableBuffers[x][z][i].begin(GL11.GL_QUADS, LodUtil.LOD_VERTEX_FORMAT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls end on each of the buildable BufferBuilders. */
|
||||
private void closeBuffers(boolean fullRegen, LodDimension lodDim)
|
||||
{
|
||||
for (int x = 0; x < buildableBuffers.length; x++)
|
||||
for (int z = 0; z < buildableBuffers.length; z++)
|
||||
for (int i = 0; i < buildableBuffers[x][z].length; i++)
|
||||
if (buildableBuffers[x][z][i] != null && buildableBuffers[x][z][i].building() && (fullRegen || lodDim.doesRegionNeedBufferRegen(x, z)))
|
||||
buildableBuffers[x][z][i].end();
|
||||
}
|
||||
|
||||
|
||||
/** Upload all buildableBuffers to the GPU. */
|
||||
private void uploadBuffers(boolean fullRegen, LodDimension lodDim)
|
||||
{
|
||||
GLProxy glProxy = GLProxy.getInstance();
|
||||
|
||||
try
|
||||
{
|
||||
// make sure we are uploading to the builder context,
|
||||
// this helps prevent interference (IE stuttering) with the Minecraft context.
|
||||
glProxy.setGlContext(GLProxyContext.LOD_BUILDER);
|
||||
|
||||
// determine the upload method
|
||||
GpuUploadMethod uploadMethod = CONFIG.client().graphics().advancedGraphics().getGpuUploadMethod();
|
||||
if (!glProxy.bufferStorageSupported && uploadMethod == GpuUploadMethod.BUFFER_STORAGE)
|
||||
{
|
||||
// if buffer storage isn't supported
|
||||
// default to SUB_DATA
|
||||
CONFIG.client().graphics().advancedGraphics().setGpuUploadMethod(GpuUploadMethod.SUB_DATA);
|
||||
uploadMethod = GpuUploadMethod.SUB_DATA;
|
||||
}
|
||||
|
||||
// actually upload the buffers
|
||||
for (int x = 0; x < buildableVbos.length; x++)
|
||||
{
|
||||
for (int z = 0; z < buildableVbos.length; z++)
|
||||
{
|
||||
if (fullRegen || lodDim.doesRegionNeedBufferRegen(x, z))
|
||||
{
|
||||
for (int i = 0; i < buildableBuffers[x][z].length; i++)
|
||||
{
|
||||
ByteBuffer uploadBuffer = buildableBuffers[x][z][i].getCleanedByteBuffer();
|
||||
int storageBufferId = 0;
|
||||
if (buildableStorageBufferIds != null)
|
||||
storageBufferId = buildableStorageBufferIds[x][z][i];
|
||||
|
||||
vboUpload(buildableVbos[x][z][i], storageBufferId, uploadBuffer, true, uploadMethod);
|
||||
lodDim.setRegenRegionBufferByArrayIndex(x, z, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// this doesn't appear to be necessary anymore, but just in case.
|
||||
ClientApi.LOGGER.error(LodBufferBuilderFactory.class.getSimpleName() + " - UploadBuffers failed: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
GL15.glFinish();
|
||||
|
||||
// close the context so it can be re-used later.
|
||||
// I'm guessing we can't just leave it because the executor service
|
||||
// does something that invalidates the OpenGL context.
|
||||
glProxy.setGlContext(GLProxyContext.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
/** Uploads the uploadBuffer so the GPU can use it. */
|
||||
private void vboUpload(LodVertexBuffer vbo, int storageBufferId, ByteBuffer uploadBuffer,
|
||||
boolean allowBufferExpansion, GpuUploadMethod uploadMethod)
|
||||
{
|
||||
// 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.capacity() / ((Float.BYTES * 3) + (Byte.BYTES * 4))); // TODO make this change with the LodTemplate
|
||||
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
|
||||
try
|
||||
{
|
||||
// if possible use the faster buffer storage route
|
||||
if (uploadMethod == GpuUploadMethod.BUFFER_STORAGE && storageBufferId != 0)
|
||||
{
|
||||
// get a pointer to the buffer in system memory
|
||||
ByteBuffer vboBuffer = GL30.glMapBufferRange(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
if (vboBuffer == null)
|
||||
{
|
||||
int previousCapacity = uploadBuffer.capacity();
|
||||
|
||||
// only expand the buffers if the uploadBuffer actually
|
||||
// has something in it and expansion is allowed
|
||||
if (previousCapacity != 0 && allowBufferExpansion)
|
||||
{
|
||||
// the buffer(s) aren't big enough, expand them.
|
||||
// This does cause lag/stuttering, so it should be avoided!
|
||||
|
||||
// expand the buffer in system memory
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
|
||||
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
|
||||
// un-bind the system memory buffer
|
||||
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
// expand the buffer storage
|
||||
GL15.glDeleteBuffers(storageBufferId);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, storageBufferId);
|
||||
GL45.glBufferStorage(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), 0);
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
|
||||
|
||||
// recursively try to upload into the newly created buffer storage
|
||||
// but don't recurse again if that fails
|
||||
// (we don't want an infinitely expanding buffer!)
|
||||
vboUpload(vbo, storageBufferId, uploadBuffer, false, uploadMethod);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// upload the buffer into system memory...
|
||||
vboBuffer.put(uploadBuffer);
|
||||
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
|
||||
// ...then upload into GPU memory
|
||||
// (uploading into GPU memory directly can only be done
|
||||
// through the glCopyBufferSubData/glCopyNamed... methods)
|
||||
GL45.glCopyNamedBufferSubData(vbo.id, storageBufferId, 0, 0, uploadBuffer.capacity());
|
||||
|
||||
// alternative way that doesn't require GL45
|
||||
// GL15.glBindBuffer(GL45.GL_COPY_WRITE_BUFFER, storageBufferId);
|
||||
// GL45.glCopyBufferSubData(GL15.GL_ARRAY_BUFFER, GL45.GL_COPY_WRITE_BUFFER, 0, 0, uploadBuffer.capacity());
|
||||
// GL15.glBindBuffer(GL45.GL_COPY_WRITE_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
else if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING)
|
||||
{
|
||||
// no stuttering but high GPU usage
|
||||
// stores everything in system memory instead of GPU memory
|
||||
// making rendering much slower.
|
||||
// Unless the user is running integrated graphics,
|
||||
// in that case this will actually work better than SUB_DATA.
|
||||
|
||||
|
||||
ByteBuffer vboBuffer;
|
||||
|
||||
// map buffer range is better since it can be explicitly unsynchronized
|
||||
if (GLProxy.getInstance().mapBufferRangeSupported)
|
||||
vboBuffer = GL30.glMapBufferRange(GL30.GL_ARRAY_BUFFER, 0, uploadBuffer.capacity(), GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_UNSYNCHRONIZED_BIT);
|
||||
else
|
||||
vboBuffer = GL15.glMapBuffer(GL30.GL_ARRAY_BUFFER, uploadBuffer.capacity());
|
||||
|
||||
|
||||
if (vboBuffer == null)
|
||||
{
|
||||
GL15.glBufferData(GL45.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
|
||||
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
vboBuffer.put(uploadBuffer);
|
||||
}
|
||||
}
|
||||
else if (uploadMethod == GpuUploadMethod.DATA)
|
||||
{
|
||||
// hybrid bufferData //
|
||||
// high stutter, low GPU usage
|
||||
// But simplest/most compatible
|
||||
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, uploadBuffer, GL15.GL_DYNAMIC_DRAW);
|
||||
}
|
||||
else
|
||||
{
|
||||
// hybrid subData/bufferData //
|
||||
// less stutter, low GPU usage
|
||||
|
||||
long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
|
||||
if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER)
|
||||
{
|
||||
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, (int) (uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER), GL15.GL_DYNAMIC_DRAW);
|
||||
}
|
||||
GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, uploadBuffer);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error("vboUpload failed: " + e.getClass().getSimpleName());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (uploadMethod == GpuUploadMethod.BUFFER_MAPPING)
|
||||
GL15.glUnmapBuffer(GL15.GL_ARRAY_BUFFER);
|
||||
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
}
|
||||
|
||||
}//if vbo exists and in correct GL context
|
||||
}//vboUpload
|
||||
|
||||
/** Get the newly created VBOs */
|
||||
public VertexBuffersAndOffset getVertexBuffers()
|
||||
{
|
||||
// don't wait for the lock to open,
|
||||
// since this is called on the main render thread
|
||||
if (bufferLock.tryLock())
|
||||
{
|
||||
LodVertexBuffer[][][] tmpVbo = drawableVbos;
|
||||
drawableVbos = buildableVbos;
|
||||
buildableVbos = tmpVbo;
|
||||
|
||||
int[][][] tmpStorage = drawableStorageBufferIds;
|
||||
drawableStorageBufferIds = buildableStorageBufferIds;
|
||||
buildableStorageBufferIds = tmpStorage;
|
||||
|
||||
drawableCenterChunkPos = buildableCenterChunkPos;
|
||||
|
||||
// the vbos have been swapped
|
||||
switchVbos = false;
|
||||
bufferLock.unlock();
|
||||
}
|
||||
|
||||
return new VertexBuffersAndOffset(drawableVbos, drawableStorageBufferIds, drawableCenterChunkPos);
|
||||
}
|
||||
|
||||
/** A simple container to pass multiple objects back in the getVertexBuffers method. */
|
||||
public static class VertexBuffersAndOffset
|
||||
{
|
||||
public final LodVertexBuffer[][][] vbos;
|
||||
public final int[][][] storageBufferIds;
|
||||
public final AbstractChunkPosWrapper drawableCenterChunkPos;
|
||||
|
||||
public VertexBuffersAndOffset(LodVertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, AbstractChunkPosWrapper newDrawableCenterChunkPos)
|
||||
{
|
||||
vbos = newVbos;
|
||||
storageBufferIds = newStorageBufferIds;
|
||||
drawableCenterChunkPos = newDrawableCenterChunkPos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is true the buildable near and far
|
||||
* buffers have been generated and are ready to be
|
||||
* sent to the LodRenderer.
|
||||
*/
|
||||
public boolean newBuffersAvailable()
|
||||
{
|
||||
return switchVbos;
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* This is the abstract class used to create different
|
||||
* BufferBuilders.
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public abstract class AbstractLodTemplate
|
||||
{
|
||||
/** Uploads the given LOD to the buffer. */
|
||||
public abstract void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled);
|
||||
|
||||
/** add the given position and color to the buffer */
|
||||
protected void addPosAndColor(LodBufferBuilder buffer,
|
||||
float x, float y, float z,
|
||||
int color)
|
||||
{
|
||||
// TODO re-add transparency by replacing the 255 with "ColorUtil.getAlpha(color)"
|
||||
buffer.vertex(x, y, z).color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255).endVertex();
|
||||
}
|
||||
|
||||
}
|
||||
-142
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* Builds LODs as rectangular prisms.
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class CubicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
|
||||
public CubicLodTemplate()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
if (box == null)
|
||||
return;
|
||||
|
||||
// equivalent to 2^detailLevel
|
||||
int blockWidth = 1 << detailLevel;
|
||||
|
||||
int color;
|
||||
if (debugging != DebugMode.OFF)
|
||||
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
|
||||
else
|
||||
color = DataPointUtil.getColor(data);
|
||||
|
||||
|
||||
generateBoundingBox(
|
||||
box,
|
||||
DataPointUtil.getHeight(data),
|
||||
DataPointUtil.getDepth(data),
|
||||
blockWidth,
|
||||
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
|
||||
bufferCenterBlockPos,
|
||||
adjData,
|
||||
color,
|
||||
DataPointUtil.getLightSkyAlt(data),
|
||||
DataPointUtil.getLightBlock(data),
|
||||
adjShadeDisabled);
|
||||
|
||||
addBoundingBoxToBuffer(buffer, box);
|
||||
}
|
||||
|
||||
private void generateBoundingBox(Box box,
|
||||
int height, int depth, int width,
|
||||
double xOffset, double yOffset, double zOffset,
|
||||
AbstractBlockPosWrapper bufferCenterBlockPos,
|
||||
Map<LodDirection, long[]> adjData,
|
||||
int color,
|
||||
int skyLight,
|
||||
int blockLight,
|
||||
boolean[] adjShadeDisabled)
|
||||
{
|
||||
// don't add an LOD if it is empty
|
||||
if (height == -1 && depth == -1)
|
||||
return;
|
||||
|
||||
if (depth == height)
|
||||
// if the top and bottom points are at the same height
|
||||
// render this LOD as 1 block thick
|
||||
height++;
|
||||
|
||||
// offset the AABB by its 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, adjShadeDisabled);
|
||||
box.setLights(skyLight, blockLight);
|
||||
box.setWidth(width, height - depth, width);
|
||||
box.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
|
||||
box.setUpCulling(32, bufferCenterBlockPos);
|
||||
box.setAdjData(adjData);
|
||||
}
|
||||
|
||||
private void addBoundingBoxToBuffer(LodBufferBuilder buffer, Box box)
|
||||
{
|
||||
int color;
|
||||
int skyLight;
|
||||
int blockLight;
|
||||
for (LodDirection lodDirection : Box.DIRECTIONS)
|
||||
{
|
||||
if(box.isCulled(lodDirection))
|
||||
continue;
|
||||
|
||||
int verticalFaceIndex = 0;
|
||||
while (box.shouldRenderFace(lodDirection, verticalFaceIndex))
|
||||
{
|
||||
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
|
||||
{
|
||||
color = box.getColor(lodDirection);
|
||||
skyLight = box.getSkyLight(lodDirection, verticalFaceIndex);
|
||||
blockLight = box.getBlockLight();
|
||||
color = ColorUtil.applyLightValue(color, skyLight, blockLight);
|
||||
addPosAndColor(buffer,
|
||||
box.getX(lodDirection, vertexIndex),
|
||||
box.getY(lodDirection, vertexIndex, verticalFaceIndex),
|
||||
box.getZ(lodDirection, vertexIndex),
|
||||
color);
|
||||
}
|
||||
verticalFaceIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* TODO DynamicLodTemplate
|
||||
* Chunks smoothly transition between
|
||||
* each other, unless a neighboring chunk
|
||||
* is at a significantly different height.
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class DynamicLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.bufferBuilding.lodTemplates;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* TODO #21 TriangularLodTemplate
|
||||
* Builds each LOD chunk as a singular rectangular prism.
|
||||
* @author James Seibel
|
||||
* @version 06-16-2021
|
||||
*/
|
||||
public class TriangularLodTemplate extends AbstractLodTemplate
|
||||
{
|
||||
@Override
|
||||
public void addLodToBuffer(LodBufferBuilder buffer, AbstractBlockPosWrapper bufferCenterBlockPos, long data, Map<LodDirection, long[]> adjData,
|
||||
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
|
||||
{
|
||||
ClientApi.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,534 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.lodBuilding;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.LodWorld;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
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.util.SingletonHandler;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorSingletonWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This object is in charge of creating Lod related objects.
|
||||
*
|
||||
* @author Cola
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 10-22-2021
|
||||
*/
|
||||
public class LodBuilder
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IBlockColorSingletonWrapper BLOCK_COLOR = SingletonHandler.get(IBlockColorSingletonWrapper.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
/** If no blocks are found in the area in determineBottomPointForArea return this */
|
||||
public static final short DEFAULT_DEPTH = 0;
|
||||
/** If no blocks are found in the area in determineHeightPointForArea return this */
|
||||
public static final short DEFAULT_HEIGHT = 0;
|
||||
/** Minecraft's max light value */
|
||||
public static final short DEFAULT_MAX_LIGHT = 15;
|
||||
|
||||
|
||||
private final ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||
private final ILodConfigWrapperSingleton config = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* How wide LodDimensions should be in regions <br>
|
||||
* Is automatically set before the first frame in ClientProxy.
|
||||
*/
|
||||
public int defaultDimensionWidthInRegions = 0;
|
||||
|
||||
//public static final boolean useExperimentalLighting = true;
|
||||
|
||||
|
||||
|
||||
|
||||
public LodBuilder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim)
|
||||
{
|
||||
generateLodNodeAsync(chunk, lodWorld, dim, DistanceGenerationMode.FULL);
|
||||
}
|
||||
|
||||
public void generateLodNodeAsync(IChunkWrapper chunk, LodWorld lodWorld, IDimensionTypeWrapper dim, DistanceGenerationMode generationMode)
|
||||
{
|
||||
if (lodWorld == null || lodWorld.getIsWorldNotLoaded())
|
||||
return;
|
||||
|
||||
// don't try to create an LOD object
|
||||
// if for some reason we aren't
|
||||
// given a valid chunk object
|
||||
if (chunk == null)
|
||||
return;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
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
|
||||
LodDimension lodDim;
|
||||
if (lodWorld.getLodDimension(dim) == null)
|
||||
{
|
||||
lodDim = new LodDimension(dim, lodWorld, defaultDimensionWidthInRegions);
|
||||
lodWorld.addLodDimension(lodDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
lodDim = lodWorld.getLodDimension(dim);
|
||||
}
|
||||
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig(generationMode));
|
||||
}
|
||||
catch (IllegalArgumentException | NullPointerException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// if the world changes while LODs are being generated
|
||||
// they will throw errors as they try to access things that no longer
|
||||
// exist.
|
||||
}
|
||||
});
|
||||
lodGenThreadPool.execute(thread);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LodNode for a chunk in the given world.
|
||||
* @throws IllegalArgumentException thrown if either the chunk or world is null.
|
||||
*/
|
||||
public void generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk) throws IllegalArgumentException
|
||||
{
|
||||
generateLodNodeFromChunk(lodDim, chunk, new LodBuilderConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a LodNode for a chunk in the given world.
|
||||
* @throws IllegalArgumentException thrown if either the chunk or world is null.
|
||||
*/
|
||||
public void generateLodNodeFromChunk(LodDimension lodDim, IChunkWrapper chunk, LodBuilderConfig config)
|
||||
throws IllegalArgumentException
|
||||
{
|
||||
if (chunk == null)
|
||||
throw new IllegalArgumentException("generateLodFromChunk given a null chunk");
|
||||
|
||||
int startX;
|
||||
int startZ;
|
||||
|
||||
|
||||
LodRegion region = lodDim.getRegion(chunk.getPos().getRegionX(), chunk.getPos().getRegionZ());
|
||||
if (region == null)
|
||||
return;
|
||||
|
||||
// this happens if a LOD is generated after the user leaves the world.
|
||||
if (MC.getWrappedClientWorld() == null)
|
||||
return;
|
||||
|
||||
// determine how many LODs to generate horizontally
|
||||
byte minDetailLevel = region.getMinDetailLevel();
|
||||
HorizontalResolution detail = DetailDistanceUtil.getLodGenDetail(minDetailLevel);
|
||||
|
||||
|
||||
// determine how many LODs to generate vertically
|
||||
//VerticalQuality verticalQuality = LodConfig.CLIENT.graphics.qualityOption.verticalQuality.get();
|
||||
byte detailLevel = detail.detailLevel;
|
||||
|
||||
|
||||
// generate the LODs
|
||||
int posX;
|
||||
int posZ;
|
||||
for (int i = 0; i < detail.dataPointLengthCount * detail.dataPointLengthCount; i++)
|
||||
{
|
||||
startX = detail.startX[i];
|
||||
startZ = detail.startZ[i];
|
||||
|
||||
long[] data;
|
||||
long[] dataToMergeVertical = createVerticalDataToMerge(detail, chunk, config, startX, startZ);
|
||||
data = DataPointUtil.mergeMultiData(dataToMergeVertical, DataPointUtil.worldHeight / 2 + 1, DetailDistanceUtil.getMaxVerticalData(detailLevel));
|
||||
|
||||
|
||||
//lodDim.clear(detailLevel, posX, posZ);
|
||||
if (data != null && data.length != 0)
|
||||
{
|
||||
posX = LevelPosUtil.convert((byte) 0, chunk.getPos().getX() * 16 + startX, detail.detailLevel);
|
||||
posZ = LevelPosUtil.convert((byte) 0, chunk.getPos().getZ() * 16 + startZ, detail.detailLevel);
|
||||
lodDim.addVerticalData(detailLevel, posX, posZ, data, false);
|
||||
}
|
||||
}
|
||||
lodDim.updateData(LodUtil.CHUNK_DETAIL_LEVEL, chunk.getPos().getX(), chunk.getPos().getZ());
|
||||
}
|
||||
|
||||
/** creates a vertical DataPoint */
|
||||
private long[] createVerticalDataToMerge(HorizontalResolution detail, IChunkWrapper chunk, LodBuilderConfig config, int startX, int startZ)
|
||||
{
|
||||
// equivalent to 2^detailLevel
|
||||
int size = 1 << detail.detailLevel;
|
||||
|
||||
long[] dataToMerge = ThreadMapUtil.getBuilderVerticalArray(detail.detailLevel);
|
||||
int verticalData = DataPointUtil.worldHeight / 2 + 1;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = chunk.getPos();
|
||||
int height;
|
||||
int depth;
|
||||
int color;
|
||||
int light;
|
||||
int lightSky;
|
||||
int lightBlock;
|
||||
int generation = config.distanceGenerationMode.complexity;
|
||||
|
||||
int xRel;
|
||||
int zRel;
|
||||
int xAbs;
|
||||
int yAbs;
|
||||
int zAbs;
|
||||
boolean hasCeiling = MC.getWrappedClientWorld().getDimensionType().hasCeiling();
|
||||
boolean hasSkyLight = MC.getWrappedClientWorld().getDimensionType().hasSkyLight();
|
||||
boolean isDefault;
|
||||
AbstractBlockPosWrapper blockPos = FACTORY.createBlockPos();
|
||||
int index;
|
||||
|
||||
for (index = 0; index < size * size; index++)
|
||||
{
|
||||
xRel = startX + index % size;
|
||||
zRel = startZ + index / size;
|
||||
xAbs = chunkPos.getMinBlockX() + xRel;
|
||||
zAbs = chunkPos.getMinBlockZ() + zRel;
|
||||
|
||||
//Calculate the height of the lod
|
||||
yAbs = DataPointUtil.worldHeight + 1;
|
||||
int count = 0;
|
||||
boolean topBlock = true;
|
||||
while (yAbs > 0)
|
||||
{
|
||||
height = determineHeightPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||
|
||||
// If the lod is at the default height, it must be void data
|
||||
if (height == DEFAULT_HEIGHT)
|
||||
{
|
||||
if (topBlock)
|
||||
dataToMerge[index * verticalData] = DataPointUtil.createVoidDataPoint(generation);
|
||||
break;
|
||||
}
|
||||
|
||||
yAbs = height - 1;
|
||||
// We search light on above air block
|
||||
depth = determineBottomPointFrom(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||
if (hasCeiling && topBlock)
|
||||
{
|
||||
yAbs = depth;
|
||||
blockPos.set(xAbs, yAbs, zAbs);
|
||||
light = getLightValue(chunk, blockPos, true, hasSkyLight, true);
|
||||
color = generateLodColor(chunk, config, xAbs, yAbs, zAbs, blockPos);
|
||||
blockPos.set(xAbs, yAbs - 1, zAbs);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockPos.set(xAbs, yAbs, zAbs);
|
||||
light = getLightValue(chunk, blockPos, hasCeiling, hasSkyLight, topBlock);
|
||||
color = generateLodColor(chunk, config, xRel, yAbs, zRel, blockPos);
|
||||
blockPos.set(xAbs, yAbs + 1, zAbs);
|
||||
}
|
||||
lightBlock = light & 0b1111;
|
||||
lightSky = (light >> 4) & 0b1111;
|
||||
isDefault = ((light >> 8)) == 1;
|
||||
|
||||
dataToMerge[index * verticalData + count] = DataPointUtil.createDataPoint(height, depth, color, lightSky, lightBlock, generation, isDefault);
|
||||
topBlock = false;
|
||||
yAbs = depth - 1;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return dataToMerge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the lowest valid point from the bottom.
|
||||
* Used when creating a vertical LOD.
|
||||
*/
|
||||
private short determineBottomPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||
{
|
||||
short depth = DEFAULT_DEPTH;
|
||||
|
||||
for (int y = yAbs; y >= 0; y--)
|
||||
{
|
||||
blockPos.set(xAbs, y, zAbs);
|
||||
if (!isLayerValidLodPoint(chunk, blockPos))
|
||||
{
|
||||
depth = (short) (y + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return depth;
|
||||
}
|
||||
|
||||
/** Find the highest valid point from the Top */
|
||||
private short determineHeightPointFrom(IChunkWrapper chunk, LodBuilderConfig config, int xAbs, int yAbs, int zAbs, AbstractBlockPosWrapper blockPos)
|
||||
{
|
||||
short height = DEFAULT_HEIGHT;
|
||||
if (config.useHeightmap)
|
||||
height = (short) chunk.getHeightMapValue(xAbs, zAbs);
|
||||
else
|
||||
{
|
||||
for (int y = yAbs; y >= 0; y--)
|
||||
{
|
||||
blockPos.set(xAbs, y, zAbs);
|
||||
if (isLayerValidLodPoint(chunk, blockPos))
|
||||
{
|
||||
height = (short) (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 xRel, int yAbs, int zRel, AbstractBlockPosWrapper blockPos)
|
||||
{
|
||||
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(xRel, yAbs, zRel).getColorForBiome(xRel, zRel);
|
||||
}
|
||||
else
|
||||
{
|
||||
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs, chunk.getPos().getMinBlockZ() + zRel);
|
||||
colorInt = getColorForBlock(chunk, blockPos);
|
||||
|
||||
// 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
|
||||
int aboveColorInt = 0;
|
||||
if (config.client().worldGenerator().getBlocksToAvoid().nonFull || config.client().worldGenerator().getBlocksToAvoid().noCollision)
|
||||
{
|
||||
blockPos.set(chunk.getPos().getMinBlockX() + xRel, yAbs + 1, chunk.getPos().getMinBlockZ() + zRel);
|
||||
aboveColorInt = getColorForBlock(chunk, blockPos);
|
||||
}
|
||||
|
||||
//if (colorInt == 0 && yAbs > 0)
|
||||
// if this block is invisible, check the block below it
|
||||
// colorInt = generateLodColor(chunk, config, xRel, yAbs - 1, zRel, blockPos);
|
||||
|
||||
// override this block's color if there was a block above this
|
||||
// and we were avoiding non-full/non-solid blocks
|
||||
if (aboveColorInt != 0)
|
||||
colorInt = aboveColorInt;
|
||||
}
|
||||
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
/** Gets the light value for the given block position */
|
||||
private int getLightValue(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos, boolean hasCeiling, boolean hasSkyLight, boolean topBlock)
|
||||
{
|
||||
int skyLight = 0;
|
||||
int blockLight;
|
||||
// 1 means the lighting is a guess
|
||||
int isDefault = 0;
|
||||
|
||||
IWorldWrapper world = MC.getWrappedServerWorld();
|
||||
|
||||
int blockBrightness = chunk.getEmittedBrightness(blockPos);
|
||||
// get the air block above or below this block
|
||||
if (hasCeiling && topBlock)
|
||||
blockPos.set(blockPos.getX(), blockPos.getY() - 1, blockPos.getZ());
|
||||
else
|
||||
blockPos.set(blockPos.getX(), blockPos.getY() + 1, blockPos.getZ());
|
||||
|
||||
|
||||
|
||||
if (world != null && !world.isEmpty())
|
||||
{
|
||||
// server world sky light (always accurate)
|
||||
blockLight = world.getBlockLight(blockPos);
|
||||
if (topBlock && !hasCeiling && hasSkyLight)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
{
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(blockPos);
|
||||
//else
|
||||
// skyLight = 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
|
||||
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
{
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
}
|
||||
else
|
||||
skyLight = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
world = MC.getWrappedServerWorld();
|
||||
if (world.isEmpty())
|
||||
return 0;
|
||||
// client world sky light (almost never accurate)
|
||||
blockLight = world.getBlockLight(blockPos);
|
||||
// estimate what the lighting should be
|
||||
if (hasSkyLight || !hasCeiling)
|
||||
{
|
||||
if (topBlock)
|
||||
skyLight = DEFAULT_MAX_LIGHT;
|
||||
else
|
||||
{
|
||||
|
||||
if (hasSkyLight)
|
||||
skyLight = world.getSkyLight(blockPos);
|
||||
//else
|
||||
// skyLight = 0;
|
||||
|
||||
if (!chunk.isLightCorrect() && (skyLight == 0 || skyLight == 15))
|
||||
{
|
||||
// we don't know what the light here is,
|
||||
// lets just take a guess
|
||||
if (blockPos.getY() >= MC.getWrappedClientWorld().getSeaLevel() - 5)
|
||||
{
|
||||
skyLight = 12;
|
||||
isDefault = 1;
|
||||
}
|
||||
else
|
||||
skyLight = 0;
|
||||
}
|
||||
}
|
||||
if (hasSkyLight)
|
||||
skyLight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
blockLight = LodUtil.clamp(0, Math.max(blockLight, blockBrightness), DEFAULT_MAX_LIGHT);
|
||||
|
||||
return blockLight + (skyLight << 4) + (isDefault << 8);
|
||||
}
|
||||
|
||||
/** Returns a color int for the given block. */
|
||||
private int getColorForBlock(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||
{
|
||||
int colorOfBlock;
|
||||
int colorInt;
|
||||
|
||||
int xRel = blockPos.getX() - chunk.getPos().getMinBlockX();
|
||||
int zRel = blockPos.getZ() - chunk.getPos().getMinBlockZ();
|
||||
//int x = blockPos.getX();
|
||||
int y = blockPos.getY();
|
||||
//int z = blockPos.getZ();
|
||||
|
||||
IBlockColorWrapper blockColorWrapper;
|
||||
IBlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
|
||||
|
||||
if (chunk.isWaterLogged(blockPos))
|
||||
blockColorWrapper = BLOCK_COLOR.getWaterColor();
|
||||
else
|
||||
blockColorWrapper = chunk.getBlockColorWrapper(blockPos);
|
||||
|
||||
if (blockShapeWrapper.isToAvoid())
|
||||
return 0;
|
||||
|
||||
colorOfBlock = blockColorWrapper.getColor();
|
||||
|
||||
|
||||
if (blockColorWrapper.hasTint())
|
||||
{
|
||||
IBiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
|
||||
int tintValue;
|
||||
if (blockColorWrapper.hasGrassTint())
|
||||
// grass and green plants
|
||||
tintValue = biome.getGrassTint(0,0);
|
||||
else if (blockColorWrapper.hasFolliageTint())
|
||||
tintValue = biome.getFolliageTint();
|
||||
else
|
||||
//we can reintroduce this with the wrappers
|
||||
tintValue = biome.getWaterTint();
|
||||
|
||||
colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, colorOfBlock);
|
||||
}
|
||||
else
|
||||
colorInt = colorOfBlock;
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
|
||||
/** Is the block at the given blockPos a valid LOD point? */
|
||||
private boolean isLayerValidLodPoint(IChunkWrapper chunk, AbstractBlockPosWrapper blockPos)
|
||||
{
|
||||
if (chunk.isWaterLogged(blockPos))
|
||||
return true;
|
||||
|
||||
boolean nonFullAvoidance = config.client().worldGenerator().getBlocksToAvoid().nonFull;
|
||||
boolean noCollisionAvoidance = config.client().worldGenerator().getBlocksToAvoid().noCollision;
|
||||
|
||||
IBlockShapeWrapper block = chunk.getBlockShapeWrapper(blockPos);
|
||||
return !block.isToAvoid()
|
||||
&& !(nonFullAvoidance && block.isNonFull())
|
||||
&& !(noCollisionAvoidance && block.hasNoCollision());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.lodBuilding;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
|
||||
/**
|
||||
* This is used to easily configure how LodChunks are generated.
|
||||
* Generally this will only be used if we want to generate a
|
||||
* LodChunk using an incomplete Chunk, otherwise the defaults
|
||||
* work best for a fully generated chunk (IE has correct surface blocks).
|
||||
* @author James Seibel
|
||||
* @version 8-14-2021
|
||||
*/
|
||||
public class LodBuilderConfig
|
||||
{
|
||||
/** default: false */
|
||||
public boolean useHeightmap;
|
||||
/** default: false */
|
||||
public boolean useBiomeColors;
|
||||
/** default: true */
|
||||
public boolean useSolidBlocksInColorGen;
|
||||
/** default: server */
|
||||
public DistanceGenerationMode distanceGenerationMode;
|
||||
|
||||
/**
|
||||
* default settings for a normal chunk <br>
|
||||
* useHeightmap = false <br>
|
||||
* useBiomeColors = false <br>
|
||||
* useSolidBlocksInColorGen = true <br>
|
||||
* generationMode = Server <br>
|
||||
*/
|
||||
public LodBuilderConfig()
|
||||
{
|
||||
useHeightmap = false;
|
||||
useBiomeColors = false;
|
||||
useSolidBlocksInColorGen = true;
|
||||
distanceGenerationMode = DistanceGenerationMode.FULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newUseHeightmap default = false
|
||||
* @param newUseBiomeColors default = false
|
||||
* @param newUseSolidBlocksInBiomeColor default = true
|
||||
* @param newDistanceGenerationMode default = Server
|
||||
*/
|
||||
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors,
|
||||
boolean newUseSolidBlocksInBiomeColor, DistanceGenerationMode newDistanceGenerationMode)
|
||||
{
|
||||
useHeightmap = newUseHeightmap;
|
||||
useBiomeColors = newUseBiomeColors;
|
||||
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
|
||||
distanceGenerationMode = newDistanceGenerationMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newUseHeightmap default = false
|
||||
* @param newUseBiomeColors default = false
|
||||
* @param newUseSolidBlocksInBiomeColor default = true
|
||||
*/
|
||||
public LodBuilderConfig(boolean newUseHeightmap, boolean newUseBiomeColors, boolean newUseSolidBlocksInBiomeColor)
|
||||
{
|
||||
this();
|
||||
useHeightmap = newUseHeightmap;
|
||||
useBiomeColors = newUseBiomeColors;
|
||||
useSolidBlocksInColorGen = newUseSolidBlocksInBiomeColor;
|
||||
distanceGenerationMode = newUseHeightmap ? DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT : DistanceGenerationMode.BIOME_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param newDistanceGenerationMode default = Server
|
||||
*/
|
||||
public LodBuilderConfig(DistanceGenerationMode newDistanceGenerationMode)
|
||||
{
|
||||
this();
|
||||
distanceGenerationMode = newDistanceGenerationMode;
|
||||
}
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.worldGeneration;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public class LodGenWorker
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
public static ExecutorService genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
|
||||
private final LodChunkGenThread thread;
|
||||
|
||||
|
||||
|
||||
public LodGenWorker(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper serverWorld)
|
||||
{
|
||||
// just a few sanity checks
|
||||
if (newPos == null)
|
||||
throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
|
||||
|
||||
if (newLodBuilder == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
|
||||
|
||||
if (newLodDimension == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension");
|
||||
|
||||
if (serverWorld == null)
|
||||
throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
|
||||
|
||||
|
||||
|
||||
thread = new LodChunkGenThread(newPos, newGenerationMode,
|
||||
newLodBuilder,
|
||||
newLodDimension, serverWorld);
|
||||
}
|
||||
|
||||
public void queueWork()
|
||||
{
|
||||
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() == DistanceGenerationMode.FULL)
|
||||
{
|
||||
// if we are using FULL generation there is no reason
|
||||
// to queue up a bunch of generation requests,
|
||||
// because MC's internal server (as of 1.16.5) only
|
||||
// responds with a single thread. And we don't
|
||||
// want to cause more lag then necessary or queue up
|
||||
// requests that may end up being unneeded.
|
||||
thread.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every other method can
|
||||
// be done asynchronously
|
||||
genThreads.execute(thread);
|
||||
}
|
||||
|
||||
// useful for debugging
|
||||
// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static class LodChunkGenThread implements Runnable
|
||||
{
|
||||
private AbstractWorldGeneratorWrapper worldGenWrapper;
|
||||
|
||||
public final LodDimension lodDim;
|
||||
public final DistanceGenerationMode generationMode;
|
||||
|
||||
private final AbstractChunkPosWrapper pos;
|
||||
|
||||
public LodChunkGenThread(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper worldWrapper)
|
||||
{
|
||||
worldGenWrapper = FACTORY.createWorldGenerator(newLodBuilder, newLodDimension, worldWrapper);
|
||||
|
||||
pos = newPos;
|
||||
generationMode = newGenerationMode;
|
||||
lodDim = newLodDimension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
|
||||
if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS))
|
||||
{
|
||||
switch (generationMode)
|
||||
{
|
||||
case NONE:
|
||||
// don't generate
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
// fastest
|
||||
worldGenWrapper.generateBiomesOnly(pos, generationMode);
|
||||
break;
|
||||
case SURFACE:
|
||||
// faster
|
||||
worldGenWrapper.generateSurface(pos);
|
||||
break;
|
||||
case FEATURES:
|
||||
// fast
|
||||
worldGenWrapper.generateFeatures(pos);
|
||||
break;
|
||||
case FULL:
|
||||
// very slow
|
||||
worldGenWrapper.generateFull(pos);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z));
|
||||
// if (dataExistence)
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
|
||||
// else
|
||||
// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
|
||||
|
||||
// shows the pool size, active threads, queued tasks and completed tasks
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
|
||||
// long endTime = System.currentTimeMillis();
|
||||
// System.out.println(endTime - startTime);
|
||||
|
||||
}// if in range
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// decrement how many threads are running
|
||||
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
|
||||
|
||||
// this position is no longer being generated
|
||||
LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos);
|
||||
}
|
||||
}// run
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Stops the current genThreads if they are running
|
||||
* and then recreates the Executor service. <br><br>
|
||||
* <p>
|
||||
* This is done to clear any outstanding tasks
|
||||
* that may exist after the player leaves their current world.
|
||||
* If this isn't done unfinished tasks may be left in the queue
|
||||
* preventing new LodChunks form being generated.
|
||||
*/
|
||||
public static void restartExecutorService()
|
||||
{
|
||||
if (genThreads != null && !genThreads.isShutdown())
|
||||
{
|
||||
genThreads.shutdownNow();
|
||||
}
|
||||
genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
}
|
||||
|
||||
}
|
||||
-209
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.builders.worldGeneration;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.render.LodRenderer;
|
||||
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.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* A singleton that handles all long distance LOD world generation.
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
public class LodWorldGenerator
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
|
||||
/** This holds the thread used to create LOD generation requests off the main thread. */
|
||||
private final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
|
||||
|
||||
/** we only want to queue up one generator thread at a time */
|
||||
private boolean generatorThreadRunning = false;
|
||||
|
||||
/**
|
||||
* How many chunks to generate outside the player's view distance at one
|
||||
* time. (or more specifically how many requests to make at one time). I
|
||||
* multiply by 8 to make sure there is always a buffer of chunk requests, to
|
||||
* make sure the CPU is always busy, and we can generate LODs as quickly as
|
||||
* possible.
|
||||
*/
|
||||
public int maxChunkGenRequests;
|
||||
|
||||
/**
|
||||
* This keeps track of how many chunk generation requests are on going. This is
|
||||
* to limit how many chunks are queued at once. To prevent chunks from being
|
||||
* generated for a long time in an area the player is no longer in.
|
||||
*/
|
||||
public final AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0);
|
||||
|
||||
public final Set<AbstractChunkPosWrapper> positionsWaitingToBeGenerated = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Singleton copy of this object
|
||||
*/
|
||||
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
|
||||
|
||||
|
||||
|
||||
private LodWorldGenerator()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up LodNodeGenWorkers for the given lodDimension.
|
||||
* @param renderer needed so the LodNodeGenWorkers can flag that the
|
||||
* buffers need to be rebuilt.
|
||||
*/
|
||||
public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder)
|
||||
{
|
||||
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() != DistanceGenerationMode.NONE
|
||||
&& !generatorThreadRunning
|
||||
&& MC.hasSinglePlayerServer())
|
||||
{
|
||||
// the thread is now running, don't queue up another thread
|
||||
generatorThreadRunning = true;
|
||||
|
||||
// just in case the config changed
|
||||
maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * 8;
|
||||
|
||||
Thread generatorThread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
// round the player's block position down to the nearest chunk BlockPos
|
||||
int playerPosX = MC.getPlayerBlockPos().getX();
|
||||
int playerPosZ = MC.getPlayerBlockPos().getZ();
|
||||
|
||||
|
||||
//=======================================//
|
||||
// fill in positionsWaitingToBeGenerated //
|
||||
//=======================================//
|
||||
|
||||
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(
|
||||
maxChunkGenRequests,
|
||||
playerPosX,
|
||||
playerPosZ);
|
||||
|
||||
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
int nearIndex = 0;
|
||||
int farIndex = 0;
|
||||
|
||||
for (int i = 0; i < posToGenerate.getNumberOfPos(); i++)
|
||||
{
|
||||
// I wish there was a way to compress this code, but I'm not aware of
|
||||
// an easy way to do so.
|
||||
|
||||
// add the near positions
|
||||
if (posToGenerate.getNthDetail(nearIndex, true) != 0 && nearIndex < posToGenerate.getNumberOfNearPos())
|
||||
{
|
||||
detailLevel = (byte) (posToGenerate.getNthDetail(nearIndex, true) - 1);
|
||||
posX = posToGenerate.getNthPosX(nearIndex, true);
|
||||
posZ = posToGenerate.getNthPosZ(nearIndex, true);
|
||||
nearIndex++;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
|
||||
// prevent generating the same chunk multiple times
|
||||
if (positionsWaitingToBeGenerated.contains(chunkPos))
|
||||
continue;
|
||||
|
||||
// don't add more to the generation queue then allowed
|
||||
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||
break;
|
||||
|
||||
positionsWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||
genWorker.queueWork();
|
||||
}
|
||||
|
||||
|
||||
// add the far positions
|
||||
if (posToGenerate.getNthDetail(farIndex, false) != 0 && farIndex < posToGenerate.getNumberOfFarPos())
|
||||
{
|
||||
detailLevel = (byte) (posToGenerate.getNthDetail(farIndex, false) - 1);
|
||||
posX = posToGenerate.getNthPosX(farIndex, false);
|
||||
posZ = posToGenerate.getNthPosZ(farIndex, false);
|
||||
farIndex++;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
|
||||
// don't add more to the generation queue then allowed
|
||||
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||
continue;
|
||||
//break;
|
||||
|
||||
// prevent generating the same chunk multiple times
|
||||
if (positionsWaitingToBeGenerated.contains(chunkPos))
|
||||
continue;
|
||||
|
||||
positionsWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
|
||||
genWorker.queueWork();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// this shouldn't ever happen, but just in case
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
generatorThreadRunning = false;
|
||||
}
|
||||
});
|
||||
|
||||
mainGenThread.execute(generatorThread);
|
||||
} // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning
|
||||
} // queueGenerationRequests
|
||||
|
||||
}
|
||||
@@ -1,532 +0,0 @@
|
||||
package com.seibel.lod.core.enums;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Vec3i;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of Minecraft's
|
||||
* Direction enum.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public enum LodDirection
|
||||
{
|
||||
DOWN(0, 1, -1, "down", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.Y, new Vec3i(0, -1, 0)),
|
||||
UP(1, 0, -1, "up", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Y, new Vec3i(0, 1, 0)),
|
||||
NORTH(2, 3, 2, "north", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.Z, new Vec3i(0, 0, -1)),
|
||||
SOUTH(3, 2, 0, "south", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Z, new Vec3i(0, 0, 1)),
|
||||
WEST(4, 5, 1, "west", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.X, new Vec3i(-1, 0, 0)),
|
||||
EAST(5, 4, 3, "east", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.X, new Vec3i(1, 0, 0));
|
||||
|
||||
// private final int data3d;
|
||||
// private final int oppositeIndex;
|
||||
// private final int data2d;
|
||||
|
||||
private final String name;
|
||||
private final LodDirection.Axis axis;
|
||||
private final LodDirection.AxisDirection axisDirection;
|
||||
private final Vec3i normal;
|
||||
private static final LodDirection[] VALUES = values();
|
||||
|
||||
private static final Map<String, LodDirection> BY_NAME = Arrays.stream(VALUES).collect(Collectors.toMap(LodDirection::getName, (p_199787_0_) ->
|
||||
{
|
||||
return p_199787_0_;
|
||||
}));
|
||||
|
||||
// private static final LodDirection[] BY_3D_DATA = Arrays.stream(VALUES).sorted(Comparator.comparingInt((p_199790_0_) ->
|
||||
// {
|
||||
// return p_199790_0_.data3d;
|
||||
// })).toArray((p_199788_0_) ->
|
||||
// {
|
||||
// return new LodDirection[p_199788_0_];
|
||||
// });
|
||||
//
|
||||
// private static final LodDirection[] BY_2D_DATA = Arrays.stream(VALUES).filter((p_199786_0_) ->
|
||||
// {
|
||||
// return p_199786_0_.getAxis().isHorizontal();
|
||||
// }).sorted(Comparator.comparingInt((p_199789_0_) ->
|
||||
// {
|
||||
// return p_199789_0_.data2d;
|
||||
// })).toArray((p_199791_0_) ->
|
||||
// {
|
||||
// return new LodDirection[p_199791_0_];
|
||||
// });
|
||||
|
||||
// private static final Long2ObjectMap<LodDirection> BY_NORMAL = Arrays.stream(VALUES).collect(Collectors.toMap((p_218385_0_) ->
|
||||
// {
|
||||
// return (new BlockPos(p_218385_0_.getNormal())).asLong();
|
||||
// }, (p_218384_0_) ->
|
||||
// {
|
||||
// return p_218384_0_;
|
||||
// }, (p_218386_0_, p_218386_1_) ->
|
||||
// {
|
||||
// throw new IllegalArgumentException("Duplicate keys");
|
||||
// }, Long2ObjectOpenHashMap::new));
|
||||
|
||||
|
||||
|
||||
private LodDirection(int p_i46016_3_, int p_i46016_4_, int p_i46016_5_, String p_i46016_6_, LodDirection.AxisDirection p_i46016_7_, LodDirection.Axis p_i46016_8_, Vec3i p_i46016_9_)
|
||||
{
|
||||
// this.data3d = p_i46016_3_;
|
||||
// this.data2d = p_i46016_5_;
|
||||
// this.oppositeIndex = p_i46016_4_;
|
||||
this.name = p_i46016_6_;
|
||||
this.axis = p_i46016_8_;
|
||||
this.axisDirection = p_i46016_7_;
|
||||
this.normal = p_i46016_9_;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// public static LodDirection[] orderedByNearest(Entity p_196054_0_)
|
||||
// {
|
||||
// float f = p_196054_0_.getViewXRot(1.0F) * ((float) Math.PI / 180F);
|
||||
// float f1 = -p_196054_0_.getViewYRot(1.0F) * ((float) Math.PI / 180F);
|
||||
// float f2 = MathHelper.sin(f);
|
||||
// float f3 = MathHelper.cos(f);
|
||||
// float f4 = MathHelper.sin(f1);
|
||||
// float f5 = MathHelper.cos(f1);
|
||||
// boolean flag = f4 > 0.0F;
|
||||
// boolean flag1 = f2 < 0.0F;
|
||||
// boolean flag2 = f5 > 0.0F;
|
||||
// float f6 = flag ? f4 : -f4;
|
||||
// float f7 = flag1 ? -f2 : f2;
|
||||
// float f8 = flag2 ? f5 : -f5;
|
||||
// float f9 = f6 * f3;
|
||||
// float f10 = f8 * f3;
|
||||
// LodDirection lodDirection = flag ? EAST : WEST;
|
||||
// LodDirection direction1 = flag1 ? UP : DOWN;
|
||||
// LodDirection direction2 = flag2 ? SOUTH : NORTH;
|
||||
// if (f6 > f8)
|
||||
// {
|
||||
// if (f7 > f9)
|
||||
// {
|
||||
// return makeDirectionArray(direction1, lodDirection, direction2);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return f10 > f7 ? makeDirectionArray(lodDirection, direction2, direction1) : makeDirectionArray(lodDirection, direction1, direction2);
|
||||
// }
|
||||
// }
|
||||
// else if (f7 > f10)
|
||||
// {
|
||||
// return makeDirectionArray(direction1, direction2, lodDirection);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// return f9 > f7 ? makeDirectionArray(direction2, lodDirection, direction1) : makeDirectionArray(direction2, direction1, lodDirection);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private static LodDirection[] makeDirectionArray(LodDirection p_196053_0_, LodDirection p_196053_1_, LodDirection p_196053_2_)
|
||||
// {
|
||||
// return new LodDirection[] { p_196053_0_, p_196053_1_, p_196053_2_, p_196053_2_.getOpposite(), p_196053_1_.getOpposite(), p_196053_0_.getOpposite() };
|
||||
// }
|
||||
|
||||
// public static LodDirection rotate(Mat4f p_229385_0_, LodDirection p_229385_1_)
|
||||
// {
|
||||
// Vec3i Vec3i = p_229385_1_.getNormal();
|
||||
// Vector4f vector4f = new Vector4f(Vec3i.getX(), Vec3i.getY(), Vec3i.getZ(), 0.0F);
|
||||
// vector4f.transform(p_229385_0_);
|
||||
// return getNearest(vector4f.x(), vector4f.y(), vector4f.z());
|
||||
// }
|
||||
|
||||
// public Quaternion getRotation()
|
||||
// {
|
||||
// Quaternion quaternion = Vector3f.XP.rotationDegrees(90.0F);
|
||||
// switch (this)
|
||||
// {
|
||||
// case DOWN:
|
||||
// return Vector3f.XP.rotationDegrees(180.0F);
|
||||
// case UP:
|
||||
// return Quaternion.ONE.copy();
|
||||
// case NORTH:
|
||||
// quaternion.mul(Vector3f.ZP.rotationDegrees(180.0F));
|
||||
// return quaternion;
|
||||
// case SOUTH:
|
||||
// return quaternion;
|
||||
// case WEST:
|
||||
// quaternion.mul(Vector3f.ZP.rotationDegrees(90.0F));
|
||||
// return quaternion;
|
||||
// case EAST:
|
||||
// default:
|
||||
// quaternion.mul(Vector3f.ZP.rotationDegrees(-90.0F));
|
||||
// return quaternion;
|
||||
// }
|
||||
// }
|
||||
|
||||
// public int get3DDataValue()
|
||||
// {
|
||||
// return this.data3d;
|
||||
// }
|
||||
//
|
||||
// public int get2DDataValue()
|
||||
// {
|
||||
// return this.data2d;
|
||||
// }
|
||||
|
||||
public LodDirection.AxisDirection getAxisDirection()
|
||||
{
|
||||
return this.axisDirection;
|
||||
}
|
||||
|
||||
// public LodDirection getOpposite()
|
||||
// {
|
||||
// return from3DDataValue(this.oppositeIndex);
|
||||
// }
|
||||
|
||||
public LodDirection getClockWise()
|
||||
{
|
||||
switch (this)
|
||||
{
|
||||
case NORTH:
|
||||
return EAST;
|
||||
case SOUTH:
|
||||
return WEST;
|
||||
case WEST:
|
||||
return NORTH;
|
||||
case EAST:
|
||||
return SOUTH;
|
||||
default:
|
||||
throw new IllegalStateException("Unable to get Y-rotated facing of " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public LodDirection getCounterClockWise()
|
||||
{
|
||||
switch (this)
|
||||
{
|
||||
case NORTH:
|
||||
return WEST;
|
||||
case SOUTH:
|
||||
return EAST;
|
||||
case WEST:
|
||||
return SOUTH;
|
||||
case EAST:
|
||||
return NORTH;
|
||||
default:
|
||||
throw new IllegalStateException("Unable to get CCW facing of " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public LodDirection.Axis getAxis()
|
||||
{
|
||||
return this.axis;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LodDirection byName(@Nullable String name)
|
||||
{
|
||||
return name == null ? null : BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
// public static LodDirection from3DDataValue(int p_82600_0_)
|
||||
// {
|
||||
// return BY_3D_DATA[MathHelper.abs(p_82600_0_ % BY_3D_DATA.length)];
|
||||
// }
|
||||
//
|
||||
// public static LodDirection from2DDataValue(int p_176731_0_)
|
||||
// {
|
||||
// return BY_2D_DATA[MathHelper.abs(p_176731_0_ % BY_2D_DATA.length)];
|
||||
// }
|
||||
|
||||
// @Nullable
|
||||
// public static LodDirection fromNormal(int p_218383_0_, int p_218383_1_, int p_218383_2_)
|
||||
// {
|
||||
// return BY_NORMAL.get(BlockPos.asLong(p_218383_0_, p_218383_1_, p_218383_2_));
|
||||
// }
|
||||
|
||||
// public static LodDirection fromYRot(double p_176733_0_)
|
||||
// {
|
||||
// return from2DDataValue(MathHelper.floor(p_176733_0_ / 90.0D + 0.5D) & 3);
|
||||
// }
|
||||
|
||||
public static LodDirection fromAxisAndDirection(LodDirection.Axis p_211699_0_, LodDirection.AxisDirection p_211699_1_)
|
||||
{
|
||||
switch (p_211699_0_)
|
||||
{
|
||||
case X:
|
||||
return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? EAST : WEST;
|
||||
case Y:
|
||||
return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? UP : DOWN;
|
||||
case Z:
|
||||
default:
|
||||
return p_211699_1_ == LodDirection.AxisDirection.POSITIVE ? SOUTH : NORTH;
|
||||
}
|
||||
}
|
||||
|
||||
// public float toYRot()
|
||||
// {
|
||||
// return (this.data2d & 3) * 90;
|
||||
// }
|
||||
|
||||
// public static LodDirection getRandom(Random p_239631_0_)
|
||||
// {
|
||||
// return Util.getRandom(VALUES, p_239631_0_);
|
||||
// }
|
||||
|
||||
// public static LodDirection getNearest(double p_210769_0_, double p_210769_2_, double p_210769_4_)
|
||||
// {
|
||||
// return getNearest((float) p_210769_0_, (float) p_210769_2_, (float) p_210769_4_);
|
||||
// }
|
||||
|
||||
// public static LodDirection getNearest(float p_176737_0_, float p_176737_1_, float p_176737_2_)
|
||||
// {
|
||||
// LodDirection lodDirection = NORTH;
|
||||
// float f = Float.MIN_VALUE;
|
||||
//
|
||||
// for (LodDirection direction1 : VALUES)
|
||||
// {
|
||||
// float f1 = p_176737_0_ * direction1.normal.x + p_176737_1_ * direction1.normal.y + p_176737_2_ * direction1.normal.z;
|
||||
// if (f1 > f)
|
||||
// {
|
||||
// f = f1;
|
||||
// lodDirection = direction1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return lodDirection;
|
||||
// }
|
||||
|
||||
public static LodDirection get(LodDirection.AxisDirection p_181076_0_, LodDirection.Axis p_181076_1_)
|
||||
{
|
||||
for (LodDirection lodDirection : VALUES)
|
||||
{
|
||||
if (lodDirection.getAxisDirection() == p_181076_0_ && lodDirection.getAxis() == p_181076_1_)
|
||||
{
|
||||
return lodDirection;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("No such direction: " + p_181076_0_ + " " + p_181076_1_);
|
||||
}
|
||||
|
||||
public Vec3i getNormal()
|
||||
{
|
||||
return this.normal;
|
||||
}
|
||||
|
||||
// public boolean isFacingAngle(float p_243532_1_)
|
||||
// {
|
||||
// float f = p_243532_1_ * ((float) Math.PI / 180F);
|
||||
// float f1 = -MathHelper.sin(f);
|
||||
// float f2 = MathHelper.cos(f);
|
||||
// return this.normal.getX() * f1 + this.normal.getZ() * f2 > 0.0F;
|
||||
// }
|
||||
|
||||
public static enum Axis implements Predicate<LodDirection>
|
||||
{
|
||||
X("x")
|
||||
{
|
||||
@Override
|
||||
public int choose(int x, int y, int z)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double choose(double x, double y, double z)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
},
|
||||
Y("y")
|
||||
{
|
||||
@Override
|
||||
public int choose(int x, int y, int z)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double choose(double x, double y, double z)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
},
|
||||
Z("z")
|
||||
{
|
||||
@Override
|
||||
public int choose(int x, int y, int z)
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double choose(double x, double y, double z)
|
||||
{
|
||||
return z;
|
||||
}
|
||||
};
|
||||
|
||||
private static final LodDirection.Axis[] VALUES = values();
|
||||
|
||||
private static final Map<String, LodDirection.Axis> BY_NAME = Arrays.stream(VALUES).collect(Collectors.toMap(LodDirection.Axis::getName, (p_199785_0_) ->
|
||||
{
|
||||
return p_199785_0_;
|
||||
}));
|
||||
private final String name;
|
||||
|
||||
private Axis(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static LodDirection.Axis byName(String name)
|
||||
{
|
||||
return BY_NAME.get(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean isVertical()
|
||||
{
|
||||
return this == Y;
|
||||
}
|
||||
|
||||
public boolean isHorizontal()
|
||||
{
|
||||
return this == X || this == Z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
// public static LodDirection.Axis getRandom(Random p_239634_0_)
|
||||
// {
|
||||
// return Util.getRandom(VALUES, p_239634_0_);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean test(@Nullable LodDirection p_test_1_)
|
||||
{
|
||||
return p_test_1_ != null && p_test_1_.getAxis() == this;
|
||||
}
|
||||
|
||||
// public LodDirection.Plane getPlane()
|
||||
// {
|
||||
// switch (this)
|
||||
// {
|
||||
// case X:
|
||||
// case Z:
|
||||
// return LodDirection.Plane.HORIZONTAL;
|
||||
// case Y:
|
||||
// return LodDirection.Plane.VERTICAL;
|
||||
// default:
|
||||
// throw new Error("Someone's been tampering with the universe!");
|
||||
// }
|
||||
// }
|
||||
|
||||
public abstract int choose(int p_196052_1_, int p_196052_2_, int p_196052_3_);
|
||||
|
||||
public abstract double choose(double p_196051_1_, double p_196051_3_, double p_196051_5_);
|
||||
}
|
||||
|
||||
public static enum AxisDirection
|
||||
{
|
||||
POSITIVE(1, "Towards positive"),
|
||||
NEGATIVE(-1, "Towards negative");
|
||||
|
||||
private final int step;
|
||||
private final String name;
|
||||
|
||||
private AxisDirection(int newStep, String newName)
|
||||
{
|
||||
this.step = newStep;
|
||||
this.name = newName;
|
||||
}
|
||||
|
||||
public int getStep()
|
||||
{
|
||||
return this.step;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public LodDirection.AxisDirection opposite()
|
||||
{
|
||||
return this == POSITIVE ? NEGATIVE : POSITIVE;
|
||||
}
|
||||
}
|
||||
|
||||
// public static enum Plane implements Iterable<LodDirection>, Predicate<LodDirection>
|
||||
// {
|
||||
// HORIZONTAL(new LodDirection[] { LodDirection.NORTH, LodDirection.EAST, LodDirection.SOUTH, LodDirection.WEST }, new LodDirection.Axis[] { LodDirection.Axis.X, LodDirection.Axis.Z }),
|
||||
// VERTICAL(new LodDirection[] { LodDirection.UP, LodDirection.DOWN }, new LodDirection.Axis[] { LodDirection.Axis.Y });
|
||||
//
|
||||
// private final LodDirection[] faces;
|
||||
// private final LodDirection.Axis[] axis;
|
||||
//
|
||||
// private Plane(LodDirection[] p_i49393_3_, LodDirection.Axis[] p_i49393_4_)
|
||||
// {
|
||||
// this.faces = p_i49393_3_;
|
||||
// this.axis = p_i49393_4_;
|
||||
// }
|
||||
//
|
||||
// public LodDirection getRandomDirection(Random p_179518_1_)
|
||||
// {
|
||||
// return Util.getRandom(this.faces, p_179518_1_);
|
||||
// }
|
||||
//
|
||||
// public LodDirection.Axis getRandomAxis(Random p_244803_1_)
|
||||
// {
|
||||
// return Util.getRandom(this.axis, p_244803_1_);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean test(@Nullable LodDirection p_test_1_)
|
||||
// {
|
||||
// return p_test_1_ != null && p_test_1_.getAxis().getPlane() == this;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public Iterator<LodDirection> iterator()
|
||||
// {
|
||||
// return Iterators.forArray(this.faces);
|
||||
// }
|
||||
//
|
||||
// public Stream<LodDirection> stream()
|
||||
// {
|
||||
// return Arrays.stream(this.faces);
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
public String getSerializedName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums;
|
||||
|
||||
/**
|
||||
* ServerWorld, ClientWorld, Unknown
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public enum WorldType
|
||||
{
|
||||
ServerWorld,
|
||||
ClientWorld,
|
||||
Unknown
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* heightmap <br>
|
||||
* multi_lod <br>
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 11-16-2021
|
||||
*/
|
||||
public enum BlocksToAvoid
|
||||
{
|
||||
NONE(false, false),
|
||||
|
||||
NON_FULL(true, false),
|
||||
|
||||
NO_COLLISION(false, true),
|
||||
|
||||
BOTH(true, true);
|
||||
|
||||
public final boolean nonFull;
|
||||
public final boolean noCollision;
|
||||
|
||||
BlocksToAvoid(boolean nonFull, boolean noCollision)
|
||||
{
|
||||
this.nonFull = nonFull;
|
||||
this.noCollision = noCollision;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* FREQUENT <br>
|
||||
* NORMAL <br>
|
||||
* RARE <br>
|
||||
* <br>
|
||||
* Determines how fast the buffers need to be regenerated
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
public enum BufferRebuildTimes
|
||||
{
|
||||
FREQUENT(1000, 500, 2500),
|
||||
|
||||
NORMAL(2000, 1000, 5000),
|
||||
|
||||
RARE(5000, 2000, 10000);
|
||||
|
||||
public final int playerMoveTimeout;
|
||||
public final int renderedChunkTimeout;
|
||||
public final int chunkChangeTimeout;
|
||||
|
||||
BufferRebuildTimes(int playerMoveTimeout, int renderedChunkTimeout, int chunkChangeTimeout)
|
||||
{
|
||||
this.playerMoveTimeout = playerMoveTimeout;
|
||||
this.renderedChunkTimeout = renderedChunkTimeout;
|
||||
this.chunkChangeTimeout = chunkChangeTimeout;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* NONE <br>
|
||||
* BIOME_ONLY <br>
|
||||
* BIOME_ONLY_SIMULATE_HEIGHT <br>
|
||||
* SURFACE <br>
|
||||
* FEATURES <br>
|
||||
* SERVER <br><br>
|
||||
* <p>
|
||||
* In order of fastest to slowest.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leonardo Amato
|
||||
* @version 8-7-2021
|
||||
*/
|
||||
public enum DistanceGenerationMode
|
||||
{
|
||||
/**
|
||||
* Don't generate anything
|
||||
*/
|
||||
NONE((byte) 0),
|
||||
|
||||
/**
|
||||
* Only generate the biomes and use biome
|
||||
* grass/foliage color, water color, or ice color
|
||||
* to generate the color.
|
||||
* Doesn't generate height, everything is shown at sea level.
|
||||
* Multithreaded - Fastest (2-5 ms)
|
||||
*/
|
||||
BIOME_ONLY((byte) 1),
|
||||
|
||||
/**
|
||||
* Same as BIOME_ONLY, except instead
|
||||
* of always using sea level as the LOD height
|
||||
* different biome types (mountain, ocean, forest, etc.)
|
||||
* use predetermined heights to simulate having height data.
|
||||
*/
|
||||
BIOME_ONLY_SIMULATE_HEIGHT((byte) 2),
|
||||
|
||||
/**
|
||||
* Generate the world surface,
|
||||
* this does NOT include caves, trees,
|
||||
* or structures.
|
||||
* Multithreaded - Faster (10-20 ms)
|
||||
*/
|
||||
SURFACE((byte) 3),
|
||||
|
||||
/**
|
||||
* Generate everything except structures.
|
||||
* NOTE: This may cause world generation bugs or instability,
|
||||
* since some features cause concurrentModification exceptions.
|
||||
* Multithreaded - Fast (15-20 ms)
|
||||
*/
|
||||
FEATURES((byte) 4),
|
||||
|
||||
/**
|
||||
* Ask the server to generate/load each chunk.
|
||||
* This is the most compatible, but causes server/simulation lag.
|
||||
* This will also show player made structures if you
|
||||
* are adding the mod on a pre-existing world.
|
||||
* Singlethreaded - Slow (15-50 ms, with spikes up to 200 ms)
|
||||
*/
|
||||
FULL((byte) 5);
|
||||
|
||||
|
||||
/**
|
||||
* The higher the number the more complete the generation is.
|
||||
*/
|
||||
public final byte complexity;
|
||||
|
||||
DistanceGenerationMode(byte complexity)
|
||||
{
|
||||
this.complexity = complexity;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* Near_First <br>
|
||||
* Far_First <br>
|
||||
* <br>
|
||||
* Determines which LODs should have priority when generating
|
||||
* outside the normal view distance.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
public enum GenerationPriority
|
||||
{
|
||||
NEAR_FIRST,
|
||||
|
||||
FAR_FIRST
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* Buffer_Storage, Sub_Data, Buffer_Mapping
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
*/
|
||||
public enum GpuUploadMethod
|
||||
{
|
||||
/** Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. */
|
||||
BUFFER_STORAGE,
|
||||
|
||||
/** Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. */
|
||||
SUB_DATA,
|
||||
|
||||
/** Fast rendering but will stutter when uploading. */
|
||||
DATA,
|
||||
|
||||
/** May end up storing buffers in System memory. Slower rendering but won't stutter when uploading. */
|
||||
BUFFER_MAPPING,
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* Lowest <br>
|
||||
* Low <br>
|
||||
* Medium <br>
|
||||
* High <br>
|
||||
* <br>
|
||||
* this indicates the base of the quadratic function we use for the quality drop off
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-29-2021
|
||||
*/
|
||||
public enum HorizontalQuality
|
||||
{
|
||||
/** 1.0 AKA Linear */
|
||||
LOWEST(1.0f),
|
||||
|
||||
/** exponent 1.5 */
|
||||
LOW(1.5f),
|
||||
|
||||
/** exponent 2.0 */
|
||||
MEDIUM(2.0f),
|
||||
|
||||
/** exponent 2.2 */
|
||||
HIGH(2.2f);
|
||||
|
||||
public final double quadraticBase;
|
||||
|
||||
HorizontalQuality(double distanceUnit)
|
||||
{
|
||||
this.quadraticBase = distanceUnit;
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* chunk <Br>
|
||||
* half_chunk <Br>
|
||||
* four_blocks <br>
|
||||
* two_blocks <Br>
|
||||
* block <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
public enum HorizontalResolution
|
||||
{
|
||||
/** render 256 LODs for each chunk */
|
||||
BLOCK(16, 0),
|
||||
|
||||
/** render 64 LODs for each chunk */
|
||||
TWO_BLOCKS(8, 1),
|
||||
|
||||
/** render 16 LODs for each chunk */
|
||||
FOUR_BLOCKS(4, 2),
|
||||
|
||||
/** render 4 LODs for each chunk */
|
||||
HALF_CHUNK(2, 3),
|
||||
|
||||
/** render 1 LOD for each chunk */
|
||||
CHUNK(1, 4);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* How many DataPoints should
|
||||
* be drawn per side, per LodChunk
|
||||
*/
|
||||
public final int dataPointLengthCount;
|
||||
|
||||
/** How wide each LOD DataPoint is */
|
||||
public final int dataPointWidth;
|
||||
|
||||
/**
|
||||
* This is the same as detailLevel in LodQuadTreeNode,
|
||||
* lowest is 0 highest is 9
|
||||
*/
|
||||
public final byte detailLevel;
|
||||
|
||||
/* Start/End X/Z give the block positions
|
||||
* for each individual dataPoint in a LodChunk */
|
||||
public final int[] startX;
|
||||
public final int[] startZ;
|
||||
|
||||
public final int[] endX;
|
||||
public final int[] endZ;
|
||||
|
||||
|
||||
/**
|
||||
* 1st dimension: LodDetail.detailLevel <br>
|
||||
* 2nd dimension: An array of all LodDetails that are less than or <br>
|
||||
* equal to that detailLevel
|
||||
*/
|
||||
private static HorizontalResolution[][] lowerDetailArrays;
|
||||
|
||||
|
||||
|
||||
|
||||
HorizontalResolution(int newLengthCount, int newDetailLevel)
|
||||
{
|
||||
detailLevel = (byte) newDetailLevel;
|
||||
dataPointLengthCount = newLengthCount;
|
||||
dataPointWidth = 16 / dataPointLengthCount;
|
||||
|
||||
startX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
endX = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
|
||||
startZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
endZ = new int[dataPointLengthCount * dataPointLengthCount];
|
||||
|
||||
|
||||
int index = 0;
|
||||
for (int x = 0; x < newLengthCount; x++)
|
||||
{
|
||||
for (int z = 0; z < newLengthCount; z++)
|
||||
{
|
||||
startX[index] = x * dataPointWidth;
|
||||
startZ[index] = z * dataPointWidth;
|
||||
|
||||
endX[index] = (x * dataPointWidth) + dataPointWidth;
|
||||
endZ[index] = (z * dataPointWidth) + dataPointWidth;
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
}// constructor
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array of all LodDetails that have a detail level
|
||||
* that is less than or equal to the given LodDetail
|
||||
*/
|
||||
public static HorizontalResolution[] getSelfAndLowerDetails(HorizontalResolution detail)
|
||||
{
|
||||
if (lowerDetailArrays == null)
|
||||
{
|
||||
// run first time setup
|
||||
lowerDetailArrays = new HorizontalResolution[HorizontalResolution.values().length][];
|
||||
|
||||
// go through each LodDetail
|
||||
for (HorizontalResolution currentDetail : HorizontalResolution.values())
|
||||
{
|
||||
ArrayList<HorizontalResolution> lowerDetails = new ArrayList<>();
|
||||
|
||||
// find the details lower than currentDetail
|
||||
for (HorizontalResolution compareDetail : HorizontalResolution.values())
|
||||
{
|
||||
if (currentDetail.detailLevel <= compareDetail.detailLevel)
|
||||
{
|
||||
lowerDetails.add(compareDetail);
|
||||
}
|
||||
}
|
||||
|
||||
// have the highest detail item first in the list
|
||||
Collections.sort(lowerDetails);
|
||||
Collections.reverse(lowerDetails);
|
||||
|
||||
lowerDetailArrays[currentDetail.detailLevel] = lowerDetails.toArray(new HorizontalResolution[lowerDetails.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
return lowerDetailArrays[detail.detailLevel];
|
||||
}
|
||||
|
||||
/** Returns what detail level should be used at a given distance and maxDistance. */
|
||||
public static HorizontalResolution getDetailForDistance(HorizontalResolution maxDetailLevel, int distance, int maxDistance)
|
||||
{
|
||||
HorizontalResolution[] lowerDetails = getSelfAndLowerDetails(maxDetailLevel);
|
||||
int distanceBetweenDetails = maxDistance / lowerDetails.length;
|
||||
int index = LodUtil.clamp(0, distance / distanceBetweenDetails, lowerDetails.length - 1);
|
||||
|
||||
return lowerDetails[index];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* Low <br>
|
||||
* Medium <br>
|
||||
* High <br>
|
||||
* <br>
|
||||
* this is a quality scale for the detail drop-off
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
public enum HorizontalScale
|
||||
{
|
||||
/** Lods are 2D with heightMap */
|
||||
LOW(64),
|
||||
|
||||
/** Lods expand in three dimension */
|
||||
MEDIUM(128),
|
||||
|
||||
/** Lods expand in three dimension */
|
||||
HIGH(256);
|
||||
|
||||
public final int distanceUnit;
|
||||
|
||||
HorizontalScale(int distanceUnit)
|
||||
{
|
||||
this.distanceUnit = distanceUnit;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.AbstractLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.CubicLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.DynamicLodTemplate;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.lodTemplates.TriangularLodTemplate;
|
||||
|
||||
/**
|
||||
* Cubic, Triangular, Dynamic
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-10-2021
|
||||
*/
|
||||
public enum LodTemplate
|
||||
{
|
||||
/**
|
||||
* LODs are rendered as
|
||||
* rectangular prisms.
|
||||
*/
|
||||
CUBIC(new CubicLodTemplate()),
|
||||
|
||||
/**
|
||||
* LODs smoothly transition between
|
||||
* each other.
|
||||
*/
|
||||
TRIANGULAR(new TriangularLodTemplate()),
|
||||
|
||||
/**
|
||||
* LODs smoothly transition between
|
||||
* each other, unless a neighboring LOD
|
||||
* is at a significantly different height.
|
||||
*/
|
||||
DYNAMIC(new DynamicLodTemplate());
|
||||
|
||||
|
||||
public final AbstractLodTemplate template;
|
||||
|
||||
LodTemplate(AbstractLodTemplate newTemplate)
|
||||
{
|
||||
template = newTemplate;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.seibel.lod.core.enums.config;
|
||||
|
||||
/**
|
||||
* NONE, GAME_SHADING
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-25-2020
|
||||
*/
|
||||
public enum ShadingMode
|
||||
{
|
||||
/**
|
||||
* LODs will have darker sides and bottoms to simulate
|
||||
* Minecraft's fast lighting.
|
||||
*/
|
||||
GAME_SHADING,
|
||||
|
||||
/**
|
||||
* LODs will use ambient occlusion to mimic Minecarft's
|
||||
* Fancy lighting.
|
||||
*/
|
||||
AMBIENT_OCCLUSION
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* None, Dynamic, Always
|
||||
*
|
||||
* <p>
|
||||
* This represents how far the LODs should overlap with
|
||||
* the vanilla Minecraft terrain.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-11-2021
|
||||
*/
|
||||
public enum VanillaOverdraw
|
||||
{
|
||||
/** Never draw LODs where a minecraft chunk could be. */
|
||||
NEVER,
|
||||
|
||||
/** Draw LODs over the farther minecraft chunks. */
|
||||
DYNAMIC,
|
||||
|
||||
/** Draw LODs over all minecraft chunks. */
|
||||
ALWAYS,
|
||||
|
||||
/** Draw LODs over border chunks. */
|
||||
BORDER,
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.config;
|
||||
|
||||
/**
|
||||
* heightmap <br>
|
||||
* multi_lod <br>
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 10-07-2021
|
||||
*/
|
||||
public enum VerticalQuality
|
||||
{
|
||||
LOW(
|
||||
new int[] { 2,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1 }
|
||||
),
|
||||
|
||||
MEDIUM(
|
||||
new int[] { 4,
|
||||
4,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1 }
|
||||
),
|
||||
|
||||
HIGH(
|
||||
new int[] {
|
||||
8,
|
||||
8,
|
||||
4,
|
||||
4,
|
||||
2,
|
||||
2,
|
||||
2,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1 }
|
||||
);
|
||||
|
||||
public final int[] maxVerticalData;
|
||||
|
||||
VerticalQuality(int[] maxVerticalData)
|
||||
{
|
||||
this.maxVerticalData = maxVerticalData;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* off, detail, detail wireframe
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-28-2021
|
||||
*/
|
||||
public enum DebugMode
|
||||
{
|
||||
/** LODs are rendered normally */
|
||||
OFF,
|
||||
|
||||
/** LOD colors are based on their detail */
|
||||
SHOW_DETAIL,
|
||||
|
||||
/** LOD colors are based on their detail, and draws in wireframe. */
|
||||
SHOW_DETAIL_WIREFRAME;
|
||||
|
||||
/** used when cycling through the different modes */
|
||||
private DebugMode next;
|
||||
|
||||
static
|
||||
{
|
||||
OFF.next = SHOW_DETAIL;
|
||||
SHOW_DETAIL.next = SHOW_DETAIL_WIREFRAME;
|
||||
SHOW_DETAIL_WIREFRAME.next = OFF;
|
||||
}
|
||||
|
||||
/** returns the next debug mode */
|
||||
public DebugMode getNext()
|
||||
{
|
||||
return this.next;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* NEAR, FAR, or NEAR_AND_FAR.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
*/
|
||||
public enum FogDistance
|
||||
{
|
||||
/** good for fast or fancy fog qualities. */
|
||||
NEAR,
|
||||
|
||||
/** good for fast or fancy fog qualities. */
|
||||
FAR,
|
||||
|
||||
/** only looks good if the fog quality is set to Fancy. */
|
||||
NEAR_AND_FAR
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* USE_OPTIFINE_FOG_SETTING, <br>
|
||||
* NEVER_DRAW_FOG, <br>
|
||||
* ALWAYS_DRAW_FOG_FAST, <br>
|
||||
* ALWAYS_DRAW_FOG_FANCY <br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-3-2021
|
||||
*/
|
||||
public enum FogDrawOverride
|
||||
{
|
||||
/**
|
||||
* Use whatever Fog setting optifine is using.
|
||||
* If optifine isn't installed this defaults to ALWAYS_DRAW_FOG.
|
||||
*/
|
||||
OPTIFINE_SETTING,
|
||||
|
||||
/** Never draw fog on the LODs */
|
||||
NO_FOG,
|
||||
|
||||
/** Always draw fast fog on the LODs */
|
||||
FAST,
|
||||
|
||||
/** Always draw fancy fog on the LODs */
|
||||
FANCY
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* fast, fancy, or off
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-14-2021
|
||||
*/
|
||||
public enum FogQuality
|
||||
{
|
||||
FAST,
|
||||
FANCY,
|
||||
OFF
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.enums.rendering;
|
||||
|
||||
/**
|
||||
* Minecraft, Lod_Builder, None
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-1-2021
|
||||
*/
|
||||
public enum GLProxyContext
|
||||
{
|
||||
/** Minecraft's render thread */
|
||||
MINECRAFT,
|
||||
|
||||
/** The context we send buffers to the GPU on */
|
||||
LOD_BUILDER,
|
||||
|
||||
/** A context that can be used for miscellaneous tasks, owned by the GLProxy */
|
||||
PROXY_WORKER,
|
||||
|
||||
/** used to un-bind threads */
|
||||
NONE,
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.handlers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Cola
|
||||
* @author Leonardo Amato
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class ChunkFileLoader
|
||||
{
|
||||
// TODO
|
||||
// public static IChunk getChunkFromFile(ChunkPos pos)
|
||||
// {
|
||||
// LevelWrapper clientLevel = MinecraftWrapper.INSTANCE.getWrappedClientLevel();
|
||||
// if (clientLevel == null)
|
||||
// return null;
|
||||
// WorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(clientLevel.getDimensionType());
|
||||
// try
|
||||
// {
|
||||
// File file = new File(serverWorld.getSaveFolder().getParent() + File.separatorChar + "region", "r." + (pos.x >> 5) + "." + (pos.z >> 5) + ".mca");
|
||||
// if(!file.exists())
|
||||
// return null;
|
||||
// IChunk loadedChunk = ChunkSerializer.read(
|
||||
// serverWorld,
|
||||
// serverWorld.getStructureManager(),
|
||||
// serverWorld.getPoiManager(),
|
||||
// pos,
|
||||
// serverWorld.getChunkSource().chunkMap.read(pos)
|
||||
// );
|
||||
// boolean emptyChunk = true;
|
||||
// for(int i = 0; i < 16; i++){
|
||||
// for(int j = 0; j < 16; j++){
|
||||
// emptyChunk &= loadedChunk.isYSpaceEmpty(i,j);
|
||||
// }
|
||||
// }
|
||||
// if(emptyChunk)
|
||||
// return null;
|
||||
// else
|
||||
// return loadedChunk;
|
||||
// }
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.handlers;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
* where they are private or potentially absent.
|
||||
* Specifically the fog setting used by Optifine or the
|
||||
* presence/absence of other mods.
|
||||
* <p>
|
||||
* This interface doesn't necessarily have to exist, but
|
||||
* it makes using the singleton handler more uniform (always
|
||||
* passing in interfaces), and it may be needed in the future if
|
||||
* we find that reflection handlers need to be different for
|
||||
* different MC versions.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public interface IReflectionHandler
|
||||
{
|
||||
/** @returns the type of fog optifine is currently set to render. */
|
||||
public FogQuality getFogQuality();
|
||||
|
||||
/** @returns if Vivecraft is present. Attempts to find the "VRRenderer" class. */
|
||||
public boolean vivecraftPresent();
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane);
|
||||
}
|
||||
@@ -1,423 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.handlers;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
||||
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.LodRegion;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.lod.VerticalLevelContainer;
|
||||
import com.seibel.lod.core.util.LodThreadFactory;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
|
||||
/**
|
||||
* This object handles creating LodRegions
|
||||
* from files and saving LodRegion objects
|
||||
* to file.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Cola
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
public class LodDimensionFileHandler
|
||||
{
|
||||
/** This is the dimension that owns this file handler */
|
||||
private LodDimension lodDimension;
|
||||
|
||||
private final File dimensionDataSaveFolder;
|
||||
|
||||
/** lod */
|
||||
private static final String FILE_NAME_PREFIX = "lod";
|
||||
/** .txt */
|
||||
private static final String FILE_EXTENSION = ".xz";
|
||||
/** detail- */
|
||||
private static final String DETAIL_FOLDER_NAME_PREFIX = "detail-";
|
||||
|
||||
/**
|
||||
* .tmp <br>
|
||||
* Added to the end of the file path when saving to prevent
|
||||
* nulling a currently existing file. <br>
|
||||
* After the file finishes saving it will end with
|
||||
* FILE_EXTENSION.
|
||||
*/
|
||||
private static final String TMP_FILE_EXTENSION = ".tmp";
|
||||
|
||||
/**
|
||||
* This is the file version currently accepted by this
|
||||
* 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 = 6;
|
||||
|
||||
/**
|
||||
* Allow saving asynchronously, but never try to save multiple regions
|
||||
* at a time
|
||||
*/
|
||||
private final ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName()));
|
||||
|
||||
|
||||
|
||||
|
||||
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLodDimension)
|
||||
{
|
||||
if (newSaveFolder == null)
|
||||
throw new IllegalArgumentException("LodDimensionFileHandler requires a valid File location to read and write to.");
|
||||
|
||||
dimensionDataSaveFolder = newSaveFolder;
|
||||
lodDimension = newLodDimension;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// read from file //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* Returns the LodRegion at the given coordinates.
|
||||
* Returns an empty region if the file doesn't exist.
|
||||
*/
|
||||
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, verticalQuality);
|
||||
|
||||
for (byte tempDetailLevel = LodUtil.REGION_DETAIL_LEVEL; tempDetailLevel >= detailLevel; tempDetailLevel--)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, tempDetailLevel, verticalQuality);
|
||||
|
||||
try
|
||||
{
|
||||
// if the fileName was null that means the folder is inaccessible
|
||||
// for some reason
|
||||
if (fileName == null)
|
||||
throw new IllegalArgumentException("Unable to read region [" + regionX + ", " + regionZ + "] file, no fileName.");
|
||||
|
||||
File file = new File(fileName);
|
||||
if (!file.exists())
|
||||
{
|
||||
//there is no file for current gen mode
|
||||
//search others above current from the most to the least detailed
|
||||
DistanceGenerationMode tempGenMode = DistanceGenerationMode.FULL;
|
||||
while (tempGenMode != generationMode)
|
||||
{
|
||||
fileName = getFileNameAndPathForRegion(regionX, regionZ, tempGenMode, tempDetailLevel, verticalQuality);
|
||||
if (fileName != null)
|
||||
{
|
||||
file = new File(fileName);
|
||||
if (file.exists())
|
||||
break;
|
||||
}
|
||||
//decrease gen mode
|
||||
if (tempGenMode == DistanceGenerationMode.FULL)
|
||||
tempGenMode = DistanceGenerationMode.FEATURES;
|
||||
else if (tempGenMode == DistanceGenerationMode.FEATURES)
|
||||
tempGenMode = DistanceGenerationMode.SURFACE;
|
||||
else if (tempGenMode == DistanceGenerationMode.SURFACE)
|
||||
tempGenMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
|
||||
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT)
|
||||
tempGenMode = DistanceGenerationMode.BIOME_ONLY;
|
||||
else if (tempGenMode == DistanceGenerationMode.BIOME_ONLY)
|
||||
tempGenMode = DistanceGenerationMode.NONE;
|
||||
}
|
||||
if (!file.exists())
|
||||
//there wasn't a file, don't return anything
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// don't try parsing empty files
|
||||
long dataSize = file.length();
|
||||
dataSize -= 1;
|
||||
if (dataSize > 0)
|
||||
{
|
||||
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(file)))
|
||||
{
|
||||
int fileVersion;
|
||||
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();
|
||||
file.delete();
|
||||
ClientApi.LOGGER.info("Outdated LOD region file for region: (" + regionX + "," + regionZ + ")"
|
||||
+ " version found: " + 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 accidentally delete anything the user may want.
|
||||
inputStream.close();
|
||||
ClientApi.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,
|
||||
// read the file
|
||||
byte[] data = ThreadMapUtil.getSaveContainer(tempDetailLevel);
|
||||
inputStream.read(data);
|
||||
inputStream.close();
|
||||
|
||||
|
||||
// add the data to our region
|
||||
region.addLevelContainer(new VerticalLevelContainer(data));
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||
ioEx.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// the buffered reader encountered a
|
||||
// problem reading the file
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}// for each detail level
|
||||
|
||||
if (region.getMinDetailLevel() >= detailLevel)
|
||||
region.growTree(detailLevel);
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
|
||||
//==============//
|
||||
// Save to File //
|
||||
//==============//
|
||||
|
||||
/** Save all dirty regions in this LodDimension to file */
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileWritingThreadPool.execute(saveDirtyRegionsThread);
|
||||
}
|
||||
|
||||
private final Thread saveDirtyRegionsThread = new Thread(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < lodDimension.getWidth(); i++)
|
||||
{
|
||||
for (int j = 0; j < lodDimension.getWidth(); j++)
|
||||
{
|
||||
if (lodDimension.GetIsRegionDirty(i, j) && lodDimension.getRegionByArrayIndex(i, j) != null)
|
||||
{
|
||||
saveRegionToFile(lodDimension.getRegionByArrayIndex(i, j));
|
||||
lodDimension.SetIsRegionDirty(i, j, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a specific region to disk.<br>
|
||||
* Note: <br>
|
||||
* 1. If a file already exists for a newer version
|
||||
* the file won't be written.<br>
|
||||
* 2. This will save to the LodDimension that this
|
||||
* handler is associated with.
|
||||
*/
|
||||
private void saveRegionToFile(LodRegion region)
|
||||
{
|
||||
for (byte detailLevel = region.getMinDetailLevel(); detailLevel <= LodUtil.REGION_DETAIL_LEVEL; detailLevel++)
|
||||
{
|
||||
String fileName = getFileNameAndPathForRegion(region.regionPosX, region.regionPosZ, region.getGenerationMode(), detailLevel, region.getVerticalQuality());
|
||||
|
||||
// if the fileName was null that means the folder is inaccessible
|
||||
// for some reason
|
||||
if (fileName == null)
|
||||
{
|
||||
ClientApi.LOGGER.warn("Unable to save region [" + region.regionPosX + ", " + region.regionPosZ + "] to file, file is inaccessible.");
|
||||
return;
|
||||
}
|
||||
File oldFile = new File(fileName);
|
||||
//ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
|
||||
byte[] temp = region.getLevel(detailLevel).toDataString();
|
||||
|
||||
try
|
||||
{
|
||||
// make sure the file and folder exists
|
||||
if (!oldFile.exists())
|
||||
{
|
||||
// the file doesn't exist,
|
||||
// create it and the folder if need be
|
||||
if (!oldFile.getParentFile().exists())
|
||||
oldFile.getParentFile().mkdirs();
|
||||
oldFile.createNewFile();
|
||||
}
|
||||
else
|
||||
{
|
||||
// the file exists, make sure it
|
||||
// is the correct version.
|
||||
// (to make sure we don't overwrite a newer
|
||||
// version file if it exists)
|
||||
int fileVersion = LOD_SAVE_FILE_VERSION;
|
||||
int isFull = 0;
|
||||
try (XZCompressorInputStream inputStream = new XZCompressorInputStream(new FileInputStream(oldFile)))
|
||||
{
|
||||
fileVersion = inputStream.read();
|
||||
inputStream.skip(1);
|
||||
isFull = inputStream.read() & 0b10000000;
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
// check if this file can be written to by the file handler
|
||||
if (fileVersion > LOD_SAVE_FILE_VERSION)
|
||||
{
|
||||
// the file we are reading is a newer version,
|
||||
// don't write anything, we don't want to accidentally
|
||||
// delete anything the user may want.
|
||||
return;
|
||||
}
|
||||
if ((temp[1] & 0b10000000) != 0b10000000 && isFull == 0b10000000)
|
||||
{
|
||||
// existing file is complete while new one is only partially generate
|
||||
// this can happen is for some reason loading failed
|
||||
// this doesn't fix the bug, but at least protects old data
|
||||
ClientApi.LOGGER.error("LOD file write error. Attempted to overwrite complete region with incomplete one [" + fileName + "]");
|
||||
return;
|
||||
}
|
||||
// if we got this far then we are good
|
||||
// to overwrite the old file
|
||||
}
|
||||
// the old file is good, now create a new temporary save file
|
||||
File newFile = new File(fileName + TMP_FILE_EXTENSION);
|
||||
try (XZCompressorOutputStream outputStream = new XZCompressorOutputStream(new FileOutputStream(newFile), 3))
|
||||
{
|
||||
// add the version of this file
|
||||
outputStream.write(LOD_SAVE_FILE_VERSION);
|
||||
|
||||
// add each LodChunk to the file
|
||||
outputStream.write(temp);
|
||||
outputStream.close();
|
||||
|
||||
// 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)
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file write error. Unable to write to [" + fileName + "] error [" + e.getMessage() + "]: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
public byte[] getHashFromFile(byte detailLevel, RegionPos regionPos, DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||
{
|
||||
int regionX = regionPos.x;
|
||||
int regionZ = regionPos.z;
|
||||
String fileName = getFileNameAndPathForRegion(regionX, regionZ, generationMode, detailLevel, verticalQuality);
|
||||
try (InputStream is = Files.newInputStream(Paths.get(fileName))) {
|
||||
return org.apache.commons.codec.digest.DigestUtils.md5(is);
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
ClientApi.LOGGER.error("LOD file read error. Unable to read to [" + fileName + "] error [" + ioEx.getMessage() + "]: ");
|
||||
ioEx.printStackTrace();
|
||||
}
|
||||
return new byte[0];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the name of the file that should contain the
|
||||
* region at the given x and z. <br>
|
||||
* Returns null if this object isn't available to read and write. <br><br>
|
||||
* <p>
|
||||
* example: "lod.0.0.txt" <br>
|
||||
* <p>
|
||||
* Returns null if there is an IO or security Exception.
|
||||
*/
|
||||
private String getFileNameAndPathForRegion(int regionX, int regionZ, DistanceGenerationMode generationMode, byte detailLevel, VerticalQuality verticalQuality)
|
||||
{
|
||||
try
|
||||
{
|
||||
// saveFolder is something like
|
||||
// ".\Super Flat\DIM-1\data\"
|
||||
// or
|
||||
// ".\Super Flat\data\"
|
||||
return dimensionDataSaveFolder.getCanonicalPath() + File.separatorChar +
|
||||
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)
|
||||
{
|
||||
ClientApi.LOGGER.warn("Unable to get the filename for the region [" + regionX + ", " + regionZ + "], error: [" + e.getMessage() + "], stacktrace: ");
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.handlers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
* where they are private or potentially absent.
|
||||
* Specifically the fog setting in Optifine or the
|
||||
* presence/absence of other mods.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public class ReflectionHandler implements IReflectionHandler
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + ReflectionHandler.class.getSimpleName());
|
||||
|
||||
private static ReflectionHandler instance;
|
||||
|
||||
private Field ofFogField = null;
|
||||
private final Object mcOptionsObject;
|
||||
|
||||
|
||||
|
||||
private ReflectionHandler(Field[] optionFields, Object newMcOptionsObject)
|
||||
{
|
||||
mcOptionsObject = newMcOptionsObject;
|
||||
|
||||
setupFogField(optionFields);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param optionFields the fields that should contain "ofFogType"
|
||||
* @param newMcOptionsObject the object instance that contains "ofFogType"
|
||||
* @return the ReflectionHandler just created
|
||||
* @throws IllegalStateException if a ReflectionHandler already exists
|
||||
*/
|
||||
public static ReflectionHandler createSingleton(Field[] optionFields, Object newMcOptionsObject) throws IllegalStateException
|
||||
{
|
||||
if (instance != null)
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
instance = new ReflectionHandler(optionFields, newMcOptionsObject);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** finds the Optifine fog type field */
|
||||
private void setupFogField(Field[] optionFields)
|
||||
{
|
||||
// try and find the ofFogType variable in gameSettings
|
||||
for (Field field : optionFields)
|
||||
{
|
||||
if (field.getName().equals("ofFogType"))
|
||||
{
|
||||
ofFogField = field;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we didn't find the field,
|
||||
// either optifine isn't installed, or
|
||||
// optifine changed the name of the variable
|
||||
LOGGER.info(ReflectionHandler.class.getSimpleName() + ": unable to find the Optifine fog field. If Optifine isn't installed this can be ignored.");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get what type of fog optifine is currently set to render.
|
||||
* @return the fog quality
|
||||
*/
|
||||
@Override
|
||||
public FogQuality getFogQuality()
|
||||
{
|
||||
if (ofFogField == null)
|
||||
{
|
||||
// either optifine isn't installed,
|
||||
// the variable name was changed, or
|
||||
// the setup method wasn't called yet.
|
||||
return FogQuality.FANCY;
|
||||
}
|
||||
|
||||
int returnNum = 0;
|
||||
|
||||
try
|
||||
{
|
||||
returnNum = (int) ofFogField.get(mcOptionsObject);
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
switch (returnNum)
|
||||
{
|
||||
default:
|
||||
case 0:
|
||||
// optifine's "default" option,
|
||||
// it should never be called in this case
|
||||
|
||||
// normal options
|
||||
case 1:
|
||||
return FogQuality.FAST;
|
||||
case 2:
|
||||
return FogQuality.FANCY;
|
||||
case 3:
|
||||
return FogQuality.OFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Detect if Vivecraft is present. Attempts to find the "VRRenderer" class. */
|
||||
@Override
|
||||
public boolean vivecraftPresent()
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName("org.vivecraft.provider.VRRenderer");
|
||||
return true;
|
||||
}
|
||||
catch (ClassNotFoundException ignored)
|
||||
{
|
||||
LOGGER.info(ReflectionHandler.class.getSimpleName() + ": Vivecraft not detected.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
@Override
|
||||
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane)
|
||||
{
|
||||
// find the matrix values.
|
||||
float nearMatrixValue = -((newFarClipPlane + newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
float farMatrixValue = -((2 * newFarClipPlane * newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
|
||||
try
|
||||
{
|
||||
// TODO this was originally created before we had the Mat4f object,
|
||||
// so this doesn't need to be done with reflection anymore.
|
||||
// And should be moved to RenderUtil
|
||||
|
||||
// get the fields of the projectionMatrix
|
||||
Field[] fields = projectionMatrix.getClass().getDeclaredFields();
|
||||
// bypass the security protections on the fields that encode near and far plane values.
|
||||
fields[10].setAccessible(true);
|
||||
fields[11].setAccessible(true);
|
||||
// Change the values of the near and far plane.
|
||||
fields[10].set(projectionMatrix, nearMatrixValue);
|
||||
fields[11].set(projectionMatrix, farMatrixValue);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return projectionMatrix;
|
||||
}
|
||||
}
|
||||
@@ -1,627 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.objects.math.Vec3i;
|
||||
import com.seibel.lod.core.util.ColorUtil;
|
||||
import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
* Similar to Minecraft's AxisAlignedBoundingBox.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 10-2-2021
|
||||
*/
|
||||
public class Box
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
public static final int ADJACENT_HEIGHT_INDEX = 0;
|
||||
public static final int ADJACENT_DEPTH_INDEX = 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;
|
||||
|
||||
/** The six cardinal directions */
|
||||
public static final LodDirection[] DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.UP,
|
||||
LodDirection.DOWN,
|
||||
LodDirection.WEST,
|
||||
LodDirection.EAST,
|
||||
LodDirection.NORTH,
|
||||
LodDirection.SOUTH };
|
||||
|
||||
/** North, South, East, West */
|
||||
public static final LodDirection[] ADJ_DIRECTIONS = new LodDirection[] {
|
||||
LodDirection.EAST,
|
||||
LodDirection.WEST,
|
||||
LodDirection.SOUTH,
|
||||
LodDirection.NORTH };
|
||||
|
||||
/** All the faces and vertices of a cube. This is used to extract the vertex from the column */
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, int[][]> DIRECTION_VERTEX_MAP = new HashMap<LodDirection, int[][]>()
|
||||
{{
|
||||
put(LodDirection.UP, new int[][] {
|
||||
{ 0, 1, 0 }, // 0
|
||||
{ 0, 1, 1 }, // 1
|
||||
{ 1, 1, 1 }, // 2
|
||||
|
||||
{ 0, 1, 0 }, // 0
|
||||
{ 1, 1, 1 }, // 2
|
||||
{ 1, 1, 0 } // 3
|
||||
});
|
||||
put(LodDirection.DOWN, new int[][] {
|
||||
{ 1, 0, 0 }, // 0
|
||||
{ 1, 0, 1 }, // 1
|
||||
{ 0, 0, 1 }, // 2
|
||||
|
||||
{ 1, 0, 0 }, // 0
|
||||
{ 0, 0, 1 }, // 2
|
||||
{ 0, 0, 0 } // 3
|
||||
});
|
||||
put(LodDirection.EAST, new int[][] {
|
||||
{ 1, 1, 0 }, // 0
|
||||
{ 1, 1, 1 }, // 1
|
||||
{ 1, 0, 1 }, // 2
|
||||
|
||||
{ 1, 1, 0 }, // 0
|
||||
{ 1, 0, 1 }, // 2
|
||||
{ 1, 0, 0 } }); // 3
|
||||
put(LodDirection.WEST, new int[][] {
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 0, 1 }, // 1
|
||||
{ 0, 1, 1 }, // 2
|
||||
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 1, 1 }, // 2
|
||||
{ 0, 1, 0 } // 3
|
||||
});
|
||||
put(LodDirection.SOUTH, new int[][] {
|
||||
{ 1, 0, 1 }, // 0
|
||||
{ 1, 1, 1 }, // 1
|
||||
{ 0, 1, 1 }, // 2
|
||||
|
||||
{ 1, 0, 1 }, // 0
|
||||
{ 0, 1, 1 }, // 2
|
||||
{ 0, 0, 1 } // 3
|
||||
});
|
||||
put(LodDirection.NORTH, new int[][] {
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 0, 1, 0 }, // 1
|
||||
{ 1, 1, 0 }, // 2
|
||||
|
||||
{ 0, 0, 0 }, // 0
|
||||
{ 1, 1, 0 }, // 2
|
||||
{ 1, 0, 0 } // 3
|
||||
});
|
||||
}};
|
||||
|
||||
|
||||
/**
|
||||
* This indicates which position is invariable in the DIRECTION_VERTEX_MAP.
|
||||
* Is used to extract the vertex
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, int[]> FACE_DIRECTION = new HashMap<LodDirection, int[]>()
|
||||
{{
|
||||
put(LodDirection.UP, new int[] { Y, MAX });
|
||||
put(LodDirection.DOWN, new int[] { Y, MIN });
|
||||
put(LodDirection.EAST, new int[] { X, MAX });
|
||||
put(LodDirection.WEST, new int[] { X, MIN });
|
||||
put(LodDirection.SOUTH, new int[] { Z, MAX });
|
||||
put(LodDirection.NORTH, new int[] { Z, MIN });
|
||||
}};
|
||||
|
||||
|
||||
/**
|
||||
* This is a map from Direction to the relative normal vector
|
||||
* we are using this since I'm not sure if the getNormal create new object at every call
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, Vec3i> DIRECTION_NORMAL_MAP = new HashMap<LodDirection, Vec3i>()
|
||||
{{
|
||||
put(LodDirection.UP, LodDirection.UP.getNormal());
|
||||
put(LodDirection.DOWN, LodDirection.DOWN.getNormal());
|
||||
put(LodDirection.EAST, LodDirection.EAST.getNormal());
|
||||
put(LodDirection.WEST, LodDirection.WEST.getNormal());
|
||||
put(LodDirection.SOUTH, LodDirection.SOUTH.getNormal());
|
||||
put(LodDirection.NORTH, LodDirection.NORTH.getNormal());
|
||||
}};
|
||||
|
||||
/** We use this index for all array that are going to */
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, Integer> DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
|
||||
{{
|
||||
put(LodDirection.UP, 0);
|
||||
put(LodDirection.DOWN, 1);
|
||||
put(LodDirection.EAST, 2);
|
||||
put(LodDirection.WEST, 3);
|
||||
put(LodDirection.SOUTH, 4);
|
||||
put(LodDirection.NORTH, 5);
|
||||
}};
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static final Map<LodDirection, Integer> ADJ_DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
|
||||
{{
|
||||
put(LodDirection.EAST, 0);
|
||||
put(LodDirection.WEST, 1);
|
||||
put(LodDirection.SOUTH, 2);
|
||||
put(LodDirection.NORTH, 3);
|
||||
}};
|
||||
/** holds the box's x, y, z offset */
|
||||
public final int[] boxOffset;
|
||||
/** holds the box's x, y, z width */
|
||||
public final int[] boxWidth;
|
||||
|
||||
/** Holds each direction's color */
|
||||
public final int[] colorMap;
|
||||
/** The original color (before shading) of this box */
|
||||
public int color;
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public final Map<LodDirection, int[]> adjHeight;
|
||||
public final Map<LodDirection, int[]> adjDepth;
|
||||
public final Map<LodDirection, byte[]> skyLights;
|
||||
public byte blockLight;
|
||||
|
||||
/** Holds if the given direction should be culled or not */
|
||||
public final boolean[] culling;
|
||||
|
||||
|
||||
/** creates an empty box */
|
||||
@SuppressWarnings("serial")
|
||||
public Box()
|
||||
{
|
||||
boxOffset = new int[3];
|
||||
boxWidth = new int[3];
|
||||
|
||||
colorMap = new int[6];
|
||||
skyLights = new HashMap<LodDirection, byte[]>()
|
||||
{{
|
||||
put(LodDirection.UP, new byte[1]);
|
||||
put(LodDirection.DOWN, new byte[1]);
|
||||
put(LodDirection.EAST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.WEST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.SOUTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.NORTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
}};
|
||||
adjHeight = new HashMap<LodDirection, int[]>()
|
||||
{{
|
||||
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
}};
|
||||
adjDepth = new HashMap<LodDirection, int[]>()
|
||||
{{
|
||||
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
|
||||
}};
|
||||
|
||||
culling = new boolean[6];
|
||||
}
|
||||
|
||||
/** Set the light of the columns */
|
||||
public void setLights(int skyLight, int blockLight)
|
||||
{
|
||||
this.blockLight = (byte) blockLight;
|
||||
skyLights.get(LodDirection.UP)[0] = (byte) skyLight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color of the columns
|
||||
* @param color color to add
|
||||
* @param adjShadeDisabled this array indicates which face does not need shading
|
||||
*/
|
||||
public void setColor(int color, boolean[] adjShadeDisabled)
|
||||
{
|
||||
this.color = color;
|
||||
for (LodDirection lodDirection : DIRECTIONS)
|
||||
{
|
||||
if (!adjShadeDisabled[DIRECTION_INDEX.get(lodDirection)])
|
||||
colorMap[DIRECTION_INDEX.get(lodDirection)] = ColorUtil.applyShade(color, MC.getShade(lodDirection));
|
||||
else
|
||||
colorMap[DIRECTION_INDEX.get(lodDirection)] = color;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection of the face of which we want to get the color
|
||||
* @return color of the face
|
||||
*/
|
||||
public int getColor(LodDirection lodDirection)
|
||||
{
|
||||
if (CONFIG.client().advanced().debugging().getDebugMode() != DebugMode.SHOW_DETAIL)
|
||||
return colorMap[DIRECTION_INDEX.get(lodDirection)];
|
||||
else
|
||||
return ColorUtil.applyShade(color, MC.getShade(lodDirection));
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public byte getSkyLight(LodDirection lodDirection, int verticalIndex)
|
||||
{
|
||||
if(lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
|
||||
return skyLights.get(lodDirection)[0];
|
||||
else
|
||||
return skyLights.get(lodDirection)[verticalIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public int getBlockLight()
|
||||
{
|
||||
return blockLight;
|
||||
}
|
||||
/** clears this box, resetting everything to default values */
|
||||
public void reset()
|
||||
{
|
||||
Arrays.fill(boxWidth, 0);
|
||||
Arrays.fill(boxOffset, 0);
|
||||
Arrays.fill(colorMap, 0);
|
||||
blockLight = 0;
|
||||
for (LodDirection lodDirection : ADJ_DIRECTIONS)
|
||||
{
|
||||
for (int i = 0; i < adjHeight.get(lodDirection).length; i++)
|
||||
{
|
||||
adjHeight.get(lodDirection)[i] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[i] = VOID_FACE;
|
||||
skyLights.get(lodDirection)[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** determine which faces should be culled */
|
||||
public void setUpCulling(int cullingDistance, AbstractBlockPosWrapper playerPos)
|
||||
{
|
||||
for (LodDirection lodDirection : DIRECTIONS)
|
||||
{
|
||||
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.WEST || lodDirection == LodDirection.NORTH)
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) > getFacePos(lodDirection) + cullingDistance;
|
||||
|
||||
else if (lodDirection == LodDirection.UP || lodDirection == LodDirection.EAST || lodDirection == LodDirection.SOUTH)
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = playerPos.get(lodDirection.getAxis()) < getFacePos(lodDirection) - cullingDistance;
|
||||
|
||||
culling[DIRECTION_INDEX.get(lodDirection)] = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection direction that we want to check if it's culled
|
||||
* @return true if and only if the face of the direction is culled
|
||||
*/
|
||||
public boolean isCulled(LodDirection lodDirection)
|
||||
{
|
||||
return culling[DIRECTION_INDEX.get(lodDirection)];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method create all the shared face culling based on the adjacent data
|
||||
* @param adjData data adjacent to the column we are going to render
|
||||
*/
|
||||
public void setAdjData(Map<LodDirection, long[]> adjData)
|
||||
{
|
||||
int height;
|
||||
int depth;
|
||||
int minY = getMinY();
|
||||
int maxY = getMaxY();
|
||||
long singleAdjDataPoint;
|
||||
|
||||
/* TODO implement attached vertical face culling
|
||||
//Up direction case
|
||||
if(DataPointUtil.doesItExist(adjData.get(Direction.UP)))
|
||||
{
|
||||
height = DataPointUtil.getHeight(singleAdjDataPoint);
|
||||
depth = DataPointUtil.getDepth(singleAdjDataPoint);
|
||||
}*/
|
||||
//Down direction case
|
||||
singleAdjDataPoint = adjData.get(LodDirection.DOWN)[0];
|
||||
if(DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
else
|
||||
skyLights.get(LodDirection.DOWN)[0] = skyLights.get(LodDirection.UP)[0];
|
||||
//other sided
|
||||
//TODO clean some similar cases
|
||||
for (LodDirection lodDirection : ADJ_DIRECTIONS)
|
||||
{
|
||||
if (isCulled(lodDirection))
|
||||
continue;
|
||||
|
||||
long[] dataPoint = adjData.get(lodDirection);
|
||||
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
|
||||
{
|
||||
adjHeight.get(lodDirection)[0] = maxY;
|
||||
adjDepth.get(lodDirection)[0] = minY;
|
||||
adjHeight.get(lodDirection)[1] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[1] = VOID_FACE;
|
||||
skyLights.get(lodDirection)[0] = 15; //in void set full sky light
|
||||
continue;
|
||||
}
|
||||
|
||||
int i;
|
||||
int faceToDraw = 0;
|
||||
boolean firstFace = true;
|
||||
boolean toFinish = false;
|
||||
int toFinishIndex = 0;
|
||||
boolean allAbove = true;
|
||||
for (i = 0; i < dataPoint.length; i++)
|
||||
{
|
||||
singleAdjDataPoint = dataPoint[i];
|
||||
|
||||
if (DataPointUtil.isVoid(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
|
||||
|
||||
if (firstFace)
|
||||
{
|
||||
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||
adjDepth.get(lodDirection)[0] = getMinY();
|
||||
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
adjDepth.get(lodDirection)[faceToDraw] = getMinY();
|
||||
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
}
|
||||
faceToDraw++;
|
||||
toFinish = false;
|
||||
|
||||
// break since all the other data will be lower
|
||||
break;
|
||||
}
|
||||
else if (depth <= minY)
|
||||
{
|
||||
if (height >= maxY)
|
||||
{
|
||||
// the adj data is inside the current data
|
||||
// don't draw the face
|
||||
adjHeight.get(lodDirection)[0] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[0] = VOID_FACE;
|
||||
}
|
||||
else // height < maxY
|
||||
{
|
||||
// the adj data intersects the lower part of the current data
|
||||
// if this is the only face, use the maxY and break,
|
||||
// if there was another face we finish the last one and break
|
||||
if (firstFace)
|
||||
{
|
||||
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||
adjDepth.get(lodDirection)[0] = height;
|
||||
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
adjDepth.get(lodDirection)[faceToDraw] = height;
|
||||
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
}
|
||||
toFinish = false;
|
||||
faceToDraw++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (height >= maxY)//depth > minY &&
|
||||
{
|
||||
// the adj data intersects the higher part of the current data
|
||||
// we start the creation of a new face
|
||||
adjHeight.get(lodDirection)[faceToDraw] = depth;
|
||||
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
firstFace = false;
|
||||
toFinish = true;
|
||||
toFinishIndex = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (depth > minY && height < maxY)
|
||||
|
||||
// the adj data is contained in the current data
|
||||
if (firstFace)
|
||||
{
|
||||
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||
}
|
||||
|
||||
adjDepth.get(lodDirection)[faceToDraw] = height;
|
||||
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
faceToDraw++;
|
||||
adjHeight.get(lodDirection)[faceToDraw] = depth;
|
||||
firstFace = false;
|
||||
toFinish = true;
|
||||
toFinishIndex = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allAbove)
|
||||
{
|
||||
adjHeight.get(lodDirection)[0] = getMaxY();
|
||||
adjDepth.get(lodDirection)[0] = getMinY();
|
||||
skyLights.get(lodDirection)[0] = skyLights.get(LodDirection.UP)[0];
|
||||
faceToDraw++;
|
||||
}
|
||||
else if (toFinish)
|
||||
{
|
||||
adjDepth.get(lodDirection)[faceToDraw] = minY;
|
||||
if(toFinishIndex < dataPoint.length)
|
||||
{
|
||||
singleAdjDataPoint = dataPoint[toFinishIndex];
|
||||
if (DataPointUtil.doesItExist(singleAdjDataPoint))
|
||||
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
|
||||
else
|
||||
skyLights.get(lodDirection)[faceToDraw] = skyLights.get(LodDirection.UP)[0];
|
||||
}
|
||||
faceToDraw++;
|
||||
}
|
||||
|
||||
adjHeight.get(lodDirection)[faceToDraw] = VOID_FACE;
|
||||
adjDepth.get(lodDirection)[faceToDraw] = VOID_FACE;
|
||||
}
|
||||
}
|
||||
|
||||
/** We use this method to set the various width of the column */
|
||||
public void setWidth(int xWidth, int yWidth, int zWidth)
|
||||
{
|
||||
boxWidth[X] = xWidth;
|
||||
boxWidth[Y] = yWidth;
|
||||
boxWidth[Z] = zWidth;
|
||||
}
|
||||
|
||||
/** We use this method to set the various offset of the column */
|
||||
public void setOffset(int xOffset, int yOffset, int zOffset)
|
||||
{
|
||||
boxOffset[X] = xOffset;
|
||||
boxOffset[Y] = yOffset;
|
||||
boxOffset[Z] = zOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method return the position of a face in the axis of the face
|
||||
* This is useful for the face culling
|
||||
* @param lodDirection that we want to check
|
||||
* @return position in the axis of the face
|
||||
*/
|
||||
public int getFacePos(LodDirection lodDirection)
|
||||
{
|
||||
return boxOffset[FACE_DIRECTION.get(lodDirection)[0]] + boxWidth[FACE_DIRECTION.get(lodDirection)[0]] * FACE_DIRECTION.get(lodDirection)[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the given direction should be rendered.
|
||||
*/
|
||||
public boolean shouldRenderFace(LodDirection lodDirection, int adjIndex)
|
||||
{
|
||||
if (lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
|
||||
return adjIndex == 0;
|
||||
return !(adjHeight.get(lodDirection)[adjIndex] == VOID_FACE && adjDepth.get(lodDirection)[adjIndex] == VOID_FACE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param lodDirection direction of the face we want to render
|
||||
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||
* @return position x of the relative vertex
|
||||
*/
|
||||
public int getX(LodDirection lodDirection, int vertexIndex)
|
||||
{
|
||||
return boxOffset[X] + boxWidth[X] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][X];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection direction of the face we want to render
|
||||
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||
* @return position y of the relative vertex
|
||||
*/
|
||||
public int getY(LodDirection lodDirection, int vertexIndex)
|
||||
{
|
||||
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection direction of the face we want to render
|
||||
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||
* @param adjIndex, index of the n-th culled face of this direction
|
||||
* @return position x of the relative vertex
|
||||
*/
|
||||
public int getY(LodDirection lodDirection, int vertexIndex, int adjIndex)
|
||||
{
|
||||
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.UP)
|
||||
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
|
||||
else
|
||||
{
|
||||
// this could probably be cleaned up more,
|
||||
// but it still works
|
||||
if (1 - DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y] == ADJACENT_HEIGHT_INDEX)
|
||||
return adjHeight.get(lodDirection)[adjIndex];
|
||||
else
|
||||
return adjDepth.get(lodDirection)[adjIndex];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lodDirection direction of the face we want to render
|
||||
* @param vertexIndex index of the vertex of the face (0-1-2-3)
|
||||
* @return position z of the relative vertex
|
||||
*/
|
||||
public int getZ(LodDirection lodDirection, int vertexIndex)
|
||||
{
|
||||
return boxOffset[Z] + boxWidth[Z] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Z];
|
||||
}
|
||||
|
||||
public int getMinX()
|
||||
{
|
||||
return boxOffset[X];
|
||||
}
|
||||
|
||||
public int getMaxX()
|
||||
{
|
||||
return boxOffset[X] + boxWidth[X];
|
||||
}
|
||||
|
||||
public int getMinY()
|
||||
{
|
||||
return boxOffset[Y];
|
||||
}
|
||||
|
||||
public int getMaxY()
|
||||
{
|
||||
return boxOffset[Y] + boxWidth[Y];
|
||||
}
|
||||
|
||||
public int getMinZ()
|
||||
{
|
||||
return boxOffset[Z];
|
||||
}
|
||||
|
||||
public int getMaxZ()
|
||||
{
|
||||
return boxOffset[Z] + boxWidth[Z];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects;
|
||||
|
||||
/**
|
||||
* Used when setting up configuration fields.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-14-2021
|
||||
* @param <T> The data type this object is storing
|
||||
*/
|
||||
public class MinDefaultMax<T>
|
||||
{
|
||||
public T minValue;
|
||||
public T defaultValue;
|
||||
public T maxValue;
|
||||
|
||||
public MinDefaultMax(T newMinValue, T newDefaultValue, T newMaxValue)
|
||||
{
|
||||
minValue = newMinValue;
|
||||
defaultValue = newDefaultValue;
|
||||
maxValue = newMaxValue;
|
||||
}
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects;
|
||||
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
|
||||
/**
|
||||
* Holds the levelPos that need to be generated.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-27-2021
|
||||
*/
|
||||
public class PosToGenerateContainer
|
||||
{
|
||||
private final int playerPosX;
|
||||
private final int playerPosZ;
|
||||
private final byte farMinDetail;
|
||||
private int nearSize;
|
||||
private int farSize;
|
||||
|
||||
// TODO what is the format of these two arrays? [detailLevel][4-children]?
|
||||
private final int[][] nearPosToGenerate;
|
||||
private final int[][] farPosToGenerate;
|
||||
|
||||
|
||||
|
||||
|
||||
public PosToGenerateContainer(byte farMinDetail, int maxDataToGenerate, int playerPosX, int playerPosZ)
|
||||
{
|
||||
this.playerPosX = playerPosX;
|
||||
this.playerPosZ = playerPosZ;
|
||||
this.farMinDetail = farMinDetail;
|
||||
nearSize = 0;
|
||||
farSize = 0;
|
||||
nearPosToGenerate = new int[maxDataToGenerate][4];
|
||||
farPosToGenerate = new int[maxDataToGenerate][4];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO what is going on in this method?
|
||||
public void addPosToGenerate(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
int distance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ);
|
||||
int index;
|
||||
|
||||
if (detailLevel >= farMinDetail)
|
||||
{
|
||||
// We are introducing a position in the far array
|
||||
|
||||
if (farSize < farPosToGenerate.length)
|
||||
farSize++;
|
||||
|
||||
index = farSize - 1;
|
||||
while (index > 0 && LevelPosUtil.compareDistance(distance, farPosToGenerate[index - 1][3]) <= 0)
|
||||
{
|
||||
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 != 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 nearSize + farSize;
|
||||
}
|
||||
|
||||
public int getNumberOfNearPos()
|
||||
{
|
||||
return nearSize;
|
||||
}
|
||||
|
||||
public int getNumberOfFarPos()
|
||||
{
|
||||
return farSize;
|
||||
}
|
||||
|
||||
// TODO what does getNth mean? could the name be more descriptive or is it just a index?
|
||||
public int getNthDetail(int n, boolean near)
|
||||
{
|
||||
if (near)
|
||||
return nearPosToGenerate[n][0];
|
||||
else
|
||||
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];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append('\n');
|
||||
builder.append('\n');
|
||||
builder.append('\n');
|
||||
builder.append("near pos to generate");
|
||||
builder.append('\n');
|
||||
for (int[] ints : nearPosToGenerate)
|
||||
{
|
||||
if (ints[0] == 0)
|
||||
break;
|
||||
builder.append(ints[0] - 1);
|
||||
builder.append(" ");
|
||||
builder.append(ints[1]);
|
||||
builder.append(" ");
|
||||
builder.append(ints[2]);
|
||||
builder.append(" ");
|
||||
builder.append(ints[3]);
|
||||
builder.append('\n');
|
||||
}
|
||||
builder.append('\n');
|
||||
|
||||
builder.append("far pos to generate");
|
||||
builder.append('\n');
|
||||
for (int[] ints : farPosToGenerate)
|
||||
{
|
||||
if (ints[0] == 0)
|
||||
break;
|
||||
builder.append(ints[0] - 1);
|
||||
builder.append(" ");
|
||||
builder.append(ints[1]);
|
||||
builder.append(" ");
|
||||
builder.append(ints[2]);
|
||||
builder.append(" ");
|
||||
builder.append(ints[3]);
|
||||
builder.append('\n');
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* Holds a levelPos that needs to be rendered.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-18-2021
|
||||
*/
|
||||
public class PosToRenderContainer
|
||||
{
|
||||
public byte minDetail;
|
||||
private int regionPosX;
|
||||
private int regionPosZ;
|
||||
private int numberOfPosToRender;
|
||||
private int[] posToRender;
|
||||
private byte[][] population;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
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
|
||||
ClientApi.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] = 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;
|
||||
if (this.minDetail == minDetail)
|
||||
{
|
||||
Arrays.fill(posToRender, 0);
|
||||
for (byte[] bytes : population)
|
||||
Arrays.fill(bytes, (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()
|
||||
{
|
||||
return numberOfPosToRender;
|
||||
}
|
||||
|
||||
public byte getNthDetailLevel(int n)
|
||||
{
|
||||
return (byte) posToRender[n * 3];
|
||||
}
|
||||
|
||||
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 * 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();
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.lod;
|
||||
|
||||
/**
|
||||
* A level container is a quad tree level
|
||||
*/
|
||||
public interface LevelContainer
|
||||
{
|
||||
/**
|
||||
* With this you can add data to the level container
|
||||
* @param data actual data to add in an 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
|
||||
*/
|
||||
boolean addData(long data, int posX, int posZ, int index);
|
||||
|
||||
/**
|
||||
* With this you can add data to the level container
|
||||
* @param data actual data to add in an 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
|
||||
*/
|
||||
boolean addVerticalData(long[] data, int posX, int posZ);
|
||||
|
||||
/**
|
||||
* With this you can add data to the level container
|
||||
* @param data actual data to add in an 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
|
||||
*/
|
||||
boolean addSingleData(long data, int posX, int posZ);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
long getData(int posX, int posZ, int index);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
long getSingleData(int posX, int posZ);
|
||||
|
||||
/**
|
||||
* @param posX x position in the detail level
|
||||
* @param posZ z position in the detail level
|
||||
* @return true only if the data exist
|
||||
*/
|
||||
boolean doesItExist(int posX, int posZ);
|
||||
|
||||
/**
|
||||
* @return return the detailLevel of this level container
|
||||
*/
|
||||
byte getDetailLevel();
|
||||
|
||||
|
||||
int getMaxVerticalData();
|
||||
|
||||
/** Clears the dataPoint at the given array index */
|
||||
void clear(int posX, int posZ);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
LevelContainer expand();
|
||||
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
void updateData(LevelContainer lowerLevelContainer, int posX, int posZ);
|
||||
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
* @return data as a String
|
||||
*/
|
||||
byte[] toDataString();
|
||||
|
||||
|
||||
/**
|
||||
* This will give the data to save in the file
|
||||
* @return data as a String
|
||||
*/
|
||||
int getMaxNumberOfLods();
|
||||
}
|
||||
@@ -1,908 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.lod;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.GenerationPriority;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.handlers.LodDimensionFileHandler;
|
||||
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||
import com.seibel.lod.core.objects.PosToRenderContainer;
|
||||
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.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This object holds all loaded LOD regions
|
||||
* for a given dimension. <Br><Br>
|
||||
*
|
||||
* <strong>Coordinate Standard: </strong><br>
|
||||
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
|
||||
* unless stated otherwise. <br>
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class LodDimension
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
public final IDimensionTypeWrapper dimension;
|
||||
|
||||
/** measured in regions */
|
||||
private volatile int width;
|
||||
/** measured in regions */
|
||||
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;
|
||||
|
||||
/** 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[][] regenRegionBuffer;
|
||||
/** stores if the buffer size at the given x and z index needs to be changed */
|
||||
private volatile boolean[][] recreateRegionBuffer;
|
||||
|
||||
/**
|
||||
* if true that means there are regions in this dimension
|
||||
* that need to have their buffers rebuilt.
|
||||
*/
|
||||
public volatile boolean regenDimensionBuffers = false;
|
||||
|
||||
private LodDimensionFileHandler fileHandler;
|
||||
|
||||
private final RegionPos center;
|
||||
|
||||
/** prevents the cutAndExpandThread from expanding at the same location multiple times */
|
||||
private volatile AbstractChunkPosWrapper lastExpandedChunk;
|
||||
/** prevents the cutAndExpandThread from cutting at the same location multiple times */
|
||||
private volatile AbstractChunkPosWrapper lastCutChunk;
|
||||
private final ExecutorService cutAndExpandThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - Cut and Expand"));
|
||||
|
||||
/**
|
||||
* Creates the dimension centered at (0,0)
|
||||
* @param newWidth in regions
|
||||
*/
|
||||
public LodDimension(IDimensionTypeWrapper newDimension, LodWorld lodWorld, int newWidth)
|
||||
{
|
||||
lastCutChunk = null;
|
||||
lastExpandedChunk = null;
|
||||
dimension = newDimension;
|
||||
width = newWidth;
|
||||
halfWidth = width / 2;
|
||||
|
||||
if (newDimension != null && lodWorld != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
// determine the save folder
|
||||
File saveDir;
|
||||
if (MC.hasSinglePlayerServer())
|
||||
{
|
||||
// local world
|
||||
|
||||
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(newDimension);
|
||||
saveDir = new File(serverWorld.getSaveFolder().getCanonicalFile().getPath() + File.separatorChar + "lod");
|
||||
}
|
||||
else
|
||||
{
|
||||
// connected to server
|
||||
|
||||
saveDir = new File(MC.getGameDirectory().getCanonicalFile().getPath() +
|
||||
File.separatorChar + "lod server data" + File.separatorChar + MC.getCurrentDimensionId());
|
||||
}
|
||||
|
||||
fileHandler = new LodDimensionFileHandler(saveDir, this);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// the file handler wasn't able to be created
|
||||
// we won't be able to read or write any files
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
regenRegionBuffer = new boolean[width][width];
|
||||
recreateRegionBuffer = new boolean[width][width];
|
||||
|
||||
center = new RegionPos(0, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 synchronized void move(RegionPos regionOffset)
|
||||
{
|
||||
int xOffset = regionOffset.x;
|
||||
int zOffset = regionOffset.z;
|
||||
|
||||
// if the x or z offset is equal to or greater than
|
||||
// the total width, 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++)
|
||||
regions[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)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[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)
|
||||
regions[x][z] = regions[x + xOffset][z];
|
||||
else
|
||||
regions[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)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[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)
|
||||
regions[x][z] = regions[x][z + zOffset];
|
||||
else
|
||||
regions[x][z] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// update the new center
|
||||
center.x += xOffset;
|
||||
center.z += zOffset;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the region at the given LevelPos
|
||||
* <br>
|
||||
* Returns null if the region doesn't exist
|
||||
* or is outside the loaded area.
|
||||
*/
|
||||
public LodRegion getRegion(byte detailLevel, int levelPosX, int levelPosZ)
|
||||
{
|
||||
int xRegion = LevelPosUtil.getRegion(detailLevel, levelPosX);
|
||||
int zRegion = LevelPosUtil.getRegion(detailLevel, levelPosZ);
|
||||
int xIndex = (xRegion - center.x) + halfWidth;
|
||||
int zIndex = (zRegion - center.z) + halfWidth;
|
||||
|
||||
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)
|
||||
return null;
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(int regionPosX, int regionPosZ)
|
||||
{
|
||||
int xIndex = (regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(regionPosX, regionPosZ))
|
||||
return null;
|
||||
//throw new ArrayIndexOutOfBoundsException("Region " + regionPosX + " " + regionPosZ + " out of range");
|
||||
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
/** Useful when iterating over every region. */
|
||||
public LodRegion getRegionByArrayIndex(int xIndex, int zIndex)
|
||||
{
|
||||
return regions[xIndex][zIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the LodRegion at the location of newRegion with newRegion.
|
||||
* @throws ArrayIndexOutOfBoundsException if newRegion is outside what can be stored in this LodDimension.
|
||||
*/
|
||||
public synchronized void addOrOverwriteRegion(LodRegion newRegion) throws ArrayIndexOutOfBoundsException
|
||||
{
|
||||
int xIndex = (newRegion.regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (newRegion.regionPosZ - center.z) + halfWidth;
|
||||
|
||||
if (!regionIsInRange(newRegion.regionPosX, newRegion.regionPosZ))
|
||||
// out of range
|
||||
throw new ArrayIndexOutOfBoundsException("Region " + newRegion.regionPosX + ", " + newRegion.regionPosZ + " out of range");
|
||||
|
||||
regions[xIndex][zIndex] = newRegion;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes nodes that are a higher detail then necessary, freeing
|
||||
* up memory.
|
||||
*/
|
||||
public void cutRegionNodesAsync(int playerPosX, int playerPosZ)
|
||||
{
|
||||
AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||
|
||||
if (lastCutChunk == null)
|
||||
lastCutChunk = FACTORY.createChunkPos(newPlayerChunk.getX() + 1, newPlayerChunk.getZ() - 1);
|
||||
|
||||
// don't run the tree cutter multiple times
|
||||
// for the same location
|
||||
if (newPlayerChunk.getX() != lastCutChunk.getX() || newPlayerChunk.getZ() != lastCutChunk.getZ())
|
||||
{
|
||||
lastCutChunk = newPlayerChunk;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte minAllowedDetailLevel;
|
||||
|
||||
// go over every region in the dimension
|
||||
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;
|
||||
|
||||
if (regions[x][z] != null)
|
||||
{
|
||||
// check what detail level this region should be
|
||||
// and cut it if it is higher then that
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeCutDetailFromDistance(minDistance);
|
||||
minAllowedDetailLevel = DetailDistanceUtil.getCutLodDetail(detail);
|
||||
|
||||
if (regions[x][z].getMinDetailLevel() > minAllowedDetailLevel)
|
||||
{
|
||||
regions[x][z].cutTree(minAllowedDetailLevel);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
}
|
||||
}// region z
|
||||
}// region z
|
||||
});
|
||||
|
||||
cutAndExpandThread.execute(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/** Either expands or loads all regions in the rendered LOD area */
|
||||
public void expandOrLoadRegionsAsync(int playerPosX, int playerPosZ)
|
||||
{
|
||||
DistanceGenerationMode generationMode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
AbstractChunkPosWrapper newPlayerChunk = FACTORY.createChunkPos(LevelPosUtil.getChunkPos((byte) 0, playerPosX), LevelPosUtil.getChunkPos((byte) 0, playerPosZ));
|
||||
VerticalQuality verticalQuality = CONFIG.client().graphics().quality().getVerticalQuality();
|
||||
|
||||
|
||||
if (lastExpandedChunk == null)
|
||||
lastExpandedChunk = FACTORY.createChunkPos(newPlayerChunk.getX() + 1, newPlayerChunk.getZ() - 1);
|
||||
|
||||
// don't run the expander multiple times
|
||||
// for the same location
|
||||
if (newPlayerChunk.getX() != lastExpandedChunk.getX() || newPlayerChunk.getZ() != lastExpandedChunk.getZ())
|
||||
{
|
||||
lastExpandedChunk = newPlayerChunk;
|
||||
|
||||
Thread thread = new Thread(() ->
|
||||
{
|
||||
int regionX;
|
||||
int regionZ;
|
||||
LodRegion region;
|
||||
int minDistance;
|
||||
byte detail;
|
||||
byte levelToGen;
|
||||
|
||||
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;
|
||||
final RegionPos regionPos = new RegionPos(regionX, regionZ);
|
||||
region = regions[x][z];
|
||||
|
||||
minDistance = LevelPosUtil.minDistance(LodUtil.REGION_DETAIL_LEVEL, regionX, regionZ, playerPosX, playerPosZ);
|
||||
detail = DetailDistanceUtil.getTreeGenDetailFromDistance(minDistance);
|
||||
levelToGen = DetailDistanceUtil.getLodGenDetail(detail).detailLevel;
|
||||
|
||||
// check that the region isn't null and at least this detail level
|
||||
if (region == null || region.getGenerationMode() != generationMode)
|
||||
{
|
||||
// First case, region has to be created
|
||||
|
||||
// try to get the region from file
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
|
||||
// if there is no region file create an empty region
|
||||
if (regions[x][z] == null)
|
||||
regions[x][z] = new LodRegion(levelToGen, regionPos, generationMode, verticalQuality);
|
||||
|
||||
regenRegionBuffer[x][z] = true;
|
||||
regenDimensionBuffers = true;
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
else if (region.getMinDetailLevel() > levelToGen)
|
||||
{
|
||||
// Second case, the region exists at a higher detail level.
|
||||
|
||||
// Expand the region by introducing the missing layer
|
||||
region.growTree(levelToGen);
|
||||
regions[x][z] = getRegionFromFile(regionPos, levelToGen, generationMode, verticalQuality);
|
||||
recreateRegionBuffer[x][z] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cutAndExpandThread.execute(thread);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use addVerticalData when possible.
|
||||
* Add the given LOD to this dimension at the coordinate
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinate it will be overwritten.
|
||||
*/
|
||||
public Boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data, boolean dontSave)
|
||||
{
|
||||
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
|
||||
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||
|
||||
// don't continue if the region can't be saved
|
||||
LodRegion region = getRegion(regionPosX, regionPosZ);
|
||||
if (region == null)
|
||||
return false;
|
||||
|
||||
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 = (regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
regenRegionBuffer[xIndex][zIndex] = true;
|
||||
regenDimensionBuffers = true;
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// If this happens, the method was probably
|
||||
// called when the dimension was changing size.
|
||||
// Hopefully this shouldn't be an issue.
|
||||
}
|
||||
}
|
||||
|
||||
return nodeAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add whole column of LODs to this dimension at the coordinate
|
||||
* stored in the LOD. If an LOD already exists at the given
|
||||
* coordinate it will be overwritten.
|
||||
*/
|
||||
public Boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data, boolean dontSave)
|
||||
{
|
||||
int regionPosX = LevelPosUtil.getRegion(detailLevel, posX);
|
||||
int regionPosZ = LevelPosUtil.getRegion(detailLevel, posZ);
|
||||
|
||||
// don't continue if the region can't be saved
|
||||
LodRegion region = getRegion(regionPosX, regionPosZ);
|
||||
if (region == null)
|
||||
return false;
|
||||
|
||||
boolean nodeAdded = region.addVerticalData(detailLevel, posX, posZ, 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 = (regionPosX - center.x) + halfWidth;
|
||||
int zIndex = (regionPosZ - center.z) + halfWidth;
|
||||
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
regenRegionBuffer[xIndex][zIndex] = true;
|
||||
regenDimensionBuffers = true;
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
// If this happens, the method was probably
|
||||
// called when the dimension was changing size.
|
||||
// Hopefully this shouldn't be an issue.
|
||||
}
|
||||
}
|
||||
|
||||
return nodeAdded;
|
||||
}
|
||||
|
||||
/** marks the region at the given region position to have its buffer rebuilt */
|
||||
public void markRegionBufferToRegen(int xRegion, int zRegion)
|
||||
{
|
||||
int xIndex = (xRegion - center.x) + halfWidth;
|
||||
int zIndex = (zRegion - center.z) + halfWidth;
|
||||
regenRegionBuffer[xIndex][zIndex] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns every position that need to be generated based on the position of the player
|
||||
*/
|
||||
public PosToGenerateContainer getPosToGenerate(int maxDataToGenerate, int playerBlockPosX, int playerBlockPosZ)
|
||||
{
|
||||
PosToGenerateContainer posToGenerate;
|
||||
LodRegion lodRegion;
|
||||
// all the following values are used for the spiral matrix visit
|
||||
// x and z are the matrix coord
|
||||
// dx and dz is the next move on the coordinate in the range -1 0 +1
|
||||
int x, z, dx, dz, t;
|
||||
x = 0;
|
||||
z = 0;
|
||||
dx = 0;
|
||||
dz = -1;
|
||||
|
||||
// We can use two type of generation scheduling
|
||||
switch (CONFIG.client().worldGenerator().getGenerationPriority())
|
||||
{
|
||||
default:
|
||||
case NEAR_FIRST:
|
||||
//in the NEAR_FIRST generation scheduling we prioritize the nearest un-generated position to the player
|
||||
//the chunk position to generate will be stored in a posToGenerate object
|
||||
posToGenerate = new PosToGenerateContainer((byte) 10, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
|
||||
|
||||
int playerChunkX = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosX);
|
||||
int playerChunkZ = LevelPosUtil.getChunkPos(LodUtil.BLOCK_DETAIL_LEVEL, playerBlockPosZ);
|
||||
|
||||
int complexity;
|
||||
int xChunkToCheck;
|
||||
int zChunkToCheck;
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
long data;
|
||||
int numbChunksWide = (width) * 32;
|
||||
int circleLimit = Integer.MAX_VALUE;
|
||||
|
||||
//posToGenerate is using an insertion sort algorithm which can become really fast if the
|
||||
//original data order is almost ordered. For this reason we explore the matrix of the position to generate
|
||||
//with a spiral matrix visit (a square spiral is almost ordered in the "nearest to farthest" order)
|
||||
for (int i = 0; i < numbChunksWide * numbChunksWide; i++)
|
||||
{
|
||||
//Firstly we check if the posToGenerate has been filled
|
||||
if (maxDataToGenerate == 0)
|
||||
{
|
||||
maxDataToGenerate--;
|
||||
//if it has been filled then we set a stop distance
|
||||
//the stop distance will be current distance (generically x) per square root of 2
|
||||
//this would guarantee a circular generation since (Math.abs(x) * 1.41f) is the
|
||||
//radius of a circle that inscribe a square
|
||||
circleLimit = (int) (Math.abs(x) * 1.41f);
|
||||
}
|
||||
//This second if check if we reached the circleLimit decided in the previous if
|
||||
//if so we stop
|
||||
else if (maxDataToGenerate < 0)
|
||||
{
|
||||
if (circleLimit < Math.abs(x) && circleLimit < Math.abs(z))
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
xChunkToCheck = x + playerChunkX;
|
||||
zChunkToCheck = z + playerChunkZ;
|
||||
|
||||
//we get the lod region in which the chunk is present
|
||||
lodRegion = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, zChunkToCheck);
|
||||
if (lodRegion == null)
|
||||
continue;
|
||||
|
||||
//Now we check if the current chunk has been generated with the correct complexity
|
||||
//if(lodRegion.isChunkPreGenerated(xChunkToCheck,zChunkToCheck))
|
||||
// complexity = DistanceGenerationMode.SERVER.complexity;
|
||||
//else
|
||||
complexity = CONFIG.client().worldGenerator().getDistanceGenerationMode().complexity;
|
||||
|
||||
|
||||
//we create the level position info of the chunk
|
||||
detailLevel = lodRegion.getMinDetailLevel();
|
||||
posX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, xChunkToCheck, detailLevel);
|
||||
posZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, zChunkToCheck, detailLevel);
|
||||
|
||||
data = getSingleData(detailLevel, posX, posZ);
|
||||
|
||||
//we will generate the position only if the current generation complexity is lower than the target one.
|
||||
//an un-generated area will always have 0 generation
|
||||
if (DataPointUtil.getGenerationMode(data) < complexity)
|
||||
{
|
||||
posToGenerate.addPosToGenerate(detailLevel, posX, posZ);
|
||||
if (maxDataToGenerate >= 0)
|
||||
maxDataToGenerate--;
|
||||
}
|
||||
|
||||
//with this code section we find the next chunk to check
|
||||
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:
|
||||
//in the FAR_FIRST generation we dedicate part of the generation process to the far region with really
|
||||
//low detail quality.
|
||||
|
||||
posToGenerate = new PosToGenerateContainer((byte) 8, maxDataToGenerate, playerBlockPosX, playerBlockPosZ);
|
||||
|
||||
int xRegion;
|
||||
int zRegion;
|
||||
|
||||
for (int i = 0; i < width * width; i++)
|
||||
{
|
||||
xRegion = x + center.x;
|
||||
zRegion = z + center.z;
|
||||
|
||||
//All of this is handled directly by the region, which scan every pos from top to bottom of the quad tree
|
||||
lodRegion = getRegion(xRegion, zRegion);
|
||||
if (lodRegion != null)
|
||||
lodRegion.getPosToGenerate(posToGenerate, playerBlockPosX, playerBlockPosZ);
|
||||
|
||||
|
||||
//with this code section we find the next chunk to check
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the posToRender with the position to render for the regionPos given in input
|
||||
*/
|
||||
public void getPosToRender(PosToRenderContainer posToRender, RegionPos regionPos, int playerPosX,
|
||||
int playerPosZ)
|
||||
{
|
||||
LodRegion region = getRegion(regionPos.x, regionPos.z);
|
||||
if (region != null)
|
||||
region.getPosToRender(posToRender, playerPosX, playerPosZ, CONFIG.client().worldGenerator().getGenerationPriority() == GenerationPriority.NEAR_FIRST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how many vertical LODs could be used
|
||||
* for the given region at the given detail level
|
||||
*/
|
||||
public int getMaxVerticalData(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
|
||||
throw new IllegalArgumentException("getMaxVerticalData 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
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 DataPointUtil.EMPTY_DATA;
|
||||
|
||||
return region.getData(detailLevel, posX, posZ, verticalIndex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 long getSingleData(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 DataPointUtil.EMPTY_DATA;
|
||||
|
||||
return region.getSingleData(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
/** Clears the given region */
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the buffer at the given array index needs
|
||||
* to have its buffer regenerated.
|
||||
*/
|
||||
public boolean doesRegionNeedBufferRegen(int xIndex, int zIndex)
|
||||
{
|
||||
return regenRegionBuffer[xIndex][zIndex] || recreateRegionBuffer[xIndex][zIndex];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets if the buffer at the given array index needs
|
||||
* to have its buffer regenerated.
|
||||
*/
|
||||
public void setRegenRegionBufferByArrayIndex(int xIndex, int zIndex, boolean newRegen)
|
||||
{
|
||||
regenRegionBuffer[xIndex][zIndex] = newRegen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data point at the given LevelPos
|
||||
* 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(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
/** Returns true if a region exists at the given LevelPos */
|
||||
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
LodRegion region = getRegion(detailLevel, posX, posZ);
|
||||
return region != null && region.doesDataExist(detailLevel, posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the region at the given RegionPos from file,
|
||||
* if a file exists for that region.
|
||||
*/
|
||||
public LodRegion getRegionFromFile(RegionPos regionPos, byte detailLevel,
|
||||
DistanceGenerationMode generationMode, VerticalQuality verticalQuality)
|
||||
{
|
||||
return fileHandler != null ? fileHandler.loadRegionFromFile(detailLevel, regionPos, generationMode, verticalQuality) : null;
|
||||
}
|
||||
|
||||
/** Save all dirty regions in this LodDimension to file. */
|
||||
public void saveDirtyRegionsToFileAsync()
|
||||
{
|
||||
fileHandler.saveDirtyRegionsToFileAsync();
|
||||
}
|
||||
|
||||
|
||||
/** Return true if the chunk has been pregenerated in game */
|
||||
//public boolean isChunkPreGenerated(int xChunkPosWrapper, int zChunkPosWrapper)
|
||||
//{
|
||||
//
|
||||
// LodRegion region = getRegion(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPosWrapper, zChunkPosWrapper);
|
||||
// if (region == null)
|
||||
// return false;
|
||||
//
|
||||
// return region.isChunkPreGenerated(xChunkPosWrapper, zChunkPosWrapper);
|
||||
//}
|
||||
|
||||
/**
|
||||
* Returns whether the region at the given RegionPos
|
||||
* is within the loaded range.
|
||||
*/
|
||||
public boolean regionIsInRange(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - center.x) + halfWidth;
|
||||
int zIndex = (regionZ - center.z) + halfWidth;
|
||||
|
||||
return xIndex >= 0 && xIndex < width && zIndex >= 0 && zIndex < width;
|
||||
}
|
||||
|
||||
/** Returns the dimension's center region position X value */
|
||||
public int getCenterRegionPosX()
|
||||
{
|
||||
return center.x;
|
||||
}
|
||||
|
||||
/** Returns the dimension's center region position Z value */
|
||||
public int getCenterRegionPosZ()
|
||||
{
|
||||
return center.z;
|
||||
}
|
||||
|
||||
/** returns the width of the dimension in regions */
|
||||
public int getWidth()
|
||||
{
|
||||
// we want to get the length directly from the
|
||||
// source to make sure it is in sync with region
|
||||
// and isRegionDirty
|
||||
return regions != null ? regions.length : width;
|
||||
}
|
||||
|
||||
/** Update the width of this dimension, in regions */
|
||||
public void setRegionWidth(int newWidth)
|
||||
{
|
||||
width = newWidth;
|
||||
halfWidth = width/ 2;
|
||||
|
||||
regions = new LodRegion[width][width];
|
||||
isRegionDirty = new boolean[width][width];
|
||||
regenRegionBuffer = new boolean[width][width];
|
||||
recreateRegionBuffer = new boolean[width][width];
|
||||
|
||||
// populate isRegionDirty
|
||||
for (int i = 0; i < width; i++)
|
||||
for (int j = 0; j < width; j++)
|
||||
isRegionDirty[i][j] = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append("Dimension : \n");
|
||||
for (LodRegion[] lodRegions : regions)
|
||||
{
|
||||
for (LodRegion region : lodRegions)
|
||||
{
|
||||
if (region == null)
|
||||
stringBuilder.append("n");
|
||||
else
|
||||
stringBuilder.append(region.getMinDetailLevel());
|
||||
stringBuilder.append("\t");
|
||||
}
|
||||
stringBuilder.append("\n");
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
public boolean GetIsRegionDirty(int i, int j)
|
||||
{
|
||||
return isRegionDirty[i][j];
|
||||
}
|
||||
|
||||
public void SetIsRegionDirty(int i, int j, boolean val)
|
||||
{
|
||||
isRegionDirty[i][j] = val;
|
||||
}
|
||||
}
|
||||
@@ -1,611 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.lod;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.objects.PosToGenerateContainer;
|
||||
import com.seibel.lod.core.objects.PosToRenderContainer;
|
||||
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.LodUtil;
|
||||
|
||||
/**
|
||||
* This object holds all loaded LevelContainers acting as a quad tree
|
||||
* for a given region. <Br><Br>
|
||||
*
|
||||
* <strong>Coordinate Standard: </strong><br>
|
||||
* Coordinate called posX or posZ are relative LevelPos coordinates <br>
|
||||
* unless stated otherwise. <br>
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 10-10-2021
|
||||
*/
|
||||
public class LodRegion
|
||||
{
|
||||
/** Number of detail level supported by a region */
|
||||
private static final byte POSSIBLE_LOD = 10;
|
||||
|
||||
|
||||
/** Holds the lowest (least detailed) detail level in this region */
|
||||
private byte minDetailLevel;
|
||||
|
||||
/**
|
||||
* This holds all data for this region
|
||||
*/
|
||||
private final LevelContainer[] dataContainer;
|
||||
|
||||
/** This chunk Pos has been generated */
|
||||
//private final boolean[] preGeneratedChunkPos;
|
||||
|
||||
/** the generation mode for this region */
|
||||
private final DistanceGenerationMode generationMode;
|
||||
/** the vertical quality of this region */
|
||||
private final VerticalQuality verticalQuality;
|
||||
|
||||
/** this region's x RegionPos */
|
||||
public final int regionPosX;
|
||||
/** this region's z RegionPos */
|
||||
public final int regionPosZ;
|
||||
|
||||
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];
|
||||
|
||||
|
||||
// Initialize all the different matrices
|
||||
for (byte lod = minDetailLevel; lod <= LodUtil.REGION_DETAIL_LEVEL; lod++)
|
||||
{
|
||||
dataContainer[lod] = new VerticalLevelContainer(lod);
|
||||
}
|
||||
|
||||
boolean fileFound = false;
|
||||
|
||||
/*
|
||||
preGeneratedChunkPos = new boolean[32 * 32];
|
||||
if (MinecraftWrapper.INSTANCE.hasSinglePlayerServer() && LodConfig.CLIENT.worldGenerator.useExperimentalPreGenLoading.get())
|
||||
{
|
||||
File regionFileDirHead;
|
||||
File regionFileDirParent;
|
||||
// local world
|
||||
|
||||
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(MinecraftWrapper.INSTANCE.getCurrentDimension());
|
||||
|
||||
// provider needs a separate variable to prevent
|
||||
// the compiler from complaining
|
||||
StringBuilder string = new StringBuilder();
|
||||
try
|
||||
{
|
||||
ServerChunkProvider provider = serverWorld.getChunkSource();
|
||||
|
||||
//System.out.println(provider.dataStorage.dataFolder);
|
||||
regionFileDirHead = new File(provider.dataStorage.dataFolder.getCanonicalFile().getParentFile().toPath().toAbsolutePath().toString() + File.separatorChar + "region", "r." + regionPosZ + "." + regionPosX + ".mca");
|
||||
if (regionFileDirHead.exists())
|
||||
{
|
||||
regionFileDirParent = regionFileDirHead.getParentFile();
|
||||
//string.append(regionFileDirParent.toString());
|
||||
string.append(regionFileDirHead);
|
||||
RegionFile regionFile = new RegionFile(regionFileDirHead, regionFileDirParent, true);
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
for (int z = 0; z < 32; z++)
|
||||
{
|
||||
preGeneratedChunkPos[x * 32 + z] = regionFile.doesChunkExist(new ChunkPos(regionPosX * 32 + x, regionPosZ * 32 + z));
|
||||
}
|
||||
}
|
||||
|
||||
string.append("region " + regionPosX + " " + regionPosZ + "\n");
|
||||
for (int x = 0; x < 32; x++)
|
||||
{
|
||||
for (int z = 0; z < 32; z++)
|
||||
{
|
||||
//regionFile.doesChunkExist()
|
||||
string.append(preGeneratedChunkPos[x * 32 + z] + "\t");
|
||||
}
|
||||
string.append("\n");
|
||||
}
|
||||
regionFile.close();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println(string);
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** Return true if the chunk has been pregenerated in game */
|
||||
//public boolean isChunkPreGenerated(int xChunkPos, int zChunkPos)
|
||||
//{
|
||||
// xChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, xChunkPos);
|
||||
// zChunkPos = LevelPosUtil.getRegionModule(LodUtil.CHUNK_DETAIL_LEVEL, zChunkPos);
|
||||
// return preGeneratedChunkPos[xChunkPos * 32 + zChunkPos];
|
||||
//}
|
||||
|
||||
/**
|
||||
* Inserts the data point into the region.
|
||||
* <p>
|
||||
* TODO this will always return true unless it has
|
||||
* @return true if the data was added successfully
|
||||
*/
|
||||
public boolean addData(byte detailLevel, int posX, int posZ, int verticalIndex, long data)
|
||||
{
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
|
||||
// The dataContainer could have null entries if the
|
||||
// detailLevel changes.
|
||||
if (this.dataContainer[detailLevel] == null)
|
||||
{
|
||||
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
|
||||
}
|
||||
|
||||
this.dataContainer[detailLevel].addData(data, posX, posZ, verticalIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts the vertical data into the region.
|
||||
* <p>
|
||||
* TODO this will always return true unless it has
|
||||
* @return true if the data was added successfully
|
||||
*/
|
||||
public boolean addVerticalData(byte detailLevel, int posX, int posZ, long[] data)
|
||||
{
|
||||
//position is already relative
|
||||
//posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
//posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
|
||||
// The dataContainer could have null entries if the
|
||||
// detailLevel changes.
|
||||
if (this.dataContainer[detailLevel] == null)
|
||||
this.dataContainer[detailLevel] = new VerticalLevelContainer(detailLevel);
|
||||
|
||||
return this.dataContainer[detailLevel].addVerticalData(data, posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dataPoint at the given relative position.
|
||||
* @return the data at the relative pos and detail level,
|
||||
* 0 if the data doesn't exist.
|
||||
*/
|
||||
public long getData(byte detailLevel, int posX, int posZ, int verticalIndex)
|
||||
{
|
||||
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dataPoint at the given relative position.
|
||||
* @return the data at the relative pos and detail level,
|
||||
* 0 if the data doesn't exist.
|
||||
*/
|
||||
public long getSingleData(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return dataContainer[detailLevel].getSingleData(posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the datapoint at the given relative position
|
||||
*/
|
||||
public void clear(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
dataContainer[detailLevel].clear(posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will fill the posToGenerate array with all levelPos that
|
||||
* are render-able.
|
||||
* <p>
|
||||
* TODO why don't we return the posToGenerate, it would make this easier to understand
|
||||
*/
|
||||
public void getPosToGenerate(PosToGenerateContainer posToGenerate,
|
||||
int playerBlockPosX, int playerBlockPosZ)
|
||||
{
|
||||
getPosToGenerate(posToGenerate, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerBlockPosX, playerBlockPosZ);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A recursive method that fills the posToGenerate array with all levelPos that
|
||||
* need to be generated.
|
||||
* <p>
|
||||
* TODO why don't we return the posToGenerate, it would make this easier to understand
|
||||
*/
|
||||
private void getPosToGenerate(PosToGenerateContainer posToGenerate, byte detailLevel,
|
||||
int childOffsetPosX, int childOffsetPosZ, int playerPosX, int playerPosZ)
|
||||
{
|
||||
// equivalent to 2^(...)
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
|
||||
// calculate what LevelPos are in range to generate
|
||||
int maxDistance = LevelPosUtil.maxDistance(detailLevel, childOffsetPosX, childOffsetPosZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
|
||||
// determine this child's levelPos
|
||||
byte childDetailLevel = (byte) (detailLevel - 1);
|
||||
int childPosX = childOffsetPosX * 2;
|
||||
int childPosZ = childOffsetPosZ * 2;
|
||||
|
||||
int childSize = 1 << (LodUtil.REGION_DETAIL_LEVEL - childDetailLevel);
|
||||
|
||||
byte targetDetailLevel = DetailDistanceUtil.getLodGenDetail(DetailDistanceUtil.getGenerationDetailFromDistance(maxDistance)).detailLevel;
|
||||
if (targetDetailLevel <= detailLevel)
|
||||
{
|
||||
if (targetDetailLevel == detailLevel)
|
||||
{
|
||||
if (!doesDataExist(detailLevel, childOffsetPosX, childOffsetPosZ))
|
||||
posToGenerate.addPosToGenerate(detailLevel, childOffsetPosX + regionPosX * size, childOffsetPosZ + regionPosZ * size);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we want at max one request per chunk (since the world generator creates chunks).
|
||||
// So for lod smaller than a chunk, only recurse down
|
||||
// the top right child
|
||||
|
||||
if (detailLevel > LodUtil.CHUNK_DETAIL_LEVEL)
|
||||
{
|
||||
int ungeneratedChildren = 0;
|
||||
|
||||
// make sure all children are generated to this detailLevel
|
||||
for (int x = 0; x <= 1; x++)
|
||||
{
|
||||
for (int z = 0; z <= 1; z++)
|
||||
{
|
||||
if (!doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
|
||||
{
|
||||
ungeneratedChildren++;
|
||||
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + x + regionPosX * childSize, childPosZ + z + regionPosZ * childSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only if all the children are correctly generated
|
||||
// should we go deeper
|
||||
if (ungeneratedChildren == 0)
|
||||
for (int x = 0; x <= 1; x++)
|
||||
for (int z = 0; z <= 1; z++)
|
||||
getPosToGenerate(posToGenerate, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The detail Level is smaller than a chunk.
|
||||
// Only recurse down the top right child.
|
||||
|
||||
if (DetailDistanceUtil.getLodGenDetail(childDetailLevel).detailLevel <= (childDetailLevel))
|
||||
{
|
||||
if (!doesDataExist(childDetailLevel, childPosX, childPosZ))
|
||||
posToGenerate.addPosToGenerate(childDetailLevel, childPosX + regionPosX * childSize, childPosZ + regionPosZ * childSize);
|
||||
else
|
||||
getPosToGenerate(posToGenerate, childDetailLevel, childPosX, childPosZ, playerPosX, playerPosZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we have gone beyond the target Detail level
|
||||
// we can stop generating
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public void getPosToRender(PosToRenderContainer posToRender,
|
||||
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
|
||||
{
|
||||
getPosToRender(posToRender, LodUtil.REGION_DETAIL_LEVEL, 0, 0, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 posX, int posZ,
|
||||
int playerPosX, int playerPosZ, boolean requireCorrectDetailLevel)
|
||||
{
|
||||
// equivalent to 2^(...)
|
||||
int size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
|
||||
byte desiredLevel;
|
||||
int maxDistance;
|
||||
int minDistance;
|
||||
int childLevel;
|
||||
|
||||
|
||||
// calculate the LevelPos that are in range
|
||||
maxDistance = LevelPosUtil.maxDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
desiredLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(maxDistance));
|
||||
minDistance = LevelPosUtil.minDistance(detailLevel, posX, posZ, playerPosX, playerPosZ, regionPosX, regionPosZ);
|
||||
childLevel = DetailDistanceUtil.getLodDrawDetail(DetailDistanceUtil.getDrawDetailFromDistance(minDistance));
|
||||
|
||||
if (detailLevel == childLevel - 1)
|
||||
{
|
||||
posToRender.addPosToRender(detailLevel,
|
||||
posX + regionPosX * size,
|
||||
posZ + regionPosZ * size);
|
||||
}
|
||||
else
|
||||
//if (desiredLevel > detailLevel)
|
||||
//{
|
||||
// we have gone beyond the target Detail level
|
||||
// we can stop generating
|
||||
//} else
|
||||
if (desiredLevel == detailLevel)
|
||||
{
|
||||
posToRender.addPosToRender(detailLevel,
|
||||
posX + regionPosX * size,
|
||||
posZ + regionPosZ * size);
|
||||
}
|
||||
else //case where (detailLevel > desiredLevel)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
if (doesDataExist(childDetailLevel, childPosX + x, childPosZ + z))
|
||||
{
|
||||
if (!requireCorrectDetailLevel)
|
||||
childrenCount++;
|
||||
else
|
||||
getPosToRender(posToRender, childDetailLevel, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!requireCorrectDetailLevel)
|
||||
{
|
||||
// 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, childPosX + x, childPosZ + z, playerPosX, playerPosZ, requireCorrectDetailLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
posToRender.addPosToRender(detailLevel,
|
||||
posX + regionPosX * size,
|
||||
posZ + regionPosZ * size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates all children.
|
||||
* <p>
|
||||
* TODO could this be renamed mergeArea?
|
||||
*/
|
||||
public void updateArea(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
int width;
|
||||
int startX;
|
||||
int startZ;
|
||||
|
||||
// TODO what are each of these loops updating?
|
||||
for (byte down = (byte) (minDetailLevel + 1); down <= detailLevel; down++)
|
||||
{
|
||||
startX = LevelPosUtil.convert(detailLevel, posX, down);
|
||||
startZ = LevelPosUtil.convert(detailLevel, posZ, down);
|
||||
width = 1 << (detailLevel - down);
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
for (int z = 0; z < width; z++)
|
||||
update(down, startX + x, startZ + z);
|
||||
}
|
||||
|
||||
|
||||
for (byte up = (byte) (detailLevel + 1); up <= LodUtil.REGION_DETAIL_LEVEL; up++)
|
||||
{
|
||||
update(up,
|
||||
LevelPosUtil.convert(detailLevel, posX, up),
|
||||
LevelPosUtil.convert(detailLevel, posZ, up));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the child at the given relative Pos
|
||||
* <p>
|
||||
* TODO could this be renamed mergeChildData?
|
||||
*/
|
||||
private void update(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
dataContainer[detailLevel].updateData(dataContainer[detailLevel - 1], posX, posZ);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns if data exists at the given relative Pos.
|
||||
*/
|
||||
public boolean doesDataExist(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if (detailLevel < minDetailLevel)
|
||||
return false;
|
||||
|
||||
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
|
||||
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
|
||||
|
||||
if (dataContainer[detailLevel] == null)
|
||||
return false;
|
||||
|
||||
return dataContainer[detailLevel].doesItExist(posX, posZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the generation mode for the data point at the given relative pos.
|
||||
*/
|
||||
public byte getGenerationMode(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
if (dataContainer[detailLevel].doesItExist(posX, posZ))
|
||||
// We take the bottom information always
|
||||
// TODO what does that mean? bottom of what?
|
||||
return DataPointUtil.getGenerationMode(dataContainer[detailLevel].getSingleData(posX, posZ));
|
||||
else
|
||||
return DistanceGenerationMode.NONE.complexity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the lowest (least detailed) detail level in this region
|
||||
* TODO is that right?
|
||||
*/
|
||||
public byte getMinDetailLevel()
|
||||
{
|
||||
return minDetailLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LevelContainer for the detailLevel
|
||||
* @throws IllegalArgumentException if the detailLevel is less than minDetailLevel
|
||||
*/
|
||||
public LevelContainer getLevel(byte detailLevel)
|
||||
{
|
||||
if (detailLevel < minDetailLevel)
|
||||
throw new IllegalArgumentException("getLevel asked for a detail level that does not exist: minimum: [" + minDetailLevel + "] level requested: [" + detailLevel + "]");
|
||||
|
||||
return dataContainer[detailLevel];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the levelContainer to this Region, updating the minDetailLevel
|
||||
* if necessary.
|
||||
* @throws IllegalArgumentException if the LevelContainer's detailLevel
|
||||
* is 2 or more detail levels lower than the
|
||||
* minDetailLevel of this region.
|
||||
*/
|
||||
public void addLevelContainer(LevelContainer levelContainer)
|
||||
{
|
||||
if (levelContainer.getDetailLevel() < minDetailLevel - 1)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"the LevelContainer's detailLevel was "
|
||||
+ "[" + levelContainer.getDetailLevel() + "] but this region "
|
||||
+ "only allows adding LevelContainers with a "
|
||||
+ "detail level of [" + (minDetailLevel - 1) + "]");
|
||||
}
|
||||
|
||||
if (levelContainer.getDetailLevel() == minDetailLevel - 1)
|
||||
minDetailLevel = levelContainer.getDetailLevel();
|
||||
|
||||
dataContainer[levelContainer.getDetailLevel()] = levelContainer;
|
||||
}
|
||||
|
||||
// TODO James thinks cutTree and growTree (which he renamed to match cutTree)
|
||||
// should have more descriptive names, to make sure the "Tree" portion isn't
|
||||
// confused with Minecraft trees (the plant).
|
||||
|
||||
/**
|
||||
* Removes any dataContainers that are higher than
|
||||
* the given detailLevel
|
||||
*/
|
||||
public void cutTree(byte detailLevel)
|
||||
{
|
||||
if (detailLevel > minDetailLevel)
|
||||
{
|
||||
for (byte detailLevelIndex = 0; detailLevelIndex < detailLevel; detailLevelIndex++)
|
||||
dataContainer[detailLevelIndex] = null;
|
||||
|
||||
minDetailLevel = detailLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make this region more detailed to the detailLevel given.
|
||||
* TODO is that correct?
|
||||
*/
|
||||
public void growTree(byte detailLevel)
|
||||
{
|
||||
if (detailLevel < minDetailLevel)
|
||||
{
|
||||
for (byte detailLevelIndex = (byte) (minDetailLevel - 1); detailLevelIndex >= detailLevel; detailLevelIndex--)
|
||||
{
|
||||
if (dataContainer[detailLevelIndex + 1] == null)
|
||||
dataContainer[detailLevelIndex + 1] = new VerticalLevelContainer((byte) (detailLevelIndex + 1));
|
||||
|
||||
dataContainer[detailLevelIndex] = dataContainer[detailLevelIndex + 1].expand();
|
||||
}
|
||||
minDetailLevel = detailLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return RegionPos of this lod region
|
||||
*/
|
||||
public RegionPos getRegionPos()
|
||||
{
|
||||
return new RegionPos(regionPosX, regionPosZ);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many LODs are in this region
|
||||
*/
|
||||
public int getNumberOfLods()
|
||||
{
|
||||
int count = 0;
|
||||
for (LevelContainer container : dataContainer)
|
||||
count += container.getMaxNumberOfLods();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public VerticalQuality getVerticalQuality()
|
||||
{
|
||||
return verticalQuality;
|
||||
}
|
||||
|
||||
public DistanceGenerationMode getGenerationMode()
|
||||
{
|
||||
return generationMode;
|
||||
}
|
||||
|
||||
public int getMaxVerticalData(byte detailLevel)
|
||||
{
|
||||
return dataContainer[detailLevel].getMaxVerticalData();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getLevel(LodUtil.REGION_DETAIL_LEVEL).toString();
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.lod;
|
||||
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
|
||||
/**
|
||||
* This stores all LODs for a given world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @author Leonardo Amato
|
||||
* @version 9-27-2021
|
||||
*/
|
||||
public class LodWorld
|
||||
{
|
||||
/** name of this world */
|
||||
private String worldName;
|
||||
|
||||
/** dimensions in this world */
|
||||
private Map<IDimensionTypeWrapper, LodDimension> lodDimensions;
|
||||
|
||||
/** If true then the LOD world is setup and ready to use */
|
||||
private boolean isWorldLoaded = false;
|
||||
|
||||
/** the name given to the world if it isn't loaded */
|
||||
public static final String NO_WORLD_LOADED = "No world loaded";
|
||||
|
||||
|
||||
|
||||
public LodWorld()
|
||||
{
|
||||
worldName = NO_WORLD_LOADED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set up the LodWorld with the given newWorldName. <br>
|
||||
* This should be done whenever loading a new world. <br><br>
|
||||
* <p>
|
||||
* Note a System.gc() call may be in order after calling this <Br>
|
||||
* since a lot of LOD data is now homeless. <br>
|
||||
* @param newWorldName name of the world
|
||||
*/
|
||||
public void selectWorld(String newWorldName)
|
||||
{
|
||||
if (newWorldName.isEmpty())
|
||||
{
|
||||
deselectWorld();
|
||||
return;
|
||||
}
|
||||
|
||||
if (worldName.equals(newWorldName))
|
||||
// don't recreate everything if we
|
||||
// didn't actually change worlds
|
||||
return;
|
||||
|
||||
worldName = newWorldName;
|
||||
lodDimensions = new Hashtable<>();
|
||||
isWorldLoaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the worldName to "No world loaded"
|
||||
* and clear the lodDimensions Map. <br>
|
||||
* This should be done whenever unloaded a world. <br><br>
|
||||
* <p>
|
||||
* Note a System.gc() call may be in order after calling this <Br>
|
||||
* since a lot of LOD data is now homeless. <br>
|
||||
*/
|
||||
public void deselectWorld()
|
||||
{
|
||||
worldName = NO_WORLD_LOADED;
|
||||
lodDimensions = null;
|
||||
isWorldLoaded = false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds newDimension to this world, if a LodDimension
|
||||
* already exists for the given dimension it is replaced.
|
||||
*/
|
||||
public void addLodDimension(LodDimension newDimension)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return;
|
||||
|
||||
lodDimensions.put(newDimension.dimension, newDimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if no LodDimension exists for the given dimension
|
||||
*/
|
||||
public LodDimension getLodDimension(IDimensionTypeWrapper dimType)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return null;
|
||||
|
||||
return lodDimensions.get(dimType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes the max width in regions that each LodDimension
|
||||
* should use.
|
||||
*/
|
||||
public void resizeDimensionRegionWidth(int newRegionWidth)
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return;
|
||||
|
||||
saveAllDimensions();
|
||||
|
||||
for (IDimensionTypeWrapper key : lodDimensions.keySet())
|
||||
lodDimensions.get(key).setRegionWidth(newRegionWidth);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests all dimensions save any dirty regions they may have.
|
||||
*/
|
||||
public void saveAllDimensions()
|
||||
{
|
||||
if (lodDimensions == null)
|
||||
return;
|
||||
|
||||
// TODO we should only print this if lods were actually saved to file
|
||||
// but that requires a LodDimension.hasDirtyRegions() method or something similar
|
||||
ClientApi.LOGGER.info("Saving LODs");
|
||||
|
||||
for (IDimensionTypeWrapper key : lodDimensions.keySet())
|
||||
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
|
||||
}
|
||||
|
||||
|
||||
public boolean getIsWorldNotLoaded()
|
||||
{
|
||||
return !isWorldLoaded;
|
||||
}
|
||||
|
||||
public String getWorldName()
|
||||
{
|
||||
return worldName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "World name: " + worldName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.lod;
|
||||
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
|
||||
/**
|
||||
* This object is similar to ChunkPos or BlockPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 8-21-2021
|
||||
*/
|
||||
public class RegionPos
|
||||
{
|
||||
private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
|
||||
public int x;
|
||||
public int z;
|
||||
|
||||
|
||||
/** Sets x and z to 0 */
|
||||
public RegionPos()
|
||||
{
|
||||
x = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
/** simple constructor that sets x and z to new x and z. */
|
||||
public RegionPos(int newX, int newZ)
|
||||
{
|
||||
x = newX;
|
||||
z = newZ;
|
||||
}
|
||||
|
||||
/** Converts from a BlockPos to a RegionPos */
|
||||
public RegionPos(AbstractBlockPosWrapper pos)
|
||||
{
|
||||
this(WRAPPER_FACTORY.createChunkPos(pos));
|
||||
}
|
||||
|
||||
/** Converts from a ChunkPos to a RegionPos */
|
||||
public RegionPos(AbstractChunkPosWrapper pos)
|
||||
{
|
||||
x = Math.floorDiv(pos.getX(), LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||
z = Math.floorDiv(pos.getZ(), LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||
}
|
||||
|
||||
/** Returns the ChunkPos at the center of this region */
|
||||
public AbstractChunkPosWrapper chunkPos()
|
||||
{
|
||||
return WRAPPER_FACTORY.createChunkPos(
|
||||
(x * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2,
|
||||
(z * LodUtil.REGION_WIDTH_IN_CHUNKS) + LodUtil.REGION_WIDTH_IN_CHUNKS / 2);
|
||||
}
|
||||
|
||||
/** Returns the BlockPos at the center of this region */
|
||||
public AbstractBlockPosWrapper blockPos()
|
||||
{
|
||||
return chunkPos().getWorldPosition()
|
||||
.offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "(" + x + "," + z + ")";
|
||||
}
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.lod;
|
||||
|
||||
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.LodUtil;
|
||||
import com.seibel.lod.core.util.ThreadMapUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version ??
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addVerticalData(long[] data, 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] = data[verticalIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addSingleData(long data, int posX, int posZ)
|
||||
{
|
||||
return addData(data, posX, posZ, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
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];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSingleData(int posX, int posZ)
|
||||
{
|
||||
return getData(posX, posZ, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxVerticalData()
|
||||
{
|
||||
return maxVerticalData;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
long newData;
|
||||
detailLevel = inputData[index];
|
||||
index++;
|
||||
maxVerticalData = inputData[index] & 0b01111111;
|
||||
index++;
|
||||
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
int x = size * size * maxVerticalData;
|
||||
this.dataContainer = new long[x];
|
||||
for (int i = 0; i < x; i++)
|
||||
{
|
||||
newData = 0;
|
||||
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||
newData += (((long) inputData[index + tempIndex]) & 0xff) << (8 * tempIndex);
|
||||
index += 8;
|
||||
dataContainer[i] = newData;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelContainer expand()
|
||||
{
|
||||
return new VerticalLevelContainer((byte) (getDetailLevel() - 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateData(LevelContainer lowerLevelContainer, int posX, int posZ)
|
||||
{
|
||||
//We reset the array
|
||||
long[] dataToMerge = ThreadMapUtil.getVerticalUpdateArray(detailLevel);
|
||||
|
||||
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());
|
||||
|
||||
addVerticalData(data, posX, posZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] toDataString()
|
||||
{
|
||||
int index = 0;
|
||||
int x = size * size;
|
||||
int tempIndex;
|
||||
long current;
|
||||
boolean allGenerated = true;
|
||||
byte[] tempData = ThreadMapUtil.getSaveContainer(detailLevel);
|
||||
|
||||
tempData[index] = detailLevel;
|
||||
index++;
|
||||
tempData[index] = (byte) maxVerticalData;
|
||||
index++;
|
||||
int j;
|
||||
for (int i = 0; i < x; i++)
|
||||
{
|
||||
for (j = 0; j < maxVerticalData; j++)
|
||||
{
|
||||
current = dataContainer[i * maxVerticalData + j];
|
||||
for (tempIndex = 0; tempIndex < 8; tempIndex++)
|
||||
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
|
||||
index += 8;
|
||||
}
|
||||
if(!DataPointUtil.doesItExist(dataContainer[i]))
|
||||
allGenerated = false;
|
||||
}
|
||||
if (allGenerated)
|
||||
tempData[1] |= 0b10000000;
|
||||
return tempData;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unused")
|
||||
public String toString()
|
||||
{
|
||||
/*
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
int size = 1 << (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 " ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxNumberOfLods()
|
||||
{
|
||||
return size * size * getMaxVerticalData();
|
||||
}
|
||||
}
|
||||
@@ -1,556 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.math;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of Minecraft's 1.16.5
|
||||
* implementation of a 4x4 float matrix.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-11-2021
|
||||
*/
|
||||
public class Mat4f
|
||||
{
|
||||
private float m00;
|
||||
private float m01;
|
||||
private float m02;
|
||||
private float m03;
|
||||
private float m10;
|
||||
private float m11;
|
||||
private float m12;
|
||||
private float m13;
|
||||
private float m20;
|
||||
private float m21;
|
||||
private float m22;
|
||||
private float m23;
|
||||
private float m30;
|
||||
private float m31;
|
||||
private float m32;
|
||||
private float m33;
|
||||
|
||||
|
||||
public Mat4f()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Mat4f(Mat4f sourceMatrix)
|
||||
{
|
||||
this.m00 = sourceMatrix.m00;
|
||||
this.m01 = sourceMatrix.m01;
|
||||
this.m02 = sourceMatrix.m02;
|
||||
this.m03 = sourceMatrix.m03;
|
||||
this.m10 = sourceMatrix.m10;
|
||||
this.m11 = sourceMatrix.m11;
|
||||
this.m12 = sourceMatrix.m12;
|
||||
this.m13 = sourceMatrix.m13;
|
||||
this.m20 = sourceMatrix.m20;
|
||||
this.m21 = sourceMatrix.m21;
|
||||
this.m22 = sourceMatrix.m22;
|
||||
this.m23 = sourceMatrix.m23;
|
||||
this.m30 = sourceMatrix.m30;
|
||||
this.m31 = sourceMatrix.m31;
|
||||
this.m32 = sourceMatrix.m32;
|
||||
this.m33 = sourceMatrix.m33;
|
||||
}
|
||||
|
||||
/* Quaternions are not currently needed/implemented
|
||||
public Matrix4float(Quaternion p_i48104_1_)
|
||||
{
|
||||
float f = p_i48104_1_.i();
|
||||
float f1 = p_i48104_1_.j();
|
||||
float f2 = p_i48104_1_.k();
|
||||
float f3 = p_i48104_1_.r();
|
||||
float f4 = 2.0F * f * f;
|
||||
float f5 = 2.0F * f1 * f1;
|
||||
float f6 = 2.0F * f2 * f2;
|
||||
this.m00 = 1.0F - f5 - f6;
|
||||
this.m11 = 1.0F - f6 - f4;
|
||||
this.m22 = 1.0F - f4 - f5;
|
||||
this.m33 = 1.0F;
|
||||
float f7 = f * f1;
|
||||
float f8 = f1 * f2;
|
||||
float f9 = f2 * f;
|
||||
float f10 = f * f3;
|
||||
float f11 = f1 * f3;
|
||||
float f12 = f2 * f3;
|
||||
this.m10 = 2.0F * (f7 + f12);
|
||||
this.m01 = 2.0F * (f7 - f12);
|
||||
this.m20 = 2.0F * (f9 - f11);
|
||||
this.m02 = 2.0F * (f9 + f11);
|
||||
this.m21 = 2.0F * (f8 + f10);
|
||||
this.m12 = 2.0F * (f8 - f10);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj != null && this.getClass() == obj.getClass())
|
||||
{
|
||||
Mat4f otherMatrix = (Mat4f) obj;
|
||||
return Float.compare(otherMatrix.m00, this.m00) == 0
|
||||
&& Float.compare(otherMatrix.m01, this.m01) == 0
|
||||
&& Float.compare(otherMatrix.m02, this.m02) == 0
|
||||
&& Float.compare(otherMatrix.m03, this.m03) == 0
|
||||
&& Float.compare(otherMatrix.m10, this.m10) == 0
|
||||
&& Float.compare(otherMatrix.m11, this.m11) == 0
|
||||
&& Float.compare(otherMatrix.m12, this.m12) == 0
|
||||
&& Float.compare(otherMatrix.m13, this.m13) == 0
|
||||
&& Float.compare(otherMatrix.m20, this.m20) == 0
|
||||
&& Float.compare(otherMatrix.m21, this.m21) == 0
|
||||
&& Float.compare(otherMatrix.m22, this.m22) == 0
|
||||
&& Float.compare(otherMatrix.m23, this.m23) == 0
|
||||
&& Float.compare(otherMatrix.m30, this.m30) == 0
|
||||
&& Float.compare(otherMatrix.m31, this.m31) == 0
|
||||
&& Float.compare(otherMatrix.m32, this.m32) == 0
|
||||
&& Float.compare(otherMatrix.m33, this.m33) == 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0;
|
||||
i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0);
|
||||
i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0);
|
||||
i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0);
|
||||
i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0);
|
||||
i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0);
|
||||
i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0);
|
||||
i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0);
|
||||
i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0);
|
||||
i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0);
|
||||
i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0);
|
||||
i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0);
|
||||
i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0);
|
||||
i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0);
|
||||
i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0);
|
||||
return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder stringbuilder = new StringBuilder();
|
||||
stringbuilder.append("Matrix4f:\n");
|
||||
stringbuilder.append(this.m00);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m01);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m02);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m03);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m10);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m11);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m12);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m13);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m20);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m21);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m22);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m23);
|
||||
stringbuilder.append("\n");
|
||||
stringbuilder.append(this.m30);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m31);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m32);
|
||||
stringbuilder.append(" ");
|
||||
stringbuilder.append(this.m33);
|
||||
stringbuilder.append("\n");
|
||||
return stringbuilder.toString();
|
||||
}
|
||||
|
||||
|
||||
public void store(FloatBuffer floatBuffer)
|
||||
{
|
||||
floatBuffer.put(bufferIndex(0, 0), this.m00);
|
||||
floatBuffer.put(bufferIndex(0, 1), this.m01);
|
||||
floatBuffer.put(bufferIndex(0, 2), this.m02);
|
||||
floatBuffer.put(bufferIndex(0, 3), this.m03);
|
||||
floatBuffer.put(bufferIndex(1, 0), this.m10);
|
||||
floatBuffer.put(bufferIndex(1, 1), this.m11);
|
||||
floatBuffer.put(bufferIndex(1, 2), this.m12);
|
||||
floatBuffer.put(bufferIndex(1, 3), this.m13);
|
||||
floatBuffer.put(bufferIndex(2, 0), this.m20);
|
||||
floatBuffer.put(bufferIndex(2, 1), this.m21);
|
||||
floatBuffer.put(bufferIndex(2, 2), this.m22);
|
||||
floatBuffer.put(bufferIndex(2, 3), this.m23);
|
||||
floatBuffer.put(bufferIndex(3, 0), this.m30);
|
||||
floatBuffer.put(bufferIndex(3, 1), this.m31);
|
||||
floatBuffer.put(bufferIndex(3, 2), this.m32);
|
||||
floatBuffer.put(bufferIndex(3, 3), this.m33);
|
||||
}
|
||||
|
||||
private static int bufferIndex(int xIndex, int zIndex)
|
||||
{
|
||||
return (zIndex * 4) + xIndex;
|
||||
}
|
||||
|
||||
|
||||
public void setIdentity()
|
||||
{
|
||||
this.m00 = 1.0F;
|
||||
this.m01 = 0.0F;
|
||||
this.m02 = 0.0F;
|
||||
this.m03 = 0.0F;
|
||||
this.m10 = 0.0F;
|
||||
this.m11 = 1.0F;
|
||||
this.m12 = 0.0F;
|
||||
this.m13 = 0.0F;
|
||||
this.m20 = 0.0F;
|
||||
this.m21 = 0.0F;
|
||||
this.m22 = 1.0F;
|
||||
this.m23 = 0.0F;
|
||||
this.m30 = 0.0F;
|
||||
this.m31 = 0.0F;
|
||||
this.m32 = 0.0F;
|
||||
this.m33 = 1.0F;
|
||||
}
|
||||
|
||||
/** adjugate and determinate */
|
||||
public float adjugateAndDet()
|
||||
{
|
||||
float f = this.m00 * this.m11 - this.m01 * this.m10;
|
||||
float f1 = this.m00 * this.m12 - this.m02 * this.m10;
|
||||
float f2 = this.m00 * this.m13 - this.m03 * this.m10;
|
||||
float f3 = this.m01 * this.m12 - this.m02 * this.m11;
|
||||
float f4 = this.m01 * this.m13 - this.m03 * this.m11;
|
||||
float f5 = this.m02 * this.m13 - this.m03 * this.m12;
|
||||
float f6 = this.m20 * this.m31 - this.m21 * this.m30;
|
||||
float f7 = this.m20 * this.m32 - this.m22 * this.m30;
|
||||
float f8 = this.m20 * this.m33 - this.m23 * this.m30;
|
||||
float f9 = this.m21 * this.m32 - this.m22 * this.m31;
|
||||
float f10 = this.m21 * this.m33 - this.m23 * this.m31;
|
||||
float f11 = this.m22 * this.m33 - this.m23 * this.m32;
|
||||
float f12 = this.m11 * f11 - this.m12 * f10 + this.m13 * f9;
|
||||
float f13 = -this.m10 * f11 + this.m12 * f8 - this.m13 * f7;
|
||||
float f14 = this.m10 * f10 - this.m11 * f8 + this.m13 * f6;
|
||||
float f15 = -this.m10 * f9 + this.m11 * f7 - this.m12 * f6;
|
||||
float f16 = -this.m01 * f11 + this.m02 * f10 - this.m03 * f9;
|
||||
float f17 = this.m00 * f11 - this.m02 * f8 + this.m03 * f7;
|
||||
float f18 = -this.m00 * f10 + this.m01 * f8 - this.m03 * f6;
|
||||
float f19 = this.m00 * f9 - this.m01 * f7 + this.m02 * f6;
|
||||
float f20 = this.m31 * f5 - this.m32 * f4 + this.m33 * f3;
|
||||
float f21 = -this.m30 * f5 + this.m32 * f2 - this.m33 * f1;
|
||||
float f22 = this.m30 * f4 - this.m31 * f2 + this.m33 * f;
|
||||
float f23 = -this.m30 * f3 + this.m31 * f1 - this.m32 * f;
|
||||
float f24 = -this.m21 * f5 + this.m22 * f4 - this.m23 * f3;
|
||||
float f25 = this.m20 * f5 - this.m22 * f2 + this.m23 * f1;
|
||||
float f26 = -this.m20 * f4 + this.m21 * f2 - this.m23 * f;
|
||||
float f27 = this.m20 * f3 - this.m21 * f1 + this.m22 * f;
|
||||
this.m00 = f12;
|
||||
this.m10 = f13;
|
||||
this.m20 = f14;
|
||||
this.m30 = f15;
|
||||
this.m01 = f16;
|
||||
this.m11 = f17;
|
||||
this.m21 = f18;
|
||||
this.m31 = f19;
|
||||
this.m02 = f20;
|
||||
this.m12 = f21;
|
||||
this.m22 = f22;
|
||||
this.m32 = f23;
|
||||
this.m03 = f24;
|
||||
this.m13 = f25;
|
||||
this.m23 = f26;
|
||||
this.m33 = f27;
|
||||
return f * f11 - f1 * f10 + f2 * f9 + f3 * f8 - f4 * f7 + f5 * f6;
|
||||
}
|
||||
|
||||
public void transpose()
|
||||
{
|
||||
float f = this.m10;
|
||||
this.m10 = this.m01;
|
||||
this.m01 = f;
|
||||
f = this.m20;
|
||||
this.m20 = this.m02;
|
||||
this.m02 = f;
|
||||
f = this.m21;
|
||||
this.m21 = this.m12;
|
||||
this.m12 = f;
|
||||
f = this.m30;
|
||||
this.m30 = this.m03;
|
||||
this.m03 = f;
|
||||
f = this.m31;
|
||||
this.m31 = this.m13;
|
||||
this.m13 = f;
|
||||
f = this.m32;
|
||||
this.m32 = this.m23;
|
||||
this.m23 = f;
|
||||
}
|
||||
|
||||
public boolean invert()
|
||||
{
|
||||
float det = this.adjugateAndDet();
|
||||
if (Math.abs(det) > 1.0E-6F)
|
||||
{
|
||||
this.multiply(det);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void multiply(Mat4f multMatrix)
|
||||
{
|
||||
float f = this.m00 * multMatrix.m00 + this.m01 * multMatrix.m10 + this.m02 * multMatrix.m20 + this.m03 * multMatrix.m30;
|
||||
float f1 = this.m00 * multMatrix.m01 + this.m01 * multMatrix.m11 + this.m02 * multMatrix.m21 + this.m03 * multMatrix.m31;
|
||||
float f2 = this.m00 * multMatrix.m02 + this.m01 * multMatrix.m12 + this.m02 * multMatrix.m22 + this.m03 * multMatrix.m32;
|
||||
float f3 = this.m00 * multMatrix.m03 + this.m01 * multMatrix.m13 + this.m02 * multMatrix.m23 + this.m03 * multMatrix.m33;
|
||||
float f4 = this.m10 * multMatrix.m00 + this.m11 * multMatrix.m10 + this.m12 * multMatrix.m20 + this.m13 * multMatrix.m30;
|
||||
float f5 = this.m10 * multMatrix.m01 + this.m11 * multMatrix.m11 + this.m12 * multMatrix.m21 + this.m13 * multMatrix.m31;
|
||||
float f6 = this.m10 * multMatrix.m02 + this.m11 * multMatrix.m12 + this.m12 * multMatrix.m22 + this.m13 * multMatrix.m32;
|
||||
float f7 = this.m10 * multMatrix.m03 + this.m11 * multMatrix.m13 + this.m12 * multMatrix.m23 + this.m13 * multMatrix.m33;
|
||||
float f8 = this.m20 * multMatrix.m00 + this.m21 * multMatrix.m10 + this.m22 * multMatrix.m20 + this.m23 * multMatrix.m30;
|
||||
float f9 = this.m20 * multMatrix.m01 + this.m21 * multMatrix.m11 + this.m22 * multMatrix.m21 + this.m23 * multMatrix.m31;
|
||||
float f10 = this.m20 * multMatrix.m02 + this.m21 * multMatrix.m12 + this.m22 * multMatrix.m22 + this.m23 * multMatrix.m32;
|
||||
float f11 = this.m20 * multMatrix.m03 + this.m21 * multMatrix.m13 + this.m22 * multMatrix.m23 + this.m23 * multMatrix.m33;
|
||||
float f12 = this.m30 * multMatrix.m00 + this.m31 * multMatrix.m10 + this.m32 * multMatrix.m20 + this.m33 * multMatrix.m30;
|
||||
float f13 = this.m30 * multMatrix.m01 + this.m31 * multMatrix.m11 + this.m32 * multMatrix.m21 + this.m33 * multMatrix.m31;
|
||||
float f14 = this.m30 * multMatrix.m02 + this.m31 * multMatrix.m12 + this.m32 * multMatrix.m22 + this.m33 * multMatrix.m32;
|
||||
float f15 = this.m30 * multMatrix.m03 + this.m31 * multMatrix.m13 + this.m32 * multMatrix.m23 + this.m33 * multMatrix.m33;
|
||||
this.m00 = f;
|
||||
this.m01 = f1;
|
||||
this.m02 = f2;
|
||||
this.m03 = f3;
|
||||
this.m10 = f4;
|
||||
this.m11 = f5;
|
||||
this.m12 = f6;
|
||||
this.m13 = f7;
|
||||
this.m20 = f8;
|
||||
this.m21 = f9;
|
||||
this.m22 = f10;
|
||||
this.m23 = f11;
|
||||
this.m30 = f12;
|
||||
this.m31 = f13;
|
||||
this.m32 = f14;
|
||||
this.m33 = f15;
|
||||
}
|
||||
|
||||
/* Quaternions aren't currently needed/implemented
|
||||
public void multiply(Quaternion p_226596_1_)
|
||||
{
|
||||
this.multiply(new Matrix4f(p_226596_1_));
|
||||
}
|
||||
*/
|
||||
|
||||
public void multiply(float scalar)
|
||||
{
|
||||
this.m00 *= scalar;
|
||||
this.m01 *= scalar;
|
||||
this.m02 *= scalar;
|
||||
this.m03 *= scalar;
|
||||
this.m10 *= scalar;
|
||||
this.m11 *= scalar;
|
||||
this.m12 *= scalar;
|
||||
this.m13 *= scalar;
|
||||
this.m20 *= scalar;
|
||||
this.m21 *= scalar;
|
||||
this.m22 *= scalar;
|
||||
this.m23 *= scalar;
|
||||
this.m30 *= scalar;
|
||||
this.m31 *= scalar;
|
||||
this.m32 *= scalar;
|
||||
this.m33 *= scalar;
|
||||
}
|
||||
|
||||
public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane)
|
||||
{
|
||||
float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D));
|
||||
Mat4f matrix = new Mat4f();
|
||||
matrix.m00 = f / widthHeightRatio;
|
||||
matrix.m11 = f;
|
||||
matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane);
|
||||
matrix.m32 = -1.0F;
|
||||
matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
|
||||
/* not currently needed/implemented
|
||||
* Also the parameter names should be double checked as they may be incorrect
|
||||
public static Matrix4Float orthographic(float left, float right, float top, float bottom)
|
||||
{
|
||||
Matrix4Float matrix4f = new Matrix4Float();
|
||||
matrix4f.m00 = 2.0F / left;
|
||||
matrix4f.m11 = 2.0F / right;
|
||||
float f = bottom - top;
|
||||
matrix4f.m22 = -2.0F / f;
|
||||
matrix4f.m33 = 1.0F;
|
||||
matrix4f.m03 = -1.0F;
|
||||
matrix4f.m13 = -1.0F;
|
||||
matrix4f.m23 = -(bottom + top) / f;
|
||||
return matrix4f;
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* TODO: what kind of translation is this?
|
||||
* and how is this different from "multiplyTranslationMatrix"?
|
||||
*/
|
||||
public void translate(Vec3f vec)
|
||||
{
|
||||
this.m03 += vec.x;
|
||||
this.m13 += vec.y;
|
||||
this.m23 += vec.z;
|
||||
}
|
||||
|
||||
/** originally "translate" from Minecraft's MatrixStack */
|
||||
public void multiplyTranslationMatrix(double x, double y, double z)
|
||||
{
|
||||
multiply(createTranslateMatrix((float)x, (float)y, (float)z));
|
||||
}
|
||||
|
||||
public Mat4f copy()
|
||||
{
|
||||
return new Mat4f(this);
|
||||
}
|
||||
|
||||
public static Mat4f createScaleMatrix(float x, float y, float z)
|
||||
{
|
||||
Mat4f matrix = new Mat4f();
|
||||
matrix.m00 = x;
|
||||
matrix.m11 = y;
|
||||
matrix.m22 = z;
|
||||
matrix.m33 = 1.0F;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
public static Mat4f createTranslateMatrix(float x, float y, float z)
|
||||
{
|
||||
Mat4f matrix = new Mat4f();
|
||||
matrix.m00 = 1.0F;
|
||||
matrix.m11 = 1.0F;
|
||||
matrix.m22 = 1.0F;
|
||||
matrix.m33 = 1.0F;
|
||||
matrix.m03 = x;
|
||||
matrix.m13 = y;
|
||||
matrix.m23 = z;
|
||||
return matrix;
|
||||
}
|
||||
|
||||
|
||||
// Forge start
|
||||
public Mat4f(float[] values)
|
||||
{
|
||||
m00 = values[0];
|
||||
m01 = values[1];
|
||||
m02 = values[2];
|
||||
m03 = values[3];
|
||||
m10 = values[4];
|
||||
m11 = values[5];
|
||||
m12 = values[6];
|
||||
m13 = values[7];
|
||||
m20 = values[8];
|
||||
m21 = values[9];
|
||||
m22 = values[10];
|
||||
m23 = values[11];
|
||||
m30 = values[12];
|
||||
m31 = values[13];
|
||||
m32 = values[14];
|
||||
m33 = values[15];
|
||||
}
|
||||
|
||||
public Mat4f(FloatBuffer buffer)
|
||||
{
|
||||
this(buffer.array());
|
||||
}
|
||||
|
||||
public void set(Mat4f mat)
|
||||
{
|
||||
this.m00 = mat.m00;
|
||||
this.m01 = mat.m01;
|
||||
this.m02 = mat.m02;
|
||||
this.m03 = mat.m03;
|
||||
this.m10 = mat.m10;
|
||||
this.m11 = mat.m11;
|
||||
this.m12 = mat.m12;
|
||||
this.m13 = mat.m13;
|
||||
this.m20 = mat.m20;
|
||||
this.m21 = mat.m21;
|
||||
this.m22 = mat.m22;
|
||||
this.m23 = mat.m23;
|
||||
this.m30 = mat.m30;
|
||||
this.m31 = mat.m31;
|
||||
this.m32 = mat.m32;
|
||||
this.m33 = mat.m33;
|
||||
}
|
||||
|
||||
public void add(Mat4f other)
|
||||
{
|
||||
m00 += other.m00;
|
||||
m01 += other.m01;
|
||||
m02 += other.m02;
|
||||
m03 += other.m03;
|
||||
m10 += other.m10;
|
||||
m11 += other.m11;
|
||||
m12 += other.m12;
|
||||
m13 += other.m13;
|
||||
m20 += other.m20;
|
||||
m21 += other.m21;
|
||||
m22 += other.m22;
|
||||
m23 += other.m23;
|
||||
m30 += other.m30;
|
||||
m31 += other.m31;
|
||||
m32 += other.m32;
|
||||
m33 += other.m33;
|
||||
}
|
||||
|
||||
public void multiplyBackward(Mat4f other)
|
||||
{
|
||||
Mat4f copy = other.copy();
|
||||
copy.multiply(this);
|
||||
this.set(copy);
|
||||
}
|
||||
|
||||
public void setTranslation(float x, float y, float z)
|
||||
{
|
||||
this.m00 = 1.0F;
|
||||
this.m11 = 1.0F;
|
||||
this.m22 = 1.0F;
|
||||
this.m33 = 1.0F;
|
||||
this.m03 = x;
|
||||
this.m13 = y;
|
||||
this.m23 = z;
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.math;
|
||||
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* This is closer to MC's implementation of a
|
||||
* 3 element float vector than a 3 element double
|
||||
* vector. Hopefully that shouldn't cause any issues.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-18-2021
|
||||
*/
|
||||
public class Vec3d
|
||||
{
|
||||
public static Vec3d XNeg = new Vec3d(-1.0F, 0.0F, 0.0F);
|
||||
public static Vec3d XPos = new Vec3d(1.0F, 0.0F, 0.0F);
|
||||
public static Vec3d YNeg = new Vec3d(0.0F, -1.0F, 0.0F);
|
||||
public static Vec3d YPos = new Vec3d(0.0F, 1.0F, 0.0F);
|
||||
public static Vec3d ZNeg = new Vec3d(0.0F, 0.0F, -1.0F);
|
||||
public static Vec3d ZPos = new Vec3d(0.0F, 0.0F, 1.0F);
|
||||
|
||||
public static final Vec3d ZERO_VECTOR = new Vec3d(0.0D, 0.0D, 0.0D);
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
public double z;
|
||||
|
||||
|
||||
|
||||
public Vec3d()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Vec3d(double x, double y, double z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj != null && this.getClass() == obj.getClass())
|
||||
{
|
||||
Vec3d Vec3f = (Vec3d) obj;
|
||||
if (Double.compare(Vec3f.x, this.x) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (Double.compare(Vec3f.y, this.y) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Double.compare(Vec3f.z, this.z) == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
long longVal = Double.doubleToLongBits(this.x);
|
||||
|
||||
int intVal = (int) (longVal ^ longVal >>> 32);
|
||||
longVal = Double.doubleToLongBits(this.y);
|
||||
intVal = 31 * intVal + (int) (longVal ^ longVal >>> 32);
|
||||
longVal = Double.doubleToLongBits(this.z);
|
||||
|
||||
return 31 * intVal + (int) (longVal ^ longVal >>> 32);
|
||||
}
|
||||
|
||||
public void mul(double scalar)
|
||||
{
|
||||
this.x *= scalar;
|
||||
this.y *= scalar;
|
||||
this.z *= scalar;
|
||||
}
|
||||
|
||||
public void mul(double x, double y, double z)
|
||||
{
|
||||
this.x *= x;
|
||||
this.y *= y;
|
||||
this.z *= z;
|
||||
}
|
||||
|
||||
public void clamp(double min, double max)
|
||||
{
|
||||
this.x = LodUtil.clamp(min, this.x, max);
|
||||
this.y = LodUtil.clamp(min, this.y, max);
|
||||
this.z = LodUtil.clamp(min, this.z, max);
|
||||
}
|
||||
|
||||
public void set(double x, double y, double z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void add(double x, double y, double z)
|
||||
{
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.z += z;
|
||||
}
|
||||
|
||||
public void add(Vec3d vector)
|
||||
{
|
||||
this.x += vector.x;
|
||||
this.y += vector.y;
|
||||
this.z += vector.z;
|
||||
}
|
||||
|
||||
public void subtract(Vec3d vector)
|
||||
{
|
||||
this.x -= vector.x;
|
||||
this.y -= vector.y;
|
||||
this.z -= vector.z;
|
||||
}
|
||||
|
||||
public double dotProduct(Vec3d vector)
|
||||
{
|
||||
return this.x * vector.x + this.y * vector.y + this.z * vector.z;
|
||||
}
|
||||
|
||||
public Vec3d normalize()
|
||||
{
|
||||
double value = Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
|
||||
return value < 1.0E-4D ? ZERO_VECTOR : new Vec3d(this.x / value, this.y / value, this.z / value);
|
||||
}
|
||||
|
||||
public void crossProduct(Vec3d vector)
|
||||
{
|
||||
double f = this.x;
|
||||
double f1 = this.y;
|
||||
double f2 = this.z;
|
||||
double f3 = vector.x;
|
||||
double f4 = vector.y;
|
||||
double f5 = vector.z;
|
||||
this.x = f1 * f5 - f2 * f4;
|
||||
this.y = f2 * f3 - f * f5;
|
||||
this.z = f * f4 - f1 * f3;
|
||||
}
|
||||
|
||||
/* Matrix3f is not currently needed/implemented
|
||||
public void transform(Matrix3f p_229188_1_)
|
||||
{
|
||||
double f = this.x;
|
||||
double f1 = this.y;
|
||||
double f2 = this.z;
|
||||
this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2;
|
||||
this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2;
|
||||
this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Quaternions are not currently needed/implemented
|
||||
public void transform(Quaternion p_214905_1_)
|
||||
{
|
||||
Quaternion quaternion = new Quaternion(p_214905_1_);
|
||||
quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F));
|
||||
Quaternion quaternion1 = new Quaternion(p_214905_1_);
|
||||
quaternion1.conj();
|
||||
quaternion.mul(quaternion1);
|
||||
this.set(quaternion.i(), quaternion.j(), quaternion.k());
|
||||
}
|
||||
*/
|
||||
|
||||
/* not currently needed
|
||||
* percent may actually be partial ticks (which is available when rendering)
|
||||
public void linearInterp(Vec3f resultingVector, double percent)
|
||||
{
|
||||
double f = 1.0F - percent;
|
||||
this.x = this.x * f + resultingVector.x * percent;
|
||||
this.y = this.y * f + resultingVector.y * percent;
|
||||
this.z = this.z * f + resultingVector.z * percent;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Quaternions are not currently needed/implemented
|
||||
public Quaternion rotation(double p_229193_1_)
|
||||
{
|
||||
return new Quaternion(this, p_229193_1_, false);
|
||||
}
|
||||
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public Quaternion rotationDegrees(double p_229187_1_)
|
||||
{
|
||||
return new Quaternion(this, p_229187_1_, true);
|
||||
}
|
||||
*/
|
||||
|
||||
public Vec3d copy()
|
||||
{
|
||||
return new Vec3d(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
/* not currently needed/implemented
|
||||
public void map(double2doubleFunction p_229191_1_)
|
||||
{
|
||||
this.x = p_229191_1_.get(this.x);
|
||||
this.y = p_229191_1_.get(this.y);
|
||||
this.z = p_229191_1_.get(this.z);
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
||||
}
|
||||
|
||||
// Forge start
|
||||
public Vec3d(double[] values)
|
||||
{
|
||||
set(values);
|
||||
}
|
||||
|
||||
public void set(double[] values)
|
||||
{
|
||||
this.x = values[0];
|
||||
this.y = values[1];
|
||||
this.z = values[2];
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.math;
|
||||
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of Minecraft's 1.16.5
|
||||
* implementation of a 3 element float vector.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-11-2021
|
||||
*/
|
||||
public class Vec3f
|
||||
{
|
||||
public static Vec3f XNeg = new Vec3f(-1.0F, 0.0F, 0.0F);
|
||||
public static Vec3f XPos = new Vec3f(1.0F, 0.0F, 0.0F);
|
||||
public static Vec3f YNeg = new Vec3f(0.0F, -1.0F, 0.0F);
|
||||
public static Vec3f YPos = new Vec3f(0.0F, 1.0F, 0.0F);
|
||||
public static Vec3f ZNeg = new Vec3f(0.0F, 0.0F, -1.0F);
|
||||
public static Vec3f ZPos = new Vec3f(0.0F, 0.0F, 1.0F);
|
||||
|
||||
|
||||
public float x;
|
||||
public float y;
|
||||
public float z;
|
||||
|
||||
|
||||
|
||||
public Vec3f()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Vec3f(float x, float y, float z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj != null && this.getClass() == obj.getClass())
|
||||
{
|
||||
Vec3f Vec3f = (Vec3f) obj;
|
||||
if (Float.compare(Vec3f.x, this.x) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (Float.compare(Vec3f.y, this.y) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Float.compare(Vec3f.z, this.z) == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int i = Float.floatToIntBits(this.x);
|
||||
i = 31 * i + Float.floatToIntBits(this.y);
|
||||
return 31 * i + Float.floatToIntBits(this.z);
|
||||
}
|
||||
|
||||
public void mul(float scalar)
|
||||
{
|
||||
this.x *= scalar;
|
||||
this.y *= scalar;
|
||||
this.z *= scalar;
|
||||
}
|
||||
|
||||
public void mul(float x, float y, float z)
|
||||
{
|
||||
this.x *= x;
|
||||
this.y *= y;
|
||||
this.z *= z;
|
||||
}
|
||||
|
||||
public void clamp(float min, float max)
|
||||
{
|
||||
this.x = LodUtil.clamp(min, this.x, max);
|
||||
this.y = LodUtil.clamp(min, this.y, max);
|
||||
this.z = LodUtil.clamp(min, this.z, max);
|
||||
}
|
||||
|
||||
public void set(float x, float y, float z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void add(float x, float y, float z)
|
||||
{
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.z += z;
|
||||
}
|
||||
|
||||
public void add(Vec3f vector)
|
||||
{
|
||||
this.x += vector.x;
|
||||
this.y += vector.y;
|
||||
this.z += vector.z;
|
||||
}
|
||||
|
||||
public void subtract(Vec3f vector)
|
||||
{
|
||||
this.x -= vector.x;
|
||||
this.y -= vector.y;
|
||||
this.z -= vector.z;
|
||||
}
|
||||
|
||||
public float dotProduct(Vec3f vector)
|
||||
{
|
||||
return this.x * vector.x + this.y * vector.y + this.z * vector.z;
|
||||
}
|
||||
|
||||
public boolean normalize()
|
||||
{
|
||||
float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z;
|
||||
if (squaredSum < 1.0E-5D)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float f1 = LodUtil.fastInvSqrt(squaredSum);
|
||||
this.x *= f1;
|
||||
this.y *= f1;
|
||||
this.z *= f1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void crossProduct(Vec3f vector)
|
||||
{
|
||||
float f = this.x;
|
||||
float f1 = this.y;
|
||||
float f2 = this.z;
|
||||
float f3 = vector.x;
|
||||
float f4 = vector.y;
|
||||
float f5 = vector.z;
|
||||
this.x = f1 * f5 - f2 * f4;
|
||||
this.y = f2 * f3 - f * f5;
|
||||
this.z = f * f4 - f1 * f3;
|
||||
}
|
||||
|
||||
/* Matrix3f is not currently needed/implemented
|
||||
public void transform(Matrix3f p_229188_1_)
|
||||
{
|
||||
float f = this.x;
|
||||
float f1 = this.y;
|
||||
float f2 = this.z;
|
||||
this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2;
|
||||
this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2;
|
||||
this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Quaternions are not currently needed/implemented
|
||||
public void transform(Quaternion p_214905_1_)
|
||||
{
|
||||
Quaternion quaternion = new Quaternion(p_214905_1_);
|
||||
quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F));
|
||||
Quaternion quaternion1 = new Quaternion(p_214905_1_);
|
||||
quaternion1.conj();
|
||||
quaternion.mul(quaternion1);
|
||||
this.set(quaternion.i(), quaternion.j(), quaternion.k());
|
||||
}
|
||||
*/
|
||||
|
||||
/* not currently needed
|
||||
* percent may actually be partial ticks (which is available when rendering)
|
||||
public void linearInterp(Vec3f resultingVector, float percent)
|
||||
{
|
||||
float f = 1.0F - percent;
|
||||
this.x = this.x * f + resultingVector.x * percent;
|
||||
this.y = this.y * f + resultingVector.y * percent;
|
||||
this.z = this.z * f + resultingVector.z * percent;
|
||||
}
|
||||
*/
|
||||
|
||||
/* Quaternions are not currently needed/implemented
|
||||
public Quaternion rotation(float p_229193_1_)
|
||||
{
|
||||
return new Quaternion(this, p_229193_1_, false);
|
||||
}
|
||||
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public Quaternion rotationDegrees(float p_229187_1_)
|
||||
{
|
||||
return new Quaternion(this, p_229187_1_, true);
|
||||
}
|
||||
*/
|
||||
|
||||
public Vec3f copy()
|
||||
{
|
||||
return new Vec3f(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
/* not currently needed/implemented
|
||||
public void map(Float2FloatFunction p_229191_1_)
|
||||
{
|
||||
this.x = p_229191_1_.get(this.x);
|
||||
this.y = p_229191_1_.get(this.y);
|
||||
this.z = p_229191_1_.get(this.z);
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
||||
}
|
||||
|
||||
// Forge start
|
||||
public Vec3f(float[] values)
|
||||
{
|
||||
set(values);
|
||||
}
|
||||
|
||||
public void set(float[] values)
|
||||
{
|
||||
this.x = values[0];
|
||||
this.y = values[1];
|
||||
this.z = values[2];
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.math;
|
||||
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of Minecraft's 1.16.5
|
||||
* implementation of a 3 element integer vector.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-11-2021
|
||||
*/
|
||||
public class Vec3i
|
||||
{
|
||||
public static Vec3i XNeg = new Vec3i(-1, 0, 0);
|
||||
public static Vec3i XPos = new Vec3i(1, 0, 0);
|
||||
public static Vec3i YNeg = new Vec3i(0, -1, 0);
|
||||
public static Vec3i YPos = new Vec3i(0, 1, 0);
|
||||
public static Vec3i ZNeg = new Vec3i(0, 0, -1);
|
||||
public static Vec3i ZPos = new Vec3i(0, 0, 1);
|
||||
|
||||
|
||||
public int x;
|
||||
public int y;
|
||||
public int z;
|
||||
|
||||
|
||||
|
||||
public Vec3i()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Vec3i(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj != null && this.getClass() == obj.getClass())
|
||||
{
|
||||
Vec3i Vec3f = (Vec3i) obj;
|
||||
if (Float.compare(Vec3f.x, this.x) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (Float.compare(Vec3f.y, this.y) != 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Float.compare(Vec3f.z, this.z) == 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int i = Float.floatToIntBits(this.x);
|
||||
i = 31 * i + Float.floatToIntBits(this.y);
|
||||
return 31 * i + Float.floatToIntBits(this.z);
|
||||
}
|
||||
|
||||
public void mul(float scalar)
|
||||
{
|
||||
this.x *= scalar;
|
||||
this.y *= scalar;
|
||||
this.z *= scalar;
|
||||
}
|
||||
|
||||
public void mul(float x, float y, float z)
|
||||
{
|
||||
this.x *= x;
|
||||
this.y *= y;
|
||||
this.z *= z;
|
||||
}
|
||||
|
||||
public void clamp(int min, int max)
|
||||
{
|
||||
this.x = LodUtil.clamp(min, this.x, max);
|
||||
this.y = LodUtil.clamp(min, this.y, max);
|
||||
this.z = LodUtil.clamp(min, this.z, max);
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void add(int x, int y, int z)
|
||||
{
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.z += z;
|
||||
}
|
||||
|
||||
public void add(Vec3i vector)
|
||||
{
|
||||
this.x += vector.x;
|
||||
this.y += vector.y;
|
||||
this.z += vector.z;
|
||||
}
|
||||
|
||||
public void subtract(Vec3i vector)
|
||||
{
|
||||
this.x -= vector.x;
|
||||
this.y -= vector.y;
|
||||
this.z -= vector.z;
|
||||
}
|
||||
|
||||
public double distSqr(double x, double y, double z, boolean centerOfBlock)
|
||||
{
|
||||
double offset = centerOfBlock ? 0.5 : 0.0;
|
||||
double xAdd = this.x + offset - x;
|
||||
double yAdd = this.y + offset - y;
|
||||
double zAdd = this.z + offset - z;
|
||||
return (xAdd * xAdd) + (yAdd * yAdd) + (zAdd * zAdd);
|
||||
}
|
||||
|
||||
public int distManhattan(Vec3i otherVec)
|
||||
{
|
||||
float xSub = Math.abs(otherVec.x - this.x);
|
||||
float ySub = Math.abs(otherVec.y - this.y);
|
||||
float zSub = Math.abs(otherVec.z - this.z);
|
||||
return (int) (xSub + ySub + zSub);
|
||||
}
|
||||
|
||||
/** inner product */
|
||||
public float dotProduct(Vec3i vector)
|
||||
{
|
||||
return (this.x * vector.x) + (this.y * vector.y) + (this.z * vector.z);
|
||||
}
|
||||
|
||||
/** Cross product */
|
||||
public Vec3i cross(Vec3i otherVec)
|
||||
{
|
||||
return new Vec3i(
|
||||
(this.y * otherVec.z) - (this.z * otherVec.y),
|
||||
(this.z * otherVec.x) - (this.x * otherVec.z),
|
||||
(this.x * otherVec.y) - (this.y * otherVec.x));
|
||||
}
|
||||
|
||||
public Vec3i copy()
|
||||
{
|
||||
return new Vec3i(this.x, this.y, this.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + this.x + ", " + this.y + ", " + this.z + "]";
|
||||
}
|
||||
|
||||
|
||||
// Forge start
|
||||
public Vec3i(int[] values)
|
||||
{
|
||||
set(values);
|
||||
}
|
||||
|
||||
public void set(int[] values)
|
||||
{
|
||||
this.x = values[0];
|
||||
this.y = values[1];
|
||||
this.z = values[2];
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.opengl;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of MC's
|
||||
* DefaultVertexFormats class.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public class DefaultLodVertexFormats
|
||||
{
|
||||
public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 3);
|
||||
public static final LodVertexFormatElement ELEMENT_COLOR = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 4);
|
||||
public static final LodVertexFormatElement ELEMENT_UV = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 2);
|
||||
public static final LodVertexFormatElement ELEMENT_LIGHT_MAP_UV = new LodVertexFormatElement(1, LodVertexFormatElement.DataType.SHORT, 2);
|
||||
public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3);
|
||||
public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1);
|
||||
|
||||
|
||||
public static final LodVertexFormat POSITION = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).build());
|
||||
public static final LodVertexFormat POSITION_COLOR = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).build());
|
||||
public static final LodVertexFormat POSITION_COLOR_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_LIGHT_MAP_UV).build());
|
||||
public static final LodVertexFormat POSITION_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_UV).build());
|
||||
public static final LodVertexFormat POSITION_COLOR_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).build());
|
||||
public static final LodVertexFormat POSITION_COLOR_TEX_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).add(ELEMENT_LIGHT_MAP_UV).build());
|
||||
|
||||
}
|
||||
@@ -1,547 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.opengl;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
/**
|
||||
* A (almost) exact copy of Minecraft's
|
||||
* BufferBuilder object. <br>
|
||||
* Which allows for creating and filling
|
||||
* OpenGL buffers.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public class LodBufferBuilder
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
public ByteBuffer buffer;
|
||||
|
||||
private final List<LodBufferBuilder.DrawState> vertexCounts = Lists.newArrayList();
|
||||
private int lastRenderedCountIndex = 0;
|
||||
private int totalRenderedBytes = 0;
|
||||
private int nextElementByte = 0;
|
||||
private int totalUploadedBytes = 0;
|
||||
private int vertices;
|
||||
@Nullable
|
||||
private LodVertexFormatElement currentElement;
|
||||
private int elementIndex;
|
||||
private int mode;
|
||||
private LodVertexFormat format;
|
||||
private boolean building;
|
||||
|
||||
|
||||
|
||||
|
||||
public LodBufferBuilder(int bufferSizeInBytes)
|
||||
{
|
||||
this.buffer = allocateByteBuffer(bufferSizeInBytes * 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** originally from MC's GLAllocation class */
|
||||
private ByteBuffer allocateByteBuffer(int bufferSizeInBytes)
|
||||
{
|
||||
return ByteBuffer.allocateDirect(bufferSizeInBytes).order(ByteOrder.nativeOrder());
|
||||
}
|
||||
/** originally from MC's GLAllocation class */
|
||||
@SuppressWarnings("unused")
|
||||
private FloatBuffer allocateFloatBuffer(int bufferSizeInBytes)
|
||||
{
|
||||
return allocateByteBuffer(bufferSizeInBytes).asFloatBuffer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** make sure the buffer doesn't overflow when inserting new elements */
|
||||
private void ensureVertexCapacity()
|
||||
{
|
||||
this.ensureCapacity(this.format.getVertexSize());
|
||||
}
|
||||
private void ensureCapacity(int vertexSizeInBytes)
|
||||
{
|
||||
if (this.nextElementByte + vertexSizeInBytes > this.buffer.capacity())
|
||||
{
|
||||
int i = this.buffer.capacity();
|
||||
int j = i + roundUp(vertexSizeInBytes);
|
||||
//LOGGER.debug("Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.", i, j);
|
||||
ByteBuffer bytebuffer = allocateByteBuffer(j);
|
||||
((Buffer) this.buffer).position(0);
|
||||
bytebuffer.put(this.buffer);
|
||||
((Buffer) bytebuffer).rewind();
|
||||
this.buffer = bytebuffer;
|
||||
}
|
||||
}
|
||||
private static int roundUp(int vertexSizeInBytes)
|
||||
{
|
||||
int i = 2097152; // 2 ^ 21
|
||||
if (vertexSizeInBytes == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (vertexSizeInBytes < 0)
|
||||
{
|
||||
i *= -1;
|
||||
}
|
||||
|
||||
int j = vertexSizeInBytes % i;
|
||||
return j == 0 ? vertexSizeInBytes : vertexSizeInBytes + i - j;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* not currently needed sortQuads()
|
||||
// the x,y,z location is a blockPos
|
||||
public void sortQuads(float x, float y, float z)
|
||||
{
|
||||
((Buffer) this.buffer).clear();
|
||||
FloatBuffer floatbuffer = this.buffer.asFloatBuffer();
|
||||
int i = this.vertices / 4;
|
||||
float[] afloat = new float[i];
|
||||
|
||||
for (int j = 0; j < i; ++j)
|
||||
{
|
||||
afloat[j] = getQuadDistanceFromPlayer(floatbuffer, x, y, z, this.format.getIntegerSize(), this.totalRenderedBytes / 4 + j * this.format.getVertexSize());
|
||||
}
|
||||
|
||||
int[] aint = new int[i];
|
||||
|
||||
for (int k = 0; k < aint.length; aint[k] = k++)
|
||||
{
|
||||
}
|
||||
|
||||
IntArrays.mergeSort(aint, (p_227830_1_, p_227830_2_) ->
|
||||
{
|
||||
return Floats.compare(afloat[p_227830_2_], afloat[p_227830_1_]);
|
||||
});
|
||||
BitSet bitset = new BitSet();
|
||||
FloatBuffer floatbuffer1 = allocateFloatBuffer(this.format.getIntegerSize() * 4);
|
||||
|
||||
for (int l = bitset.nextClearBit(0); l < aint.length; l = bitset.nextClearBit(l + 1))
|
||||
{
|
||||
int i1 = aint[l];
|
||||
if (i1 != l)
|
||||
{
|
||||
this.limitToVertex(floatbuffer, i1);
|
||||
((Buffer) floatbuffer1).clear();
|
||||
floatbuffer1.put(floatbuffer);
|
||||
int j1 = i1;
|
||||
|
||||
for (int k1 = aint[i1]; j1 != l; k1 = aint[k1])
|
||||
{
|
||||
this.limitToVertex(floatbuffer, k1);
|
||||
FloatBuffer floatbuffer2 = floatbuffer.slice();
|
||||
this.limitToVertex(floatbuffer, j1);
|
||||
floatbuffer.put(floatbuffer2);
|
||||
bitset.set(j1);
|
||||
j1 = k1;
|
||||
}
|
||||
|
||||
this.limitToVertex(floatbuffer, l);
|
||||
((Buffer) floatbuffer1).flip();
|
||||
floatbuffer.put(floatbuffer1);
|
||||
}
|
||||
|
||||
bitset.set(l);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void limitToVertex(FloatBuffer p_227829_1_, int p_227829_2_)
|
||||
{
|
||||
int i = this.format.getIntegerSize() * 4;
|
||||
((Buffer) p_227829_1_).limit(this.totalRenderedBytes / 4 + (p_227829_2_ + 1) * i);
|
||||
((Buffer) p_227829_1_).position(this.totalRenderedBytes / 4 + p_227829_2_ * i);
|
||||
}
|
||||
*/
|
||||
|
||||
/* not curerntly needed getState()
|
||||
public LodBufferBuilder.State getState()
|
||||
{
|
||||
((Buffer) this.buffer).limit(this.nextElementByte);
|
||||
((Buffer) this.buffer).position(this.totalRenderedBytes);
|
||||
ByteBuffer bytebuffer = ByteBuffer.allocate(this.vertices * this.format.getVertexSize());
|
||||
bytebuffer.put(this.buffer);
|
||||
((Buffer) this.buffer).clear();
|
||||
return new LodBufferBuilder.State(bytebuffer, this.format);
|
||||
}
|
||||
*/
|
||||
|
||||
/* not currently needed getQuadDistanceFromPlayer()
|
||||
private static float getQuadDistanceFromPlayer(FloatBuffer floatBuffer, float x, float y, float z, int p_181665_4_, int p_181665_5_)
|
||||
{
|
||||
float f = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 0);
|
||||
float f1 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 1);
|
||||
float f2 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 0 + 2);
|
||||
float f3 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 0);
|
||||
float f4 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 1);
|
||||
float f5 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 1 + 2);
|
||||
float f6 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 0);
|
||||
float f7 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 1);
|
||||
float f8 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 2 + 2);
|
||||
float f9 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 0);
|
||||
float f10 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 1);
|
||||
float f11 = floatBuffer.get(p_181665_5_ + p_181665_4_ * 3 + 2);
|
||||
float f12 = (f + f3 + f6 + f9) * 0.25F - x;
|
||||
float f13 = (f1 + f4 + f7 + f10) * 0.25F - y;
|
||||
float f14 = (f2 + f5 + f8 + f11) * 0.25F - z;
|
||||
return f12 * f12 + f13 * f13 + f14 * f14;
|
||||
}
|
||||
*/
|
||||
|
||||
/* not currently needed restoreState()
|
||||
public void restoreState(LodBufferBuilder.State bufferState)
|
||||
{
|
||||
((Buffer) bufferState.data).clear();
|
||||
int i = bufferState.data.capacity();
|
||||
this.ensureCapacity(i);
|
||||
((Buffer) this.buffer).limit(this.buffer.capacity());
|
||||
((Buffer) this.buffer).position(this.totalRenderedBytes);
|
||||
this.buffer.put(bufferState.data);
|
||||
((Buffer) this.buffer).clear();
|
||||
LodVertexFormat LodVertexFormat = bufferState.format;
|
||||
this.switchFormat(LodVertexFormat);
|
||||
this.vertices = i / LodVertexFormat.getVertexSize();
|
||||
this.nextElementByte = this.totalRenderedBytes + this.vertices * LodVertexFormat.getVertexSize();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void switchFormat(LodVertexFormat newFormat)
|
||||
{
|
||||
format = newFormat;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========================================//
|
||||
// methods for actually building a buffer //
|
||||
//========================================//
|
||||
|
||||
/**
|
||||
* @param openGlLodVertexFormat GL11.GL_QUADS, GL11.GL_TRIANGLES, etc.
|
||||
* @param LodVertexFormat
|
||||
*/
|
||||
public void begin(int openGlLodVertexFormat, LodVertexFormat LodVertexFormat)
|
||||
{
|
||||
if (this.building)
|
||||
{
|
||||
throw new IllegalStateException("Already building!");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.building = true;
|
||||
this.mode = openGlLodVertexFormat;
|
||||
this.switchFormat(LodVertexFormat);
|
||||
this.currentElement = LodVertexFormat.getElements().get(0);
|
||||
this.elementIndex = 0;
|
||||
((Buffer) this.buffer).clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void end()
|
||||
{
|
||||
if (!this.building)
|
||||
{
|
||||
throw new IllegalStateException("Not building!");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.building = false;
|
||||
this.vertexCounts.add(new LodBufferBuilder.DrawState(this.format, this.vertices, this.mode));
|
||||
this.totalRenderedBytes += this.vertices * this.format.getVertexSize();
|
||||
this.vertices = 0;
|
||||
this.currentElement = null;
|
||||
this.elementIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void putByte(int index, byte newByte)
|
||||
{
|
||||
this.buffer.put(this.nextElementByte + index, newByte);
|
||||
}
|
||||
|
||||
public void putShort(int index, short newShort)
|
||||
{
|
||||
this.buffer.putShort(this.nextElementByte + index, newShort);
|
||||
}
|
||||
|
||||
public void putFloat(int index, float newFloat)
|
||||
{
|
||||
this.buffer.putFloat(this.nextElementByte + index, newFloat);
|
||||
}
|
||||
|
||||
public void endVertex()
|
||||
{
|
||||
if (this.elementIndex != 0)
|
||||
{
|
||||
throw new IllegalStateException("Not filled all elements of the vertex");
|
||||
}
|
||||
else
|
||||
{
|
||||
++this.vertices;
|
||||
this.ensureVertexCapacity();
|
||||
}
|
||||
}
|
||||
|
||||
public void nextElement()
|
||||
{
|
||||
ImmutableList<LodVertexFormatElement> immutablelist = this.format.getElements();
|
||||
this.elementIndex = (this.elementIndex + 1) % immutablelist.size();
|
||||
this.nextElementByte += this.currentElement.getByteSize();
|
||||
LodVertexFormatElement LodVertexFormatelement = immutablelist.get(this.elementIndex);
|
||||
this.currentElement = LodVertexFormatelement;
|
||||
// if (LodVertexFormatelement.getUsage() == LodVertexFormatElement.Usage.PADDING)
|
||||
// {
|
||||
// this.nextElement();
|
||||
// }
|
||||
|
||||
// if (this.defaultColorSet && this.currentElement.getUsage() == LodVertexFormatElement.Usage.COLOR)
|
||||
// {
|
||||
// color(this.defaultR, this.defaultG, this.defaultB, this.defaultA);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
public LodBufferBuilder color(int red, int green, int blue, int alpha)
|
||||
{
|
||||
LodVertexFormatElement LodVertexFormatelement = this.currentElement();
|
||||
if (LodVertexFormatelement.getType() != LodVertexFormatElement.DataType.UBYTE)
|
||||
{
|
||||
throw new IllegalStateException("Color must be stored as a UBYTE");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.putByte(0, (byte) red);
|
||||
this.putByte(1, (byte) green);
|
||||
this.putByte(2, (byte) blue);
|
||||
this.putByte(3, (byte) alpha);
|
||||
this.nextElement();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public LodBufferBuilder vertex(float x, float y, float z)
|
||||
{
|
||||
if (this.currentElement().getType() != LodVertexFormatElement.DataType.FLOAT)
|
||||
{
|
||||
throw new IllegalStateException("Position verticies must be stored as a FLOAT");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.putFloat(0, x);
|
||||
this.putFloat(4, y);
|
||||
this.putFloat(8, z);
|
||||
this.nextElement();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* not currently needed fullVertex()
|
||||
* TODO James isn't sure about these names
|
||||
public void vertex(float blockPosX, float blockPosY, float blockPosZ,
|
||||
float red, float green, float blue, float alpha,
|
||||
float textureU, float textureV,
|
||||
int p_225588_10_, int p_225588_11_,
|
||||
float p_225588_12_, float p_225588_13_, float p_225588_14_)
|
||||
{
|
||||
if (this.defaultColorSet)
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
else if (this.fastFormat)
|
||||
{
|
||||
this.putFloat(0, blockPosX);
|
||||
this.putFloat(4, blockPosY);
|
||||
this.putFloat(8, blockPosZ);
|
||||
this.putByte(12, (byte) ((int) (red * 255.0F)));
|
||||
this.putByte(13, (byte) ((int) (green * 255.0F)));
|
||||
this.putByte(14, (byte) ((int) (blue * 255.0F)));
|
||||
this.putByte(15, (byte) ((int) (alpha * 255.0F)));
|
||||
this.putFloat(16, textureU);
|
||||
this.putFloat(20, textureV);
|
||||
int i;
|
||||
if (this.fullFormat)
|
||||
{
|
||||
this.putShort(24, (short) (p_225588_10_ & '\uffff'));
|
||||
this.putShort(26, (short) (p_225588_10_ >> 16 & '\uffff'));
|
||||
i = 28;
|
||||
}
|
||||
else
|
||||
{
|
||||
i = 24;
|
||||
}
|
||||
|
||||
this.putShort(i + 0, (short) (p_225588_11_ & '\uffff'));
|
||||
this.putShort(i + 2, (short) (p_225588_11_ >> 16 & '\uffff'));
|
||||
this.putByte(i + 4, IVertexConsumer.normalIntValue(p_225588_12_));
|
||||
this.putByte(i + 5, IVertexConsumer.normalIntValue(p_225588_13_));
|
||||
this.putByte(i + 6, IVertexConsumer.normalIntValue(p_225588_14_));
|
||||
this.nextElementByte += i + 8;
|
||||
this.endVertex();
|
||||
}
|
||||
else
|
||||
{
|
||||
super.vertex(blockPosX, blockPosY, blockPosZ, red, green, blue, alpha, textureU, textureV, p_225588_10_, p_225588_11_, p_225588_12_, p_225588_13_, p_225588_14_);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* James isn't sure what the difference between
|
||||
* using this method and just directly getting the buffer would be.
|
||||
* But this was what was being used before, so it will stay for now.
|
||||
*
|
||||
* If anyone figures out what is special about this, please replace this comment.
|
||||
*/
|
||||
public ByteBuffer getCleanedByteBuffer()
|
||||
{
|
||||
LodBufferBuilder.DrawState bufferbuilder$drawstate = this.vertexCounts.get(this.lastRenderedCountIndex++);
|
||||
((Buffer) this.buffer).position(this.totalUploadedBytes);
|
||||
this.totalUploadedBytes += bufferbuilder$drawstate.vertexCount() * bufferbuilder$drawstate.format().getVertexSize();
|
||||
((Buffer) this.buffer).limit(this.totalUploadedBytes);
|
||||
if (this.lastRenderedCountIndex == this.vertexCounts.size() && this.vertices == 0)
|
||||
{
|
||||
this.clear();
|
||||
}
|
||||
|
||||
ByteBuffer bytebuffer = this.buffer.slice();
|
||||
bytebuffer.order(this.buffer.order()); // FORGE: Fix incorrect byte order
|
||||
((Buffer) this.buffer).clear();
|
||||
return bytebuffer; // the original method also returned bufferbuilder$drawstate
|
||||
}
|
||||
|
||||
|
||||
public void clear()
|
||||
{
|
||||
if (this.totalRenderedBytes != this.totalUploadedBytes)
|
||||
{
|
||||
LOGGER.warn("Bytes mismatch " + this.totalRenderedBytes + " " + this.totalUploadedBytes);
|
||||
}
|
||||
|
||||
this.discard();
|
||||
}
|
||||
|
||||
public void discard()
|
||||
{
|
||||
this.totalRenderedBytes = 0;
|
||||
this.totalUploadedBytes = 0;
|
||||
this.nextElementByte = 0;
|
||||
this.vertexCounts.clear();
|
||||
this.lastRenderedCountIndex = 0;
|
||||
}
|
||||
|
||||
public LodVertexFormatElement currentElement()
|
||||
{
|
||||
if (this.currentElement == null)
|
||||
{
|
||||
throw new IllegalStateException("BufferBuilder not started");
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.currentElement;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean building()
|
||||
{
|
||||
return this.building;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// internal classes //
|
||||
//==================//
|
||||
|
||||
|
||||
public static final class DrawState
|
||||
{
|
||||
private final LodVertexFormat format;
|
||||
private final int vertexCount;
|
||||
private final int mode;
|
||||
|
||||
private DrawState(LodVertexFormat p_i225905_1_, int p_i225905_2_, int p_i225905_3_)
|
||||
{
|
||||
this.format = p_i225905_1_;
|
||||
this.vertexCount = p_i225905_2_;
|
||||
this.mode = p_i225905_3_;
|
||||
}
|
||||
|
||||
public LodVertexFormat format()
|
||||
{
|
||||
return this.format;
|
||||
}
|
||||
|
||||
public int vertexCount()
|
||||
{
|
||||
return this.vertexCount;
|
||||
}
|
||||
|
||||
public int mode()
|
||||
{
|
||||
return this.mode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Forge added methods
|
||||
public void putBulkData(ByteBuffer buffer)
|
||||
{
|
||||
ensureCapacity(buffer.limit() + this.format.getVertexSize());
|
||||
((Buffer) this.buffer).position(this.vertices * this.format.getVertexSize());
|
||||
this.buffer.put(buffer);
|
||||
this.vertices += buffer.limit() / this.format.getVertexSize();
|
||||
this.nextElementByte += buffer.limit();
|
||||
}
|
||||
|
||||
public LodVertexFormat getLodVertexFormat()
|
||||
{
|
||||
return this.format;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.opengl;
|
||||
|
||||
import org.lwjgl.opengl.GL15;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.GLProxyContext;
|
||||
import com.seibel.lod.core.render.GLProxy;
|
||||
|
||||
/**
|
||||
* This is a container for a OpenGL
|
||||
* VBO (Vertex Buffer Object).
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public class LodVertexBuffer implements AutoCloseable
|
||||
{
|
||||
public int id;
|
||||
public int vertexCount;
|
||||
|
||||
public LodVertexBuffer()
|
||||
{
|
||||
if (GLProxy.getInstance().getGlContext() == GLProxyContext.NONE)
|
||||
throw new IllegalStateException("Thread [" +Thread.currentThread().getName() + "] tried to create a [" + LodVertexBuffer.class.getSimpleName() + "] outside a OpenGL contex.");
|
||||
|
||||
this.id = GL15.glGenBuffers();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
if (this.id >= 0)
|
||||
{
|
||||
GLProxy.getInstance().recordOpenGlCall(() -> GL15.glDeleteBuffers(this.id));
|
||||
this.id = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.opengl;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
/**
|
||||
* This is used to represent a single vertex
|
||||
* stored in GPU memory,
|
||||
* <p>
|
||||
* A (almost) exact copy of Minecraft's
|
||||
* VertexFormat class, several methods
|
||||
* were commented out since we didn't need them.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public class LodVertexFormat
|
||||
{
|
||||
private final ImmutableList<LodVertexFormatElement> elements;
|
||||
private final IntList offsets = new IntArrayList();
|
||||
private final int vertexSize;
|
||||
|
||||
public LodVertexFormat(ImmutableList<LodVertexFormatElement> elementList)
|
||||
{
|
||||
this.elements = elementList;
|
||||
int i = 0;
|
||||
|
||||
for (LodVertexFormatElement LodVertexFormatElement : elementList)
|
||||
{
|
||||
this.offsets.add(i);
|
||||
i += LodVertexFormatElement.getByteSize();
|
||||
}
|
||||
|
||||
this.vertexSize = i;
|
||||
}
|
||||
|
||||
public int getIntegerSize()
|
||||
{
|
||||
return this.getVertexSize() / 4;
|
||||
}
|
||||
|
||||
public int getVertexSize()
|
||||
{
|
||||
return this.vertexSize;
|
||||
}
|
||||
|
||||
public ImmutableList<LodVertexFormatElement> getElements()
|
||||
{
|
||||
return this.elements;
|
||||
}
|
||||
|
||||
|
||||
// Forge added method
|
||||
public int getOffset(int index)
|
||||
{
|
||||
return offsets.getInt(index);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "format: " + this.elements.size() + " elements: " + this.elements.stream().map(Object::toString).collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj != null && this.getClass() == obj.getClass())
|
||||
{
|
||||
LodVertexFormat vertexformat = (LodVertexFormat) obj;
|
||||
return this.vertexSize != vertexformat.vertexSize ? false : this.elements.equals(vertexformat.elements);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return this.elements.hashCode();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* not currently needed setupBufferState()
|
||||
public void setupBufferState(long p_227892_1_)
|
||||
{
|
||||
if (!RenderSystem.isOnRenderThread())
|
||||
{
|
||||
RenderSystem.recordRenderCall(() ->
|
||||
{
|
||||
this.setupBufferState(p_227892_1_);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = this.getVertexSize();
|
||||
List<LodVertexFormatElement> list = this.getElements();
|
||||
|
||||
for (int j = 0; j < list.size(); ++j)
|
||||
{
|
||||
list.get(j).setupBufferState(p_227892_1_ + this.offsets.getInt(j), i);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/* not currently needed clearBufferState()
|
||||
public void clearBufferState()
|
||||
{
|
||||
if (!RenderSystem.isOnRenderThread())
|
||||
{
|
||||
RenderSystem.recordRenderCall(this::clearBufferState);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (LodVertexFormatElement LodVertexFormatElement : this.getElements())
|
||||
{
|
||||
LodVertexFormatElement.clearBufferState();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* not currently needed has-Position/Normal/Color/UV
|
||||
public boolean hasPosition()
|
||||
{
|
||||
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.POSITION);
|
||||
}
|
||||
|
||||
public boolean hasNormal()
|
||||
{
|
||||
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.NORMAL);
|
||||
}
|
||||
|
||||
public boolean hasColor()
|
||||
{
|
||||
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.COLOR);
|
||||
}
|
||||
|
||||
public boolean hasUV(int which)
|
||||
{
|
||||
return elements.stream().anyMatch(e -> e.getUsage() == LodVertexFormatElement.Usage.UV && e.getIndex() == which);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.opengl;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
/**
|
||||
* This object is used to build LodVertexFormats.
|
||||
* <p>
|
||||
* A (almost) exact copy of Minecraft's
|
||||
* VertexFormatElement class. <br>
|
||||
* A number of things were removed from the original
|
||||
* object since we didn't need them, specifically "usage".
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public class LodVertexFormatElement
|
||||
{
|
||||
private final LodVertexFormatElement.DataType dataType;
|
||||
/** James isn't sure what index is for */
|
||||
private final int index;
|
||||
private final int count;
|
||||
private final int byteSize;
|
||||
|
||||
public LodVertexFormatElement(int newIndex, LodVertexFormatElement.DataType newType, int newCount)
|
||||
{
|
||||
this.dataType = newType;
|
||||
this.index = newIndex;
|
||||
this.count = newCount;
|
||||
this.byteSize = newType.getSize() * this.count;
|
||||
}
|
||||
|
||||
public final LodVertexFormatElement.DataType getType()
|
||||
{
|
||||
return this.dataType;
|
||||
}
|
||||
|
||||
public final int getIndex()
|
||||
{
|
||||
return this.index;
|
||||
}
|
||||
|
||||
public final int getByteSize()
|
||||
{
|
||||
return this.byteSize;
|
||||
}
|
||||
|
||||
// added by Forge
|
||||
public int getElementCount()
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static enum DataType
|
||||
{
|
||||
FLOAT(4, "Float", GL11.GL_FLOAT),
|
||||
UBYTE(1, "Unsigned Byte", GL11.GL_UNSIGNED_BYTE),
|
||||
BYTE(1, "Byte", GL11.GL_BYTE),
|
||||
USHORT(2, "Unsigned Short", GL11.GL_UNSIGNED_SHORT),
|
||||
SHORT(2, "Short", GL11.GL_SHORT),
|
||||
UINT(4, "Unsigned Int", GL11.GL_UNSIGNED_INT),
|
||||
INT(4, "Int", GL11.GL_INT);
|
||||
|
||||
private final int size;
|
||||
private final String name;
|
||||
private final int glType;
|
||||
|
||||
private DataType(int sizeInBytes, String newName, int openGlDataType)
|
||||
{
|
||||
this.size = sizeInBytes;
|
||||
this.name = newName;
|
||||
this.glType = openGlDataType;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getGlType()
|
||||
{
|
||||
return this.glType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
int i = this.dataType.hashCode();
|
||||
i = 31 * i + this.index;
|
||||
return 31 * i + this.count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.count + "," + this.dataType.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (obj != null && this.getClass() == obj.getClass())
|
||||
{
|
||||
LodVertexFormatElement LodVertexFormatElement = (LodVertexFormatElement) obj;
|
||||
if (this.count != LodVertexFormatElement.count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.index != LodVertexFormatElement.index)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (this.dataType != LodVertexFormatElement.dataType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.objects.rending;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
|
||||
/**
|
||||
* This object is just a replacement for an array
|
||||
* to make things easier to understand in the LodRenderer.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 7-03-2021
|
||||
*/
|
||||
public class NearFarFogSettings
|
||||
{
|
||||
public final NearOrFarSetting near = new NearOrFarSetting(FogDistance.NEAR);
|
||||
public final NearOrFarSetting far = new NearOrFarSetting(FogDistance.FAR);
|
||||
|
||||
/**
|
||||
* If true that means Minecraft is
|
||||
* rendering fog
|
||||
*/
|
||||
public boolean vanillaIsRenderingFog = true;
|
||||
|
||||
public NearFarFogSettings()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This holds all relevant data to rendering fog at either
|
||||
* near or far distances.
|
||||
*/
|
||||
public static class NearOrFarSetting
|
||||
{
|
||||
public FogQuality quality = FogQuality.FANCY;
|
||||
public FogDistance distance;
|
||||
|
||||
public NearOrFarSetting(FogDistance newFogDistance)
|
||||
{
|
||||
distance = newFogDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.render;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.GL;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.GLCapabilities;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
import com.seibel.lod.core.enums.rendering.GLProxyContext;
|
||||
import com.seibel.lod.core.render.shader.LodShader;
|
||||
import com.seibel.lod.core.render.shader.LodShaderProgram;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
* A singleton that holds references to different openGL contexts
|
||||
* and GPU capabilities.
|
||||
*
|
||||
* <p>
|
||||
* Helpful OpenGL resources: <br><br>
|
||||
* <p>
|
||||
* 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 11-21-2021
|
||||
*/
|
||||
public class GLProxy
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
|
||||
private static ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build());
|
||||
|
||||
|
||||
private static GLProxy instance = null;
|
||||
|
||||
/** Minecraft's GLFW window */
|
||||
public final long minecraftGlContext;
|
||||
/** Minecraft's GL capabilities */
|
||||
public final GLCapabilities minecraftGlCapabilities;
|
||||
|
||||
/** the LodBuilder's GLFW window */
|
||||
public final long lodBuilderGlContext;
|
||||
/** the LodBuilder's GL capabilities */
|
||||
public final GLCapabilities lodBuilderGlCapabilities;
|
||||
|
||||
/** the proxyWorker's GLFW window */
|
||||
public final long proxyWorkerGlContext;
|
||||
/** the proxyWorker's GL capabilities */
|
||||
public final GLCapabilities proxyWorkerGlCapabilities;
|
||||
|
||||
|
||||
|
||||
/** This program contains all shaders required when rendering LODs */
|
||||
public LodShaderProgram lodShaderProgram;
|
||||
/** This is the VAO that is used when rendering */
|
||||
public final int vertexArrayObjectId;
|
||||
|
||||
|
||||
/** Requires OpenGL 4.5, and offers the best buffer uploading */
|
||||
public final boolean bufferStorageSupported;
|
||||
|
||||
/** Requires OpenGL 3.0 */
|
||||
public final boolean mapBufferRangeSupported;
|
||||
|
||||
|
||||
|
||||
|
||||
private GLProxy()
|
||||
{
|
||||
ClientApi.LOGGER.error("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
|
||||
|
||||
// getting Minecraft's context has to be done on the render thread,
|
||||
// where the GL context is
|
||||
if (GLFW.glfwGetCurrentContext() == 0L)
|
||||
throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!");
|
||||
|
||||
|
||||
|
||||
//============================//
|
||||
// create the builder context //
|
||||
//============================//
|
||||
|
||||
// get Minecraft's context
|
||||
minecraftGlContext = GLFW.glfwGetCurrentContext();
|
||||
minecraftGlCapabilities = GL.getCapabilities();
|
||||
|
||||
|
||||
// context creation setup
|
||||
GLFW.glfwDefaultWindowHints();
|
||||
// make the context window invisible
|
||||
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, GLFW.GLFW_FALSE);
|
||||
// by default the context should get the highest available OpenGL version
|
||||
// but this can be explicitly set for testing
|
||||
// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
// GLFW.glfwWindowHint(GLFW.GLFW_CONTEXT_VERSION_MINOR, 5);
|
||||
|
||||
|
||||
// create the LodBuilder context
|
||||
lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext);
|
||||
GLFW.glfwMakeContextCurrent(lodBuilderGlContext);
|
||||
lodBuilderGlCapabilities = GL.createCapabilities();
|
||||
|
||||
|
||||
// create the proxyWorker's context
|
||||
proxyWorkerGlContext = GLFW.glfwCreateWindow(64, 48, "LOD proxy worker Window", 0L, minecraftGlContext);
|
||||
GLFW.glfwMakeContextCurrent(proxyWorkerGlContext);
|
||||
proxyWorkerGlCapabilities = GL.createCapabilities();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==================================//
|
||||
// get any GPU related capabilities //
|
||||
//==================================//
|
||||
|
||||
setGlContext(GLProxyContext.LOD_BUILDER);
|
||||
|
||||
ClientApi.LOGGER.info("Lod Render OpenGL version [" + GL11.glGetString(GL11.GL_VERSION) + "].");
|
||||
|
||||
// crash the game if the GPU doesn't support OpenGL 2.0
|
||||
if (!minecraftGlCapabilities.OpenGL20)
|
||||
{
|
||||
// Note: as of MC 1.17 this shouldn't happen since MC
|
||||
// requires OpenGL 3.3, but just in case.
|
||||
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GLProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 2.0 or greater.";
|
||||
MC.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 2.0 or greater."));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// get specific capabilities
|
||||
// TODO re-add buffer storage support
|
||||
bufferStorageSupported = lodBuilderGlCapabilities.glBufferStorage != 0;
|
||||
mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0;
|
||||
|
||||
|
||||
// display the capabilities
|
||||
if (!bufferStorageSupported)
|
||||
{
|
||||
String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5";
|
||||
ClientApi.LOGGER.error("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// shader setup //
|
||||
//==============//
|
||||
|
||||
//setGlContext(GLProxyContext.LOD_RENDER);
|
||||
setGlContext(GLProxyContext.MINECRAFT);
|
||||
|
||||
createShaderProgram();
|
||||
|
||||
// Note: VAO objects can not be shared between contexts,
|
||||
// this must be created on minecraft's render context to work correctly
|
||||
vertexArrayObjectId = GL30.glGenVertexArrays();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//==========//
|
||||
// clean up //
|
||||
//==========//
|
||||
|
||||
// Since this is created on the render thread, make sure the Minecraft context is used in the end
|
||||
setGlContext(GLProxyContext.MINECRAFT);
|
||||
|
||||
|
||||
// GLProxy creation success
|
||||
ClientApi.LOGGER.error(GLProxy.class.getSimpleName() + " creation successful. OpenGL smiles upon you this day.");
|
||||
}
|
||||
|
||||
/** Creates all required shaders */
|
||||
public void createShaderProgram()
|
||||
{
|
||||
LodShader vertexShader = null;
|
||||
LodShader fragmentShader = null;
|
||||
|
||||
try
|
||||
{
|
||||
// get the shaders from the resource folder
|
||||
vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "shaders/unshaded.vert", false);
|
||||
fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "shaders/unshaded.frag", false);
|
||||
|
||||
// this can be used when testing shaders,
|
||||
// since we can't hot swap the files in the resource folder
|
||||
// vertexShader = LodShader.loadShader(GL20.GL_VERTEX_SHADER, "C:/Users/James Seibel/Desktop/shaders/unshaded.vert", true);
|
||||
// fragmentShader = LodShader.loadShader(GL20.GL_FRAGMENT_SHADER, "C:/Users/James Seibel/Desktop/shaders/unshaded.frag", true);
|
||||
|
||||
|
||||
// create the shaders
|
||||
|
||||
lodShaderProgram = new LodShaderProgram();
|
||||
|
||||
// Attach the compiled shaders to the program
|
||||
lodShaderProgram.attachShader(vertexShader);
|
||||
lodShaderProgram.attachShader(fragmentShader);
|
||||
|
||||
// activate the fragment shader output
|
||||
GL30.glBindFragDataLocation(lodShaderProgram.id, 0, "fragColor");
|
||||
|
||||
// attach the shader program to the OpenGL context
|
||||
lodShaderProgram.link();
|
||||
|
||||
// after the shaders have been attached to the program
|
||||
// we don't need their OpenGL references anymore
|
||||
GL20.glDeleteShader(vertexShader.id);
|
||||
GL20.glDeleteShader(fragmentShader.id);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error("Unable to compile shaders. Error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A wrapper function to make switching contexts easier. <br>
|
||||
* Does nothing if the calling thread is already using newContext.
|
||||
*/
|
||||
public void setGlContext(GLProxyContext newContext)
|
||||
{
|
||||
GLProxyContext currentContext = getGlContext();
|
||||
|
||||
// we don't have to change the context, we are already there.
|
||||
if (currentContext == newContext)
|
||||
return;
|
||||
|
||||
|
||||
long contextPointer;
|
||||
GLCapabilities newGlCapabilities = null;
|
||||
|
||||
// get the pointer(s) for this context
|
||||
switch (newContext)
|
||||
{
|
||||
case LOD_BUILDER:
|
||||
contextPointer = lodBuilderGlContext;
|
||||
newGlCapabilities = lodBuilderGlCapabilities;
|
||||
break;
|
||||
|
||||
case MINECRAFT:
|
||||
contextPointer = minecraftGlContext;
|
||||
newGlCapabilities = minecraftGlCapabilities;
|
||||
break;
|
||||
|
||||
case PROXY_WORKER:
|
||||
contextPointer = proxyWorkerGlContext;
|
||||
newGlCapabilities = proxyWorkerGlCapabilities;
|
||||
break;
|
||||
|
||||
default: // default should never happen, it is just here to make the compiler happy
|
||||
case NONE:
|
||||
// 0L is equivalent to null
|
||||
contextPointer = 0L;
|
||||
break;
|
||||
}
|
||||
|
||||
GLFW.glfwMakeContextCurrent(contextPointer);
|
||||
GL.setCapabilities(newGlCapabilities);
|
||||
}
|
||||
|
||||
/** Returns this thread's OpenGL context. */
|
||||
public GLProxyContext getGlContext()
|
||||
{
|
||||
long currentContext = GLFW.glfwGetCurrentContext();
|
||||
|
||||
|
||||
if (currentContext == lodBuilderGlContext)
|
||||
return GLProxyContext.LOD_BUILDER;
|
||||
else if (currentContext == minecraftGlContext)
|
||||
return GLProxyContext.MINECRAFT;
|
||||
else if (currentContext == proxyWorkerGlContext)
|
||||
return GLProxyContext.PROXY_WORKER;
|
||||
else if (currentContext == 0L)
|
||||
return GLProxyContext.NONE;
|
||||
else
|
||||
// hopefully this shouldn't happen
|
||||
throw new IllegalStateException(Thread.currentThread().getName() +
|
||||
" has a unknown OpenGl context: [" + currentContext + "]. "
|
||||
+ "Minecraft context [" + minecraftGlContext + "], "
|
||||
+ "LodBuilder context [" + lodBuilderGlContext + "], "
|
||||
+ "ProxyWorker context [" + proxyWorkerGlContext + "], "
|
||||
+ "no context [0].");
|
||||
}
|
||||
|
||||
|
||||
public static GLProxy getInstance()
|
||||
{
|
||||
if (instance == null)
|
||||
instance = new GLProxy();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Asynchronously calls the given runnable on proxy's OpenGL context.
|
||||
* Useful for creating/destroying OpenGL objects in a thread
|
||||
* that doesn't normally have access to a OpenGL context. <br>
|
||||
* No rendering can be done through this method.
|
||||
*/
|
||||
public void recordOpenGlCall(Runnable renderCall)
|
||||
{
|
||||
workerThread.execute(new Thread(() -> { runnableContainer(renderCall); }));
|
||||
}
|
||||
private void runnableContainer(Runnable renderCall)
|
||||
{
|
||||
try
|
||||
{
|
||||
// set up the context...
|
||||
setGlContext(GLProxyContext.PROXY_WORKER);
|
||||
// ...run the actual code...
|
||||
renderCall.run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ClientApi.LOGGER.error(Thread.currentThread().getName() + " ran into a issue: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
// ...and make sure the context is released when the thread finishes
|
||||
setGlContext(GLProxyContext.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,930 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.render;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.lwjgl.opengl.GL15;
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.opengl.GL30;
|
||||
import org.lwjgl.opengl.NVFogDistance;
|
||||
|
||||
import com.seibel.lod.core.api.ApiShared;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
|
||||
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.VertexBuffersAndOffset;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawOverride;
|
||||
import com.seibel.lod.core.enums.rendering.FogQuality;
|
||||
import com.seibel.lod.core.handlers.IReflectionHandler;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
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.objects.opengl.LodVertexBuffer;
|
||||
import com.seibel.lod.core.objects.rending.NearFarFogSettings;
|
||||
import com.seibel.lod.core.render.shader.LodShaderProgram;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LevelPosUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IProfilerWrapper;
|
||||
|
||||
/**
|
||||
* This is where all the magic happens. <br>
|
||||
* This is where LODs are draw to the world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class LodRenderer
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IReflectionHandler REFLECTION_HANDLER = SingletonHandler.get(IReflectionHandler.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
|
||||
// /**
|
||||
// * this is the light used when rendering the LODs,
|
||||
// * it should be something different from what is used by Minecraft
|
||||
// */
|
||||
// private static final int LOD_GL_LIGHT_NUMBER = GL15.GL_LIGHT2;
|
||||
|
||||
/**
|
||||
* If true the LODs colors will be replaced with
|
||||
* a checkerboard, this can be used for debugging.
|
||||
*/
|
||||
public DebugMode previousDebugMode = DebugMode.OFF;
|
||||
|
||||
private int farPlaneBlockDistance;
|
||||
|
||||
|
||||
/** This is used to generate the buildable buffers */
|
||||
private final LodBufferBuilderFactory lodBufferBuilderFactory;
|
||||
|
||||
/** Each VertexBuffer represents 1 region */
|
||||
private LodVertexBuffer[][][] vbos;
|
||||
/**
|
||||
* the OpenGL IDs for the vbos of the same indices.
|
||||
* These have to be separate because we can't override the
|
||||
* buffers in the VBOs (and we don't want to)
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private int[][][] storageBufferIds;
|
||||
|
||||
private AbstractChunkPosWrapper vbosCenter = FACTORY.createChunkPos();
|
||||
|
||||
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private int[] previousPos = new int[] { 0, 0, 0 };
|
||||
|
||||
// these variables are used to determine if the buffers should be rebuilt
|
||||
private float prevSkyBrightness = 0;
|
||||
private double prevBrightness = 0;
|
||||
private int prevRenderDistance = 0;
|
||||
private long prevPlayerPosTime = 0;
|
||||
private long prevVanillaChunkTime = 0;
|
||||
private long prevChunkTime = 0;
|
||||
|
||||
|
||||
/** This is used to determine if the LODs should be regenerated */
|
||||
private FogDistance prevFogDistance = FogDistance.NEAR_AND_FAR;
|
||||
|
||||
/**
|
||||
* if this is true the LOD buffers should be regenerated,
|
||||
* provided they aren't already being regenerated.
|
||||
*/
|
||||
private volatile boolean partialRegen = false;
|
||||
private volatile boolean fullRegen = true;
|
||||
|
||||
/**
|
||||
* This HashSet contains every chunk that Vanilla Minecraft
|
||||
* is going to render
|
||||
*/
|
||||
public boolean[][] vanillaRenderedChunks;
|
||||
public boolean vanillaRenderedChunksChanged;
|
||||
public boolean vanillaRenderedChunksEmptySkip = false;
|
||||
public int vanillaBlockRenderedDistance;
|
||||
|
||||
|
||||
|
||||
|
||||
public LodRenderer(LodBufferBuilderFactory newLodNodeBufferBuilder)
|
||||
{
|
||||
lodBufferBuilderFactory = newLodNodeBufferBuilder;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Besides drawing the LODs this method also starts
|
||||
* 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 mcModelViewMatrix This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method
|
||||
* @param mcProjectionMatrix
|
||||
* @param partialTicks how far into the current tick this method was called.
|
||||
*/
|
||||
public void drawLODs(LodDimension lodDim, Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler)
|
||||
{
|
||||
//=================================//
|
||||
// determine if LODs should render //
|
||||
//=================================//
|
||||
|
||||
if (lodDim == null)
|
||||
{
|
||||
// if there aren't any loaded LodChunks
|
||||
// don't try drawing anything
|
||||
return;
|
||||
}
|
||||
|
||||
if (MC_RENDER.playerHasBlindnessEffect())
|
||||
{
|
||||
// if the player is blind don't render LODs,
|
||||
// and don't change minecraft's fog
|
||||
// which blindness relies on.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
|
||||
// starting here...
|
||||
determineIfLodsShouldRegenerate(lodDim, partialTicks);
|
||||
|
||||
//=================//
|
||||
// create the LODs //
|
||||
//=================//
|
||||
|
||||
// only regenerate the LODs if:
|
||||
// 1. we want to regenerate LODs
|
||||
// 2. we aren't already regenerating the LODs
|
||||
// 3. we aren't waiting for the build and draw buffers to swap
|
||||
// (this is to prevent thread conflicts)
|
||||
if ((partialRegen || fullRegen) && !lodBufferBuilderFactory.generatingBuffers && !lodBufferBuilderFactory.newBuffersAvailable())
|
||||
{
|
||||
// generate the LODs on a separate thread to prevent stuttering or freezing
|
||||
lodBufferBuilderFactory.generateLodBuffersAsync(this, lodDim, MC.getPlayerBlockPos(), true);
|
||||
|
||||
// the regen process has been started,
|
||||
// it will be done when lodBufferBuilder.newBuffersAvailable()
|
||||
// is true
|
||||
fullRegen = false;
|
||||
partialRegen = false;
|
||||
}
|
||||
|
||||
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
|
||||
// ...ending here
|
||||
|
||||
if (lodBufferBuilderFactory.newBuffersAvailable())
|
||||
{
|
||||
swapBuffers();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// initial setup //
|
||||
//===============//
|
||||
|
||||
profiler.push("LOD setup");
|
||||
|
||||
GLProxy glProxy = GLProxy.getInstance();
|
||||
|
||||
|
||||
|
||||
// set the required open GL settings
|
||||
|
||||
if (CONFIG.client().advanced().debugging().getDebugMode() == DebugMode.SHOW_DETAIL_WIREFRAME)
|
||||
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_LINE);
|
||||
else
|
||||
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
|
||||
|
||||
GL15.glEnable(GL15.GL_CULL_FACE);
|
||||
GL15.glEnable(GL15.GL_DEPTH_TEST);
|
||||
|
||||
// enable transparent rendering
|
||||
GL15.glBlendFunc(GL15.GL_SRC_ALPHA, GL15.GL_ONE_MINUS_SRC_ALPHA);
|
||||
GL15.glEnable(GL15.GL_BLEND);
|
||||
|
||||
// get MC's shader program
|
||||
int currentProgram = GL20.glGetInteger(GL20.GL_CURRENT_PROGRAM);
|
||||
|
||||
|
||||
Mat4f modelViewMatrix = offsetTheModelViewMatrix(mcModelViewMatrix, partialTicks);
|
||||
vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
|
||||
// required for setupFog and setupProjectionMatrix
|
||||
if (MC.getWrappedClientWorld().getDimensionType().hasCeiling())
|
||||
farPlaneBlockDistance = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH;
|
||||
else
|
||||
farPlaneBlockDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * LodUtil.CHUNK_WIDTH;
|
||||
|
||||
|
||||
Mat4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks);
|
||||
|
||||
|
||||
// commented out until we can add shaders to handle lighting
|
||||
//setupLighting(lodDim, partialTicks);
|
||||
|
||||
|
||||
// // determine the current fog settings, so they can be
|
||||
// // reset after drawing the LODs
|
||||
// float defaultFogStartDist = GL15.glGetFloat(GL15.GL_FOG_START);
|
||||
// float defaultFogEndDist = GL15.glGetFloat(GL15.GL_FOG_END);
|
||||
// int defaultFogMode = GL15.glGetInteger(GL15.GL_FOG_MODE);
|
||||
// int defaultFogDistance = glProxy.fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1;
|
||||
|
||||
//ShaderInstance mcShader = RenderSystem.getShader();
|
||||
|
||||
// NearFarFogSettings fogSettings = determineFogSettings();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// rendering //
|
||||
//===========//
|
||||
|
||||
profiler.popPush("LOD draw");
|
||||
|
||||
if (vbos != null)
|
||||
{
|
||||
Vec3f cameraDir = MC_RENDER.getLookAtVector();
|
||||
|
||||
// TODO re-enable once rendering is totally working
|
||||
boolean cullingDisabled = true; //LodConfig.client().graphics.advancedGraphicsOption.disableDirectionalCulling.get();
|
||||
// boolean renderBufferStorage = config.client().graphics().advancedGraphics().getGpuUploadMethod() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported;
|
||||
|
||||
// used to determine what type of fog to render
|
||||
// int halfWidth = vbos.length / 2;
|
||||
// int quarterWidth = vbos.length / 4;
|
||||
|
||||
// where the center of the built buffers is (needed when culling regions)
|
||||
RegionPos vboCenterRegionPos = new RegionPos(vbosCenter);
|
||||
|
||||
|
||||
|
||||
|
||||
// can be used when testing shaders
|
||||
//glProxy.createShaderProgram();
|
||||
|
||||
|
||||
LodShaderProgram shaderProgram = glProxy.lodShaderProgram;
|
||||
shaderProgram.use();
|
||||
|
||||
|
||||
// determine the VertexArrayObject's element positions
|
||||
int posAttrib = shaderProgram.getAttributeLocation("vPosition");
|
||||
shaderProgram.enableVertexAttribute(posAttrib);
|
||||
int colAttrib = shaderProgram.getAttributeLocation("color");
|
||||
shaderProgram.enableVertexAttribute(colAttrib);
|
||||
|
||||
|
||||
|
||||
// upload the required uniforms
|
||||
int mvmUniform = shaderProgram.getUniformLocation("modelViewMatrix");
|
||||
shaderProgram.setUniform(mvmUniform, modelViewMatrix);
|
||||
int projUniform = shaderProgram.getUniformLocation("projectionMatrix");
|
||||
shaderProgram.setUniform(projUniform, projectionMatrix);
|
||||
|
||||
|
||||
// render each of the buffers
|
||||
for (int x = 0; x < vbos.length; x++)
|
||||
{
|
||||
for (int z = 0; z < vbos.length; z++)
|
||||
{
|
||||
RegionPos vboPos = new RegionPos(
|
||||
x + vboCenterRegionPos.x - (lodDim.getWidth() / 2),
|
||||
z + vboCenterRegionPos.z - (lodDim.getWidth() / 2));
|
||||
|
||||
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(), cameraDir, vboPos.blockPos()))
|
||||
{
|
||||
// TODO add fog to the fragment shader
|
||||
// if ((x > halfWidth - quarterWidth && x < halfWidth + quarterWidth)
|
||||
// && (z > halfWidth - quarterWidth && z < halfWidth + quarterWidth))
|
||||
// setupFog(fogSettings.near.distance, fogSettings.near.quality);
|
||||
// else
|
||||
// setupFog(fogSettings.far.distance, fogSettings.far.quality);
|
||||
|
||||
// if (storageBufferIds != null && renderBufferStorage)
|
||||
// for (int i = 0; i < storageBufferIds[x][z].length; i++)
|
||||
// drawArrays(storageBufferIds[x][z][i], vbos[x][z][i].vertexCount, posAttrib, colAttrib);
|
||||
// else
|
||||
for (int i = 0; i < vbos[x][z].length; i++)
|
||||
drawArrays(vbos[x][z][i].id, vbos[x][z][i].vertexCount, posAttrib, colAttrib);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GL20.glDisableVertexAttribArray(posAttrib);
|
||||
GL20.glDisableVertexAttribArray(colAttrib);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// cleanup //
|
||||
//=========//
|
||||
|
||||
profiler.popPush("LOD cleanup");
|
||||
|
||||
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
|
||||
GL15.glDisable(GL15.GL_BLEND); // TODO: what should this be reset to?
|
||||
|
||||
GL20.glUseProgram(currentProgram);
|
||||
//RenderSystem.setShader(() -> mcShader);
|
||||
|
||||
// clear the depth buffer so everything drawn is drawn
|
||||
// over the LODs
|
||||
GL15.glClear(GL15.GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
|
||||
// end of internal LOD profiling
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
/** This is where the actual drawing happens. */
|
||||
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib)
|
||||
{
|
||||
if (glBufferId == 0)
|
||||
return;
|
||||
|
||||
// can be used to check for OpenGL errors
|
||||
// int error = GL15.glGetError();
|
||||
// ClientProxy.LOGGER.info(Integer.toHexString(error));
|
||||
|
||||
|
||||
// bind the buffer we are going to draw
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, glBufferId);
|
||||
GL30.glBindVertexArray(GLProxy.getInstance().vertexArrayObjectId);
|
||||
|
||||
// let OpenGL know how our buffer is set up
|
||||
int vertexByteCount = (Float.BYTES * 3) + (Byte.BYTES * 4);
|
||||
GL20.glEnableVertexAttribArray(posAttrib);
|
||||
GL20.glVertexAttribPointer(posAttrib, 3, GL15.GL_FLOAT, false, vertexByteCount, 0);
|
||||
GL20.glEnableVertexAttribArray(colAttrib);
|
||||
GL20.glVertexAttribPointer(colAttrib, 4, GL15.GL_UNSIGNED_BYTE, true, vertexByteCount, Float.BYTES * 3);
|
||||
|
||||
// draw the LODs
|
||||
GL30.glDrawArrays(GL30.GL_TRIANGLES, 0, vertexCount);
|
||||
|
||||
|
||||
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
|
||||
GL30.glBindVertexArray(0);
|
||||
|
||||
GL20.glDisableVertexAttribArray(posAttrib);
|
||||
GL20.glDisableVertexAttribArray(colAttrib);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// Setup Functions //
|
||||
//=================//
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
|
||||
{
|
||||
if (fogQuality == FogQuality.OFF)
|
||||
{
|
||||
GL15.glDisable(GL15.GL_FOG);
|
||||
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)
|
||||
{
|
||||
// fancy fog (fragment distance based fog)
|
||||
glFogDistanceMode = NVFogDistance.GL_EYE_RADIAL_NV;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// the reason that I wrote fogEnd then fogStart backwards
|
||||
// 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
|
||||
if (CONFIG.client().graphics().fogQuality().getFogDistance() == FogDistance.NEAR_AND_FAR)
|
||||
GL15.glFogf(GL15.GL_FOG_START, farPlaneBlockDistance * 1.6f * 0.9f);
|
||||
else
|
||||
GL15.glFogf(GL15.GL_FOG_START, Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f));
|
||||
GL15.glFogf(GL15.GL_FOG_END, farPlaneBlockDistance * 1.6f);
|
||||
}
|
||||
else if (fogQuality == FogQuality.FAST)
|
||||
{
|
||||
// for the far fog of the normal chunks
|
||||
// to start right where the LODs' end use:
|
||||
// end = 0.8f, start = 1.5f
|
||||
GL15.glFogf(GL15.GL_FOG_START, farPlaneBlockDistance * 0.75f);
|
||||
GL15.glFogf(GL15.GL_FOG_END, farPlaneBlockDistance * 1.0f);
|
||||
}
|
||||
}
|
||||
else if (fogDistance == FogDistance.NEAR)
|
||||
{
|
||||
if (fogQuality == FogQuality.FANCY)
|
||||
{
|
||||
GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.41f);
|
||||
GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.6f);
|
||||
}
|
||||
else if (fogQuality == FogQuality.FAST)
|
||||
{
|
||||
GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.0f);
|
||||
GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.5f);
|
||||
}
|
||||
}
|
||||
|
||||
GL15.glEnable(GL15.GL_FOG);
|
||||
GL15.glFogi(GL15.GL_FOG_MODE, GL15.GL_LINEAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revert any changes that were made to the fog
|
||||
* and sets up the fog for Minecraft.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private void cleanupFog(NearFarFogSettings fogSettings,
|
||||
float defaultFogStartDist, float defaultFogEndDist,
|
||||
int defaultFogMode, int defaultFogDistance)
|
||||
{
|
||||
GL15.glFogf(GL15.GL_FOG_START, defaultFogStartDist);
|
||||
GL15.glFogf(GL15.GL_FOG_END, defaultFogEndDist);
|
||||
GL15.glFogi(GL15.GL_FOG_MODE, defaultFogMode);
|
||||
|
||||
// disable fog if Minecraft wasn't rendering fog
|
||||
// or we want it disabled
|
||||
if (!fogSettings.vanillaIsRenderingFog
|
||||
|| CONFIG.client().graphics().fogQuality().getDisableVanillaFog())
|
||||
{
|
||||
// Make fog render a infinite distance away.
|
||||
// This doesn't technically disable Minecraft's fog
|
||||
// so performance will probably be the same regardless, unlike
|
||||
// Optifine's no fog setting.
|
||||
|
||||
// we can't disable minecraft's fog outright because by default
|
||||
// minecraft will re-enable the fog after our code
|
||||
|
||||
GL15.glFogf(GL15.GL_FOG_START, 0.0F);
|
||||
GL15.glFogf(GL15.GL_FOG_END, Float.MAX_VALUE);
|
||||
GL15.glFogf(GL15.GL_FOG_DENSITY, Float.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 Mat4f offsetTheModelViewMatrix(Mat4f mcModelViewMatrix, float partialTicks)
|
||||
{
|
||||
// get all relevant camera info
|
||||
Vec3d projectedView = MC_RENDER.getCameraExactPosition();
|
||||
|
||||
// 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)
|
||||
AbstractBlockPosWrapper bufferPos = vbosCenter.getWorldPosition();
|
||||
double xDiff = projectedView.x - bufferPos.getX();
|
||||
double zDiff = projectedView.z - bufferPos.getZ();
|
||||
mcModelViewMatrix.multiplyTranslationMatrix(-xDiff, -projectedView.y, -zDiff);
|
||||
|
||||
return mcModelViewMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* create and return a new projection matrix based on MC's projection matrix
|
||||
* @param currentProjectionMatrix this is Minecraft's current projection matrix
|
||||
* @param vanillaBlockRenderedDistance Minecraft's vanilla far plane distance
|
||||
* @param partialTicks how many ticks into the frame we are
|
||||
*/
|
||||
private Mat4f createProjectionMatrix(Mat4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks)
|
||||
{
|
||||
// create the new projection matrix
|
||||
|
||||
Mat4f lodProj = Mat4f.perspective(
|
||||
MC_RENDER.getFov(partialTicks),
|
||||
(float) this.MC_RENDER.getScreenWidth() / (float) this.MC_RENDER.getScreenHeight(),
|
||||
CONFIG.client().graphics().advancedGraphics().getUseExtendedNearClipPlane() ? vanillaBlockRenderedDistance / 5 : 1,
|
||||
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2);
|
||||
|
||||
|
||||
// get Minecraft's un-edited projection matrix
|
||||
// (this is before it is zoomed, distorted, etc.)
|
||||
Mat4f defaultMcProj = MC_RENDER.getDefaultProjectionMatrix(partialTicks);
|
||||
// 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
|
||||
Mat4f defaultMcProjInv = defaultMcProj.copy();
|
||||
defaultMcProjInv.invert();
|
||||
|
||||
Mat4f distortionMatrix = defaultMcProjInv.copy();
|
||||
distortionMatrix.multiply(currentProjectionMatrix);
|
||||
|
||||
|
||||
// edit the lod projection to match Minecraft's
|
||||
// (so the LODs line up with the real world)
|
||||
lodProj.multiply(distortionMatrix);
|
||||
|
||||
return lodProj;
|
||||
}
|
||||
|
||||
///** setup the lighting to be used for the LODs */
|
||||
/*private void setupLighting(LodDimension lodDimension, float partialTicks)
|
||||
{
|
||||
// Determine if the player has night vision
|
||||
boolean playerHasNightVision = false;
|
||||
if (this.mc.getPlayer() != null)
|
||||
{
|
||||
Iterator<EffectInstance> iterator = this.mc.getPlayer().getActiveEffects().iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
EffectInstance instance = iterator.next();
|
||||
if (instance.getEffect() == Effects.NIGHT_VISION)
|
||||
{
|
||||
playerHasNightVision = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float sunBrightness = lodDimension.dimension.hasSkyLight() ? mc.getSkyDarken(partialTicks) : 0.2f;
|
||||
sunBrightness = playerHasNightVision ? 1.0f : sunBrightness;
|
||||
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};
|
||||
|
||||
|
||||
// can be used for debugging
|
||||
// if (partialTicks < 0.005)
|
||||
// ClientProxy.LOGGER.debug(lightStrength);
|
||||
|
||||
ByteBuffer temp = ByteBuffer.allocateDirect(16);
|
||||
temp.order(ByteOrder.nativeOrder());
|
||||
GL15.glLightfv(LOD_GL_LIGHT_NUMBER, GL15.GL_AMBIENT, (FloatBuffer) temp.asFloatBuffer().put(lightAmbient).flip());
|
||||
GL15.glEnable(LOD_GL_LIGHT_NUMBER); // Enable the above lighting
|
||||
|
||||
RenderSystem.enableLighting();
|
||||
}*/
|
||||
|
||||
/** Create all buffers that will be used. */
|
||||
public void setupBuffers(LodDimension lodDim)
|
||||
{
|
||||
lodBufferBuilderFactory.setupBuffers(lodDim);
|
||||
}
|
||||
|
||||
|
||||
//======================//
|
||||
// Other Misc Functions //
|
||||
//======================//
|
||||
|
||||
/**
|
||||
* If this is called then the next time "drawLODs" is called
|
||||
* the LODs will be regenerated; the same as if the player moved.
|
||||
*/
|
||||
public void regenerateLODsNextFrame()
|
||||
{
|
||||
fullRegen = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace the current Vertex Buffers with the newly
|
||||
* created buffers from the lodBufferBuilder. <br><br>
|
||||
* <p>
|
||||
* 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
|
||||
VertexBuffersAndOffset result = lodBufferBuilderFactory.getVertexBuffers();
|
||||
vbos = result.vbos;
|
||||
storageBufferIds = result.storageBufferIds;
|
||||
vbosCenter = result.drawableCenterChunkPos;
|
||||
}
|
||||
|
||||
/** Calls the BufferBuilder's destroyBuffers method. */
|
||||
public void destroyBuffers()
|
||||
{
|
||||
lodBufferBuilderFactory.destroyBuffers();
|
||||
}
|
||||
|
||||
/** Return what fog settings should be used when rendering. */
|
||||
@SuppressWarnings("unused")
|
||||
private NearFarFogSettings determineFogSettings()
|
||||
{
|
||||
NearFarFogSettings fogSettings = new NearFarFogSettings();
|
||||
|
||||
|
||||
FogQuality quality = REFLECTION_HANDLER.getFogQuality();
|
||||
FogDrawOverride override = CONFIG.client().graphics().fogQuality().getFogDrawOverride();
|
||||
|
||||
|
||||
fogSettings.vanillaIsRenderingFog = quality != FogQuality.OFF;
|
||||
|
||||
|
||||
// use any fog overrides the user may have set
|
||||
switch (override)
|
||||
{
|
||||
case FANCY:
|
||||
quality = FogQuality.FANCY;
|
||||
break;
|
||||
|
||||
case NO_FOG:
|
||||
quality = FogQuality.OFF;
|
||||
break;
|
||||
|
||||
case FAST:
|
||||
quality = FogQuality.FAST;
|
||||
break;
|
||||
|
||||
case OPTIFINE_SETTING:
|
||||
// don't override anything
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// how different distances are drawn depends on the quality set
|
||||
switch (quality)
|
||||
{
|
||||
case FANCY:
|
||||
fogSettings.near.quality = FogQuality.FANCY;
|
||||
fogSettings.far.quality = FogQuality.FANCY;
|
||||
|
||||
switch (CONFIG.client().graphics().fogQuality().getFogDistance())
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
|
||||
case NEAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case FAR:
|
||||
fogSettings.near.distance = FogDistance.FAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case FAST:
|
||||
fogSettings.near.quality = FogQuality.FAST;
|
||||
fogSettings.far.quality = FogQuality.FAST;
|
||||
|
||||
// fast fog setting should only have one type of
|
||||
// fog, since the LODs are separated into a near
|
||||
// and far portion; and fast fog is rendered from the
|
||||
// frustrum's perspective instead of the camera
|
||||
switch (CONFIG.client().graphics().fogQuality().getFogDistance())
|
||||
{
|
||||
case NEAR_AND_FAR:
|
||||
case NEAR:
|
||||
fogSettings.near.distance = FogDistance.NEAR;
|
||||
fogSettings.far.distance = FogDistance.NEAR;
|
||||
break;
|
||||
|
||||
case FAR:
|
||||
fogSettings.near.distance = FogDistance.FAR;
|
||||
fogSettings.far.distance = FogDistance.FAR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case OFF:
|
||||
fogSettings.near.quality = FogQuality.OFF;
|
||||
fogSettings.far.quality = FogQuality.OFF;
|
||||
break;
|
||||
}
|
||||
return fogSettings;
|
||||
}
|
||||
|
||||
|
||||
/** Determines if the LODs should have a fullRegen or partialRegen */
|
||||
private void determineIfLodsShouldRegenerate(LodDimension lodDim, float partialTicks)
|
||||
{
|
||||
short chunkRenderDistance = (short) MC_RENDER.getRenderDistance();
|
||||
int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2;
|
||||
|
||||
//=============//
|
||||
// full regens //
|
||||
//=============//
|
||||
|
||||
// check if the view distance changed
|
||||
if (ApiShared.previousLodRenderDistance != CONFIG.client().graphics().quality().getLodChunkRenderDistance()
|
||||
|| chunkRenderDistance != prevRenderDistance
|
||||
|| prevFogDistance != CONFIG.client().graphics().fogQuality().getFogDistance())
|
||||
{
|
||||
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
DetailDistanceUtil.updateSettings();
|
||||
fullRegen = true;
|
||||
previousPos = LevelPosUtil.createLevelPos((byte) 4, MC.getPlayerChunkPos().getZ(), MC.getPlayerChunkPos().getZ());
|
||||
prevFogDistance = CONFIG.client().graphics().fogQuality().getFogDistance();
|
||||
prevRenderDistance = chunkRenderDistance;
|
||||
}
|
||||
|
||||
// did the user change the debug setting?
|
||||
if (CONFIG.client().advanced().debugging().getDebugMode() != previousDebugMode)
|
||||
{
|
||||
previousDebugMode = CONFIG.client().advanced().debugging().getDebugMode();
|
||||
fullRegen = true;
|
||||
}
|
||||
|
||||
|
||||
long newTime = System.currentTimeMillis();
|
||||
|
||||
// check if the player has moved
|
||||
if (newTime - prevPlayerPosTime > CONFIG.client().advanced().buffers().getRebuildTimes().playerMoveTimeout)
|
||||
{
|
||||
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|
||||
|| MC.getPlayerChunkPos().getX() != LevelPosUtil.getPosX(previousPos)
|
||||
|| MC.getPlayerChunkPos().getZ() != LevelPosUtil.getPosZ(previousPos))
|
||||
{
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
fullRegen = true;
|
||||
previousPos = LevelPosUtil.createLevelPos((byte) 4, MC.getPlayerChunkPos().getX(), MC.getPlayerChunkPos().getZ());
|
||||
}
|
||||
prevPlayerPosTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// determine how far the lighting has to
|
||||
// change in order to rebuild the buffers
|
||||
|
||||
// the max brightness is 1 and the minimum is 0.2
|
||||
float skyBrightness = lodDim.dimension.hasSkyLight() ? MC.getSkyDarken(partialTicks) : 0.2f;
|
||||
float minLightingDifference;
|
||||
switch (CONFIG.client().advanced().buffers().getRebuildTimes())
|
||||
{
|
||||
case FREQUENT:
|
||||
minLightingDifference = 0.025f;
|
||||
break;
|
||||
case NORMAL:
|
||||
minLightingDifference = 0.05f;
|
||||
break;
|
||||
default:
|
||||
case RARE:
|
||||
minLightingDifference = 0.1f;
|
||||
break;
|
||||
}
|
||||
|
||||
// check if the lighting changed
|
||||
if (Math.abs(skyBrightness - prevSkyBrightness) > minLightingDifference
|
||||
// make sure the lighting gets to the max/minimum value
|
||||
// (just in case the minLightingDifference is too large to notice the change)
|
||||
|| (skyBrightness == 1.0f && prevSkyBrightness != 1.0f) // noon
|
||||
|| (skyBrightness == 0.2f && prevSkyBrightness != 0.2f) // midnight
|
||||
|| MC_RENDER.getGamma() != prevBrightness)
|
||||
{
|
||||
fullRegen = true;
|
||||
prevBrightness = MC_RENDER.getGamma();
|
||||
prevSkyBrightness = skyBrightness;
|
||||
}
|
||||
|
||||
/*if (lightMap != lastLightMap)
|
||||
{
|
||||
fullRegen = true;
|
||||
lastLightMap = lightMap;
|
||||
}*/
|
||||
|
||||
//================//
|
||||
// partial regens //
|
||||
//================//
|
||||
|
||||
|
||||
// check if the vanilla rendered chunks changed
|
||||
if (newTime - prevVanillaChunkTime > CONFIG.client().advanced().buffers().getRebuildTimes().renderedChunkTimeout)
|
||||
{
|
||||
if (vanillaRenderedChunksChanged)
|
||||
{
|
||||
partialRegen = true;
|
||||
vanillaRenderedChunksChanged = false;
|
||||
}
|
||||
prevVanillaChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
// check if there is any newly generated terrain to show
|
||||
if (newTime - prevChunkTime > CONFIG.client().advanced().buffers().getRebuildTimes().chunkChangeTimeout)
|
||||
{
|
||||
if (lodDim.regenDimensionBuffers)
|
||||
{
|
||||
partialRegen = true;
|
||||
lodDim.regenDimensionBuffers = false;
|
||||
}
|
||||
prevChunkTime = newTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// LOD skipping //
|
||||
//==============//
|
||||
|
||||
// determine which LODs should not be rendered close to the player
|
||||
HashSet<AbstractChunkPosWrapper> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, MC.getPlayerBlockPos());
|
||||
int xIndex;
|
||||
int zIndex;
|
||||
for (AbstractChunkPosWrapper pos : chunkPosToSkip)
|
||||
{
|
||||
vanillaRenderedChunksEmptySkip = false;
|
||||
|
||||
xIndex = (pos.getX() - MC.getPlayerChunkPos().getX()) + (chunkRenderDistance + 1);
|
||||
zIndex = (pos.getZ() - MC.getPlayerChunkPos().getZ()) + (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[xIndex][zIndex])
|
||||
{
|
||||
vanillaRenderedChunks[xIndex][zIndex] = true;
|
||||
vanillaRenderedChunksChanged = true;
|
||||
lodDim.markRegionBufferToRegen(pos.getRegionX(), pos.getRegionZ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if the player is high enough, draw all LODs
|
||||
if (chunkPosToSkip.isEmpty() && MC.getPlayerBlockPos().getY() > 256 && !vanillaRenderedChunksEmptySkip)
|
||||
{
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
vanillaRenderedChunksChanged = true;
|
||||
vanillaRenderedChunksEmptySkip = true;
|
||||
}
|
||||
|
||||
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.render;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Vec3f;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
|
||||
/**
|
||||
* This holds miscellaneous helper code
|
||||
* to be used in the rendering process.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 10-19-2021
|
||||
*/
|
||||
public class RenderUtil
|
||||
{
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
|
||||
/**
|
||||
* Returns if the given ChunkPos is in the loaded area of the world.
|
||||
* @param center the center of the loaded world (probably the player's ChunkPos)
|
||||
*/
|
||||
public static boolean isChunkPosInLoadedArea(AbstractChunkPosWrapper pos, AbstractChunkPosWrapper center)
|
||||
{
|
||||
return (pos.getX() >= center.getX() - MC_RENDER.getRenderDistance()
|
||||
&& pos.getX() <= center.getX() + MC_RENDER.getRenderDistance())
|
||||
&&
|
||||
(pos.getZ() >= center.getZ() - MC_RENDER.getRenderDistance()
|
||||
&& pos.getZ() <= center.getZ() + MC_RENDER.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 x, int z, int centerCoordinate)
|
||||
{
|
||||
return (x >= centerCoordinate - MC_RENDER.getRenderDistance()
|
||||
&& x <= centerCoordinate + MC_RENDER.getRenderDistance())
|
||||
&&
|
||||
(z >= centerCoordinate - MC_RENDER.getRenderDistance()
|
||||
&& z <= centerCoordinate + MC_RENDER.getRenderDistance());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the coordinates that are in the center half of the given
|
||||
* 2D matrix, starting at (0,0) and going to (2 * lodRadius, 2 * lodRadius).
|
||||
*/
|
||||
public static boolean isCoordinateInNearFogArea(int i, int j, int lodRadius)
|
||||
{
|
||||
int halfRadius = lodRadius / 2;
|
||||
|
||||
return (i >= lodRadius - halfRadius
|
||||
&& i <= lodRadius + halfRadius)
|
||||
&&
|
||||
(j >= lodRadius - halfRadius
|
||||
&& j <= lodRadius + halfRadius);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if one of the region's 4 corners is in front
|
||||
* of the camera.
|
||||
*/
|
||||
public static boolean isRegionInViewFrustum(AbstractBlockPosWrapper playerBlockPos, Vec3f cameraDir, AbstractBlockPosWrapper vboCenterPos)
|
||||
{
|
||||
// convert the vbo position into a direction vector
|
||||
// starting from the player's position
|
||||
Vec3f vboVec = new Vec3f(vboCenterPos.getX(), 0, vboCenterPos.getZ());
|
||||
Vec3f playerVec = new Vec3f(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ());
|
||||
|
||||
vboVec.subtract(playerVec);
|
||||
Vec3f vboCenterVec = vboVec;
|
||||
|
||||
|
||||
int halfRegionWidth = LodUtil.REGION_WIDTH / 2;
|
||||
|
||||
// calculate the 4 corners
|
||||
Vec3f vboSeVec = new Vec3f(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);
|
||||
Vec3f vboSwVec = new Vec3f(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);
|
||||
Vec3f vboNwVec = new Vec3f(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
|
||||
Vec3f vboNeVec = new Vec3f(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
|
||||
|
||||
// if any corner is visible, this region should be rendered
|
||||
return isNormalizedVectorInViewFrustum(vboSeVec, cameraDir) ||
|
||||
isNormalizedVectorInViewFrustum(vboSwVec, cameraDir) ||
|
||||
isNormalizedVectorInViewFrustum(vboNwVec, cameraDir) ||
|
||||
isNormalizedVectorInViewFrustum(vboNeVec, cameraDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently takes the dot product of the two vectors,
|
||||
* but in the future could do more complicated frustum culling tests.
|
||||
*/
|
||||
private static boolean isNormalizedVectorInViewFrustum(Vec3f objectVector, Vec3f cameraDir)
|
||||
{
|
||||
// the -0.1 is to offer a slight buffer, so we are
|
||||
// more likely to render LODs and thus, hopefully prevent
|
||||
// flickering or odd disappearances
|
||||
return objectVector.dotProduct(cameraDir) > -0.1;
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.render.shader;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi;
|
||||
|
||||
/**
|
||||
* This object holds a OpenGL reference to a shader
|
||||
* and allows for reading in and compiling a shader file.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class LodShader
|
||||
{
|
||||
/** OpenGL shader ID */
|
||||
public final int id;
|
||||
|
||||
|
||||
|
||||
/** Creates a shader with specified type. */
|
||||
public LodShader(int type)
|
||||
{
|
||||
id = GL20.glCreateShader(type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Loads a shader from file.
|
||||
*
|
||||
* @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
|
||||
* @param path File path of the shader
|
||||
* @param absoluteFilePath If false the file path is relative to the resource jar folder.
|
||||
* @throws Exception if the shader fails to compile
|
||||
*/
|
||||
public static LodShader loadShader(int type, String path, boolean absoluteFilePath) throws Exception
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
// open the file
|
||||
InputStream in = absoluteFilePath ? new FileInputStream(path) : LodShader.class.getClassLoader().getResourceAsStream(path);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
|
||||
|
||||
// read in the file
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
stringBuilder.append(line).append("\n");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
ClientApi.LOGGER.error("Unable to load shader from file [" + path + "]. Error: " + e.getMessage());
|
||||
}
|
||||
CharSequence shaderFileSource = stringBuilder.toString();
|
||||
|
||||
return createShader(type, shaderFileSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shader with the specified type and source.
|
||||
*
|
||||
* @param type Either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
|
||||
* @param source Source of the shader
|
||||
* @throws Exception if the shader fails to compile
|
||||
*/
|
||||
public static LodShader createShader(int type, CharSequence source) throws Exception
|
||||
{
|
||||
LodShader shader = new LodShader(type);
|
||||
GL20.glShaderSource(shader.id, source);
|
||||
shader.compile();
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the shader and checks it's status afterwards.
|
||||
* @throws Exception if the shader fails to compile
|
||||
*/
|
||||
public void compile() throws Exception
|
||||
{
|
||||
GL20.glCompileShader(id);
|
||||
|
||||
// check if the shader compiled
|
||||
int status = GL20.glGetShaderi(id, GL20.GL_COMPILE_STATUS);
|
||||
if (status != GL20.GL_TRUE)
|
||||
throw new Exception(GL20.glGetShaderInfoLog(id));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.render.shader;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
|
||||
import org.lwjgl.opengl.GL20;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
|
||||
|
||||
/**
|
||||
* This object holds the reference to a OpenGL shader program
|
||||
* and contains a few methods that can be used with OpenGL shader programs.
|
||||
* The reason for many of these simple wrapper methods is as reminders of what
|
||||
* can (and needs to be) done with a shader program.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-8-2021
|
||||
*/
|
||||
public class LodShaderProgram
|
||||
{
|
||||
/** Stores the handle of the program. */
|
||||
public final int id;
|
||||
|
||||
/** Creates a shader program. */
|
||||
public LodShaderProgram()
|
||||
{
|
||||
id = GL20.glCreateProgram();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Calls GL20.glUseProgram(this.id) */
|
||||
public void use()
|
||||
{
|
||||
GL20.glUseProgram(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls GL20.glAttachShader(this.id, shader.id)
|
||||
*
|
||||
* @param shader Shader to get attached
|
||||
*/
|
||||
public void attachShader(LodShader shader)
|
||||
{
|
||||
GL20.glAttachShader(this.id, shader.id);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Links the shader program to the current OpenGL context.
|
||||
* @throws Exception Exception if the program failed to link
|
||||
*/
|
||||
public void link() throws Exception
|
||||
{
|
||||
GL20.glLinkProgram(this.id);
|
||||
checkLinkStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the program was linked successfully.
|
||||
* @throws Exception if the program failed to link
|
||||
*/
|
||||
public void checkLinkStatus() throws Exception
|
||||
{
|
||||
int status = GL20.glGetProgrami(this.id, GL20.GL_LINK_STATUS);
|
||||
if (status != GL20.GL_TRUE)
|
||||
throw new Exception(GL20.glGetProgramInfoLog(this.id));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the location of an attribute variable with specified name.
|
||||
* Calls GL20.glGetAttribLocation(id, name)
|
||||
*
|
||||
* @param name Attribute name
|
||||
*
|
||||
* @return Location of the attribute
|
||||
*/
|
||||
public int getAttributeLocation(CharSequence name)
|
||||
{
|
||||
return GL20.glGetAttribLocation(id, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls GL20.glEnableVertexAttribArray(location)
|
||||
*
|
||||
* @param location Location of the vertex attribute
|
||||
*/
|
||||
public void enableVertexAttribute(int location)
|
||||
{
|
||||
GL20.glEnableVertexAttribArray(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls GL20.glDisableVertexAttribArray(location)
|
||||
*
|
||||
* @param location Location of the vertex attribute
|
||||
*/
|
||||
public void disableVertexAttribute(int location)
|
||||
{
|
||||
GL20.glDisableVertexAttribArray(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the vertex attribute pointer.
|
||||
* Calls GL20.glVertexAttribPointer(...)
|
||||
*
|
||||
* @param location Location of the vertex attribute
|
||||
* @param size Number of values per vertex
|
||||
* @param stride Offset between consecutive generic vertex attributes in
|
||||
* bytes
|
||||
* @param offset Offset of the first component of the first generic vertex
|
||||
* attribute in bytes
|
||||
*/
|
||||
public void pointVertexAttribute(int location, int size, int stride, int offset)
|
||||
{
|
||||
GL20.glVertexAttribPointer(location, size, GL20.GL_FLOAT, false, stride, offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location of an uniform variable with specified name.
|
||||
* Calls GL20.glGetUniformLocation(id, name)
|
||||
*
|
||||
* @param name Uniform name
|
||||
*
|
||||
* @return -1 = error value, 0 = first value, 1 = second value, etc.
|
||||
*/
|
||||
public int getUniformLocation(CharSequence name)
|
||||
{
|
||||
return GL20.glGetUniformLocation(id, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the uniform variable for specified location.
|
||||
*
|
||||
* @param location Uniform location
|
||||
* @param value Value to set
|
||||
*/
|
||||
public void setUniform(int location, int value)
|
||||
{
|
||||
GL20.glUniform1i(location, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the uniform variable for specified location.
|
||||
*
|
||||
* @param location Uniform location
|
||||
* @param value Value to set
|
||||
*/
|
||||
public void setUniform(int location, Mat4f value)
|
||||
{
|
||||
try (MemoryStack stack = MemoryStack.stackPush())
|
||||
{
|
||||
FloatBuffer buffer = stack.mallocFloat(4 * 4);
|
||||
value.store(buffer);
|
||||
GL20.glUniformMatrix4fv(location, false, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Cola
|
||||
* @author Leonardo Amato
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public class ColorUtil
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
|
||||
|
||||
public static int rgbToInt(int red, int green, int blue)
|
||||
{
|
||||
return (0xFF << 24) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
|
||||
public static int rgbToInt(int alpha, int red, int green, int blue)
|
||||
{
|
||||
return (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
}
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getAlpha(int color)
|
||||
{
|
||||
return (color >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getRed(int color)
|
||||
{
|
||||
return (color >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getGreen(int color)
|
||||
{
|
||||
return (color >> 8) & 0xFF;
|
||||
}
|
||||
|
||||
/** Returns a value between 0 and 255 */
|
||||
public static int getBlue(int color)
|
||||
{
|
||||
return color & 0xFF;
|
||||
}
|
||||
|
||||
public static int applyShade(int color, int shade)
|
||||
{
|
||||
if (shade < 0)
|
||||
return (getAlpha(color) << 24) | (Math.max(getRed(color) + shade, 0) << 16) | (Math.max(getGreen(color) + shade, 0) << 8) | Math.max(getBlue(color) + shade, 0);
|
||||
else
|
||||
return (getAlpha(color) << 24) | (Math.min(getRed(color) + shade, 255) << 16) | (Math.min(getGreen(color) + shade, 255) << 8) | Math.min(getBlue(color) + shade, 255);
|
||||
}
|
||||
|
||||
public static int applyShade(int color, float shade)
|
||||
{
|
||||
if (shade < 1)
|
||||
return (getAlpha(color) << 24) | ((int) Math.max(getRed(color) * shade, 0) << 16) | ((int) Math.max(getGreen(color) * shade, 0) << 8) | (int) Math.max(getBlue(color) * shade, 0);
|
||||
else
|
||||
return (getAlpha(color) << 24) | ((int) Math.min(getRed(color) * shade, 255) << 16) | ((int) Math.min(getGreen(color) * shade, 255) << 8) | (int) Math.min(getBlue(color) * shade, 255);
|
||||
}
|
||||
|
||||
/** This method apply the lightmap to the color to use */
|
||||
public static int applyLightValue(int color, int skyLight, int blockLight)
|
||||
{
|
||||
int lightColor = MC.getColorIntFromLightMap(blockLight, skyLight);
|
||||
int red = ColorUtil.getBlue(lightColor);
|
||||
int green = ColorUtil.getGreen(lightColor);
|
||||
int blue = ColorUtil.getRed(lightColor);
|
||||
|
||||
return ColorUtil.multiplyRGBcolors(color, ColorUtil.rgbToInt(red, green, blue));
|
||||
}
|
||||
|
||||
/** Edit the given color as an HSV (Hue Saturation Value) color */
|
||||
public static int applySaturationAndBrightnessMultipliers(int color, float saturationMultiplier, float brightnessMultiplier)
|
||||
{
|
||||
float[] hsv = Color.RGBtoHSB(getRed(color), getGreen(color), getBlue(color), null);
|
||||
return Color.getHSBColor(
|
||||
hsv[0], // hue
|
||||
LodUtil.clamp(0.0f, hsv[1] * saturationMultiplier, 1.0f),
|
||||
LodUtil.clamp(0.0f, hsv[2] * brightnessMultiplier, 1.0f)).getRGB();
|
||||
}
|
||||
|
||||
/** Multiply 2 RGB colors */
|
||||
public static int multiplyRGBcolors(int color1, int color2)
|
||||
{
|
||||
return ((getAlpha(color1) * getAlpha(color2) / 255) << 24) | ((getRed(color1) * getRed(color2) / 255) << 16) | ((getGreen(color1) * getGreen(color2) / 255) << 8) | (getBlue(color1) * getBlue(color2) / 255);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static String toString(int color)
|
||||
{
|
||||
return Integer.toHexString(getAlpha(color)) + " " +
|
||||
Integer.toHexString(getRed(color)) + " " +
|
||||
Integer.toHexString(getGreen(color)) + " " +
|
||||
Integer.toHexString(getBlue(color));
|
||||
}
|
||||
}
|
||||
@@ -1,520 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
import static com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory.skyLightPlayer;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version ??
|
||||
*/
|
||||
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 |
|
||||
|
||||
|
||||
*/
|
||||
|
||||
// Reminder: bytes have range of [-128, 127].
|
||||
// When converting to or from an int a 128 should be added or removed.
|
||||
// If there is a bug with color then it's probably caused by this.
|
||||
|
||||
//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, boolean flag)
|
||||
{
|
||||
return createDataPoint(
|
||||
ColorUtil.getAlpha(color),
|
||||
ColorUtil.getRed(color),
|
||||
ColorUtil.getGreen(color),
|
||||
ColorUtil.getBlue(color),
|
||||
height, depth, lightSky, lightBlock, generationMode, flag);
|
||||
}
|
||||
|
||||
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode, boolean flag)
|
||||
{
|
||||
long dataPoint = 0;
|
||||
dataPoint += (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << 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;
|
||||
if (flag) dataPoint += FLAG_MASK << FLAG_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) | 0b1111);
|
||||
}
|
||||
|
||||
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 byte getLightSky(long dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static byte getLightSkyAlt(long dataPoint)
|
||||
{
|
||||
if (skyLightPlayer == 0 && ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1)
|
||||
return 0;
|
||||
else
|
||||
return (byte) ((dataPoint >>> SKY_LIGHT_SHIFT) & SKY_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static byte getLightBlock(long dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> BLOCK_LIGHT_SHIFT) & BLOCK_LIGHT_MASK);
|
||||
}
|
||||
|
||||
public static boolean getFlag(long dataPoint)
|
||||
{
|
||||
return ((dataPoint >>> FLAG_SHIFT) & FLAG_MASK) == 1;
|
||||
}
|
||||
|
||||
public static byte getGenerationMode(long dataPoint)
|
||||
{
|
||||
return (byte) ((dataPoint >>> GEN_TYPE_SHIFT) & GEN_TYPE_MASK);
|
||||
}
|
||||
|
||||
|
||||
public static boolean isVoid(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)
|
||||
{
|
||||
return (int) (((dataPoint >>> COLOR_SHIFT) & COLOR_MASK) | (/*((dataPoint >>> (ALPHA_SHIFT - ALPHA_DOWNSIZE_SHIFT)) | 0b1111)*/255 << 24));
|
||||
}
|
||||
|
||||
/** This is used to convert a dataPoint to string (useful for the print function) */
|
||||
@SuppressWarnings("unused")
|
||||
public static String toString(long dataPoint)
|
||||
{
|
||||
return getHeight(dataPoint) + " " +
|
||||
getDepth(dataPoint) + " " +
|
||||
getAlpha(dataPoint) + " " +
|
||||
getRed(dataPoint) + " " +
|
||||
getBlue(dataPoint) + " " +
|
||||
getGreen(dataPoint) + " " +
|
||||
getLightBlock(dataPoint) + " " +
|
||||
getLightSky(dataPoint) + " " +
|
||||
getGenerationMode(dataPoint) + " " +
|
||||
isVoid(dataPoint) + " " +
|
||||
doesItExist(dataPoint) + '\n';
|
||||
}
|
||||
|
||||
public static void shrinkArray(short[] array, int packetSize, int start, int length, int arraySize)
|
||||
{
|
||||
start *= packetSize;
|
||||
length *= packetSize;
|
||||
arraySize *= packetSize;
|
||||
for (int i = 0; i < arraySize - start; i++)
|
||||
{
|
||||
array[start + i] = array[start + length + i];
|
||||
//remove comment to not leave garbage at the end
|
||||
//array[start + packetSize + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void extendArray(short[] array, int packetSize, int start, int length, int arraySize)
|
||||
{
|
||||
start *= packetSize;
|
||||
length *= packetSize;
|
||||
arraySize *= packetSize;
|
||||
for (int i = arraySize - start - 1; i >= 0; i--)
|
||||
{
|
||||
array[start + length + i] = array[start + i];
|
||||
array[start + i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method merge column of multiple data together
|
||||
* @param dataToMerge one or more columns of data
|
||||
* @param inputVerticalData vertical size of an input data
|
||||
* @param maxVerticalData max vertical size of the merged data
|
||||
* @return one column of correctly parsed data
|
||||
*/
|
||||
public static long[] mergeMultiData(long[] dataToMerge, int inputVerticalData, int maxVerticalData)
|
||||
{
|
||||
int size = dataToMerge.length / inputVerticalData;
|
||||
|
||||
// We initialize the arrays that are going to be used
|
||||
short[] heightAndDepth = ThreadMapUtil.getHeightAndDepth((worldHeight / 2 + 1) * 2);
|
||||
long[] dataPoint = ThreadMapUtil.getVerticalDataArray(DetailDistanceUtil.getMaxVerticalData(0));
|
||||
|
||||
|
||||
int genMode = DistanceGenerationMode.FULL.complexity;
|
||||
boolean allEmpty = true;
|
||||
boolean allVoid = true;
|
||||
boolean allDefault;
|
||||
long singleData;
|
||||
|
||||
|
||||
short depth;
|
||||
short height;
|
||||
int count = 0;
|
||||
int i;
|
||||
int ii;
|
||||
int dataIndex;
|
||||
//We collect the indexes of the data, ordered by the depth
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData))
|
||||
{
|
||||
genMode = Math.min(genMode, getGenerationMode(singleData));
|
||||
allEmpty = false;
|
||||
if (!isVoid(singleData))
|
||||
{
|
||||
allVoid = false;
|
||||
depth = getDepth(singleData);
|
||||
height = getHeight(singleData);
|
||||
|
||||
int botPos = -1;
|
||||
int topPos = -1;
|
||||
//values fall in between and possibly require extension of array
|
||||
boolean botExtend = false;
|
||||
boolean topExtend = false;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (depth <= heightAndDepth[i * 2] && depth >= heightAndDepth[i * 2 + 1])
|
||||
{
|
||||
botPos = i;
|
||||
break;
|
||||
}
|
||||
else if (depth < heightAndDepth[i * 2 + 1] && ((i + 1 < count && depth > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
{
|
||||
botPos = i;
|
||||
botExtend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
if (height <= heightAndDepth[i * 2] && height >= heightAndDepth[i * 2 + 1])
|
||||
{
|
||||
topPos = i;
|
||||
break;
|
||||
}
|
||||
else if (height < heightAndDepth[i * 2 + 1] && ((i + 1 < count && height > heightAndDepth[(i + 1) * 2]) || i + 1 == count))
|
||||
{
|
||||
topPos = i;
|
||||
topExtend = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (topPos == -1)
|
||||
{
|
||||
if (botPos == -1)
|
||||
{
|
||||
//whole block falls above
|
||||
extendArray(heightAndDepth, 2, 0, 1, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count++;
|
||||
}
|
||||
else if (!botExtend)
|
||||
{
|
||||
//only top falls above extending it there, while bottom is inside existing
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
count -= botPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//top falls between some blocks, extending those as well
|
||||
shrinkArray(heightAndDepth, 2, 0, botPos, count);
|
||||
heightAndDepth[0] = height;
|
||||
heightAndDepth[1] = depth;
|
||||
count -= botPos;
|
||||
}
|
||||
}
|
||||
else if (!topExtend)
|
||||
{
|
||||
if (!botExtend)
|
||||
//both top and bottom are within some exiting blocks, possibly merging them
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
else
|
||||
//top falls between some blocks, extending it there
|
||||
heightAndDepth[topPos * 2 + 1] = depth;
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!botExtend)
|
||||
{
|
||||
//only top is within some exiting block, extending it
|
||||
topPos++; //to make it easier
|
||||
heightAndDepth[topPos * 2] = height;
|
||||
heightAndDepth[topPos * 2 + 1] = heightAndDepth[botPos * 2 + 1];
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
//both top and bottom are outside existing blocks
|
||||
shrinkArray(heightAndDepth, 2, topPos + 1, botPos - topPos, count);
|
||||
count -= botPos - topPos;
|
||||
extendArray(heightAndDepth, 2, topPos + 1, 1, count);
|
||||
count++;
|
||||
heightAndDepth[topPos * 2 + 2] = height;
|
||||
heightAndDepth[topPos * 2 + 3] = depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//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 limit the vertical portion to maxVerticalData
|
||||
int j = 0;
|
||||
while (count > maxVerticalData)
|
||||
{
|
||||
ii = worldHeight;
|
||||
for (i = 0; i < count - 1; i++)
|
||||
{
|
||||
if (heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2] <= ii)
|
||||
{
|
||||
ii = heightAndDepth[i * 2 + 1] - heightAndDepth[(i + 1) * 2];
|
||||
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 = count - 1; j >= 0; j--)
|
||||
{
|
||||
height = heightAndDepth[j * 2];
|
||||
depth = heightAndDepth[j * 2 + 1];
|
||||
|
||||
if ((depth == 0 && height == 0) || j >= heightAndDepth.length / 2)
|
||||
break;
|
||||
|
||||
int numberOfChildren = 0;
|
||||
int tempAlpha = 0;
|
||||
int tempRed = 0;
|
||||
int tempGreen = 0;
|
||||
int tempBlue = 0;
|
||||
int tempLightBlock = 0;
|
||||
int tempLightSky = 0;
|
||||
byte tempGenMode = DistanceGenerationMode.FULL.complexity;
|
||||
allEmpty = true;
|
||||
allVoid = true;
|
||||
allDefault = true;
|
||||
long data = 0;
|
||||
|
||||
for (int index = 0; index < size; index++)
|
||||
{
|
||||
for (dataIndex = 0; dataIndex < inputVerticalData; dataIndex++)
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData + dataIndex];
|
||||
if (doesItExist(singleData) && !isVoid(singleData))
|
||||
{
|
||||
|
||||
if ((depth <= getDepth(singleData) && getDepth(singleData) <= height)
|
||||
|| (depth <= getHeight(singleData) && getHeight(singleData) <= height))
|
||||
{
|
||||
if (getHeight(singleData) > getHeight(data))
|
||||
data = singleData;
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!doesItExist(data))
|
||||
{
|
||||
singleData = dataToMerge[index * inputVerticalData];
|
||||
data = createVoidDataPoint(getGenerationMode(singleData));
|
||||
}
|
||||
|
||||
if (doesItExist(data))
|
||||
{
|
||||
allEmpty = false;
|
||||
if (!isVoid(data))
|
||||
{
|
||||
numberOfChildren++;
|
||||
allVoid = false;
|
||||
tempAlpha += getAlpha(data);
|
||||
tempRed += getRed(data);
|
||||
tempGreen += getGreen(data);
|
||||
tempBlue += getBlue(data);
|
||||
tempLightBlock += getLightBlock(data);
|
||||
tempLightSky += getLightSky(data);
|
||||
if (!getFlag(data)) allDefault = false;
|
||||
}
|
||||
tempGenMode = (byte) Math.min(tempGenMode, getGenerationMode(data));
|
||||
}
|
||||
else
|
||||
tempGenMode = (byte) Math.min(tempGenMode, DistanceGenerationMode.NONE.complexity);
|
||||
}
|
||||
|
||||
if (allEmpty)
|
||||
//no child has been initialized
|
||||
dataPoint[j] = EMPTY_DATA;
|
||||
else if (allVoid)
|
||||
//all the children are void
|
||||
dataPoint[j] = 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;
|
||||
dataPoint[j] = createDataPoint(tempAlpha, tempRed, tempGreen, tempBlue, height, depth, tempLightSky, tempLightBlock, tempGenMode, allDefault);
|
||||
}
|
||||
}
|
||||
return dataPoint;
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.HorizontalQuality;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version ??
|
||||
*/
|
||||
public class DetailDistanceUtil
|
||||
{
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
|
||||
private static final double genMultiplier = 1.0;
|
||||
private static final double treeGenMultiplier = 1.0;
|
||||
private static final double treeCutMultiplier = 1.0;
|
||||
private static byte minGenDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel;
|
||||
private static byte minDrawDetail = (byte) Math.max(CONFIG.client().graphics().quality().getDrawResolution().detailLevel, CONFIG.client().graphics().quality().getDrawResolution().detailLevel);
|
||||
private static final int maxDetail = LodUtil.REGION_DETAIL_LEVEL + 1;
|
||||
private static final int minDistance = 0;
|
||||
private static int minDetailDistance = (int) (MC_RENDER.getRenderDistance()*16 * 1.42f);
|
||||
private static int maxDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 16 * 2;
|
||||
|
||||
|
||||
private static final 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()
|
||||
{
|
||||
minDetailDistance = (int) (MC_RENDER.getRenderDistance()*16 * 1.42f);
|
||||
minGenDetail = CONFIG.client().graphics().quality().getDrawResolution().detailLevel;
|
||||
minDrawDetail = (byte) Math.max(CONFIG.client().graphics().quality().getDrawResolution().detailLevel, CONFIG.client().graphics().quality().getDrawResolution().detailLevel);
|
||||
maxDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 16 * 8;
|
||||
}
|
||||
|
||||
public static int baseDistanceFunction(int detail)
|
||||
{
|
||||
if (detail <= minGenDetail)
|
||||
return minDistance;
|
||||
if (detail >= maxDetail)
|
||||
return maxDistance;
|
||||
|
||||
if (CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality())
|
||||
return detail * 0x10000; //if you want more you are doing wrong
|
||||
|
||||
int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale().distanceUnit;
|
||||
if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST)
|
||||
return (detail * distanceUnit);
|
||||
else
|
||||
{
|
||||
double base = CONFIG.client().graphics().quality().getHorizontalQuality().quadraticBase;
|
||||
return (int) (Math.pow(base, detail) * distanceUnit);
|
||||
}
|
||||
}
|
||||
|
||||
public static int getDrawDistanceFromDetail(int detail)
|
||||
{
|
||||
return baseDistanceFunction(detail);
|
||||
}
|
||||
|
||||
public static byte baseInverseFunction(int distance, byte minDetail, boolean useRenderMinDistance)
|
||||
{
|
||||
int detail;
|
||||
if (distance == 0
|
||||
|| (distance < minDetailDistance && useRenderMinDistance)
|
||||
|| CONFIG.client().graphics().advancedGraphics().getAlwaysDrawAtMaxQuality())
|
||||
return minDetail;
|
||||
int distanceUnit = CONFIG.client().graphics().quality().getHorizontalScale().distanceUnit;
|
||||
if (CONFIG.client().graphics().quality().getHorizontalQuality() == HorizontalQuality.LOWEST)
|
||||
detail = (byte) distance / distanceUnit;
|
||||
else
|
||||
{
|
||||
double base = CONFIG.client().graphics().quality().getHorizontalQuality().quadraticBase;
|
||||
double logBase = Math.log(base);
|
||||
//noinspection IntegerDivisionInFloatingPointContext
|
||||
detail = (byte) (Math.log(distance / distanceUnit) / logBase);
|
||||
}
|
||||
return (byte) LodUtil.clamp(minDetail, detail, maxDetail - 1);
|
||||
}
|
||||
|
||||
public static byte getDrawDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction(distance, minDrawDetail, false);
|
||||
}
|
||||
|
||||
public static byte getGenerationDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction((int) (distance * genMultiplier), minGenDetail, true);
|
||||
}
|
||||
|
||||
public static byte getTreeCutDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction((int) (distance * treeCutMultiplier), minGenDetail, true);
|
||||
}
|
||||
|
||||
public static byte getTreeGenDetailFromDistance(int distance)
|
||||
{
|
||||
return baseInverseFunction((int) (distance * treeGenMultiplier), minGenDetail, true);
|
||||
}
|
||||
|
||||
public static DistanceGenerationMode getDistanceGenerationMode(int detail)
|
||||
{
|
||||
return CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
}
|
||||
|
||||
public static byte getLodDrawDetail(int detail)
|
||||
{
|
||||
if (detail < minDrawDetail)
|
||||
return minDrawDetail;
|
||||
else
|
||||
return (byte) detail;
|
||||
}
|
||||
|
||||
public static HorizontalResolution getLodGenDetail(int detail)
|
||||
{
|
||||
if (detail < minGenDetail)
|
||||
return lodGenDetails[minGenDetail];
|
||||
else
|
||||
return lodGenDetails[detail];
|
||||
}
|
||||
|
||||
|
||||
public static byte getCutLodDetail(int detail)
|
||||
{
|
||||
if (detail < minGenDetail)
|
||||
return lodGenDetails[minGenDetail].detailLevel;
|
||||
else if (detail == maxDetail)
|
||||
return LodUtil.REGION_DETAIL_LEVEL;
|
||||
else
|
||||
return lodGenDetails[detail].detailLevel;
|
||||
}
|
||||
|
||||
public static int getMaxVerticalData(int detail)
|
||||
{
|
||||
return CONFIG.client().graphics().quality().getVerticalQuality().maxVerticalData[LodUtil.clamp(minGenDetail, detail, LodUtil.REGION_DETAIL_LEVEL)];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,269 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version ??
|
||||
*/
|
||||
public class LevelPosUtil
|
||||
{
|
||||
public static int[] convert(int[] levelPos, byte newDetailLevel)
|
||||
{
|
||||
return convert(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos), newDetailLevel);
|
||||
}
|
||||
|
||||
public static int[] convert(byte detailLevel, int posX, int posZ, byte newDetailLevel)
|
||||
{
|
||||
int width;
|
||||
if (newDetailLevel >= detailLevel)
|
||||
{
|
||||
width = 1 << (newDetailLevel - detailLevel);
|
||||
return createLevelPos(
|
||||
newDetailLevel,
|
||||
Math.floorDiv(posX, width),
|
||||
Math.floorDiv(posZ, width));
|
||||
}
|
||||
else
|
||||
{
|
||||
width = 1 << (detailLevel - newDetailLevel);
|
||||
return createLevelPos(
|
||||
newDetailLevel,
|
||||
posX * width,
|
||||
posZ * width);
|
||||
}
|
||||
}
|
||||
|
||||
public static int[] createLevelPos(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return new int[] { detailLevel, posX, posZ };
|
||||
}
|
||||
|
||||
public static int convert(byte detailLevel, int pos, byte newDetailLevel)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return (byte) levelPos[0];
|
||||
}
|
||||
|
||||
public static int getPosX(int[] levelPos)
|
||||
{
|
||||
return levelPos[1];
|
||||
}
|
||||
|
||||
public static int getPosZ(int[] levelPos)
|
||||
{
|
||||
return levelPos[2];
|
||||
}
|
||||
|
||||
public static int getDistance(int[] levelPos)
|
||||
{
|
||||
return levelPos[3];
|
||||
}
|
||||
|
||||
public static int[] getRegionModule(int[] levelPos)
|
||||
{
|
||||
return getRegionModule(getDetailLevel(levelPos), getPosX(levelPos), getPosZ(levelPos));
|
||||
}
|
||||
|
||||
public static int[] getRegionModule(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
return createLevelPos(
|
||||
detailLevel,
|
||||
Math.floorMod(posX, width),
|
||||
Math.floorMod(posZ, width));
|
||||
}
|
||||
|
||||
public static int[] applyOffset(int[] levelPos, int xOffset, int zOffset)
|
||||
{
|
||||
return createLevelPos(
|
||||
getDetailLevel(levelPos),
|
||||
getPosX(levelPos) + xOffset,
|
||||
getPosZ(levelPos) + zOffset);
|
||||
}
|
||||
|
||||
public static int[] applyLevelOffset(int[] levelPos, byte detailOffset, int xOffset, int zOffset)
|
||||
{
|
||||
return createLevelPos(
|
||||
getDetailLevel(levelPos),
|
||||
getPosX(levelPos) + xOffset * (1 << detailOffset),
|
||||
getPosZ(levelPos) + zOffset * (1 << detailOffset));
|
||||
}
|
||||
|
||||
public static int getRegionPosX(int[] levelPos)
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - getDetailLevel(levelPos));
|
||||
return Math.floorDiv(getPosX(levelPos), width);
|
||||
}
|
||||
|
||||
public static int getRegionPosZ(int[] levelPos)
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - getDetailLevel(levelPos));
|
||||
return Math.floorDiv(getPosZ(levelPos), width);
|
||||
}
|
||||
|
||||
public static int getChunkPos(byte detailLevel, int pos)
|
||||
{
|
||||
return convert(detailLevel, pos, LodUtil.CHUNK_DETAIL_LEVEL);
|
||||
}
|
||||
|
||||
public static int myPow2(int x)
|
||||
{
|
||||
return x*x;
|
||||
}
|
||||
|
||||
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ)
|
||||
{
|
||||
int width = 1 << detailLevel;
|
||||
|
||||
int startPosX = posX * width;
|
||||
int startPosZ = posZ * width;
|
||||
int endPosX = myPow2(playerPosX - startPosX - width);
|
||||
int endPosZ = myPow2(playerPosZ - startPosZ - width);
|
||||
startPosX = myPow2(playerPosX - startPosX);
|
||||
startPosZ = myPow2(playerPosZ - startPosZ);
|
||||
|
||||
int maxDistance = (int) Math.sqrt(startPosX + startPosZ);
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(startPosX + endPosZ));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(endPosX + startPosZ));
|
||||
maxDistance = Math.max(maxDistance, (int) Math.sqrt(endPosX + endPosZ));
|
||||
|
||||
return maxDistance;
|
||||
}
|
||||
|
||||
public static int maxDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion)
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
int newPosX = xRegion * width + posX;
|
||||
int newPosZ = zRegion * width + posZ;
|
||||
return maxDistance(detailLevel, newPosX, newPosZ, playerPosX, playerPosZ);
|
||||
}
|
||||
|
||||
|
||||
public static int minDistance(byte detailLevel, int posX, int posZ, 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
|
||||
{
|
||||
startPosX = myPow2(playerPosX - startPosX);
|
||||
startPosZ = myPow2(playerPosZ - startPosZ);
|
||||
endPosX = myPow2(playerPosX - endPosX);
|
||||
endPosZ = myPow2(playerPosZ - endPosZ);
|
||||
|
||||
int minDistance = (int) Math.sqrt(startPosX + startPosZ);
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(startPosX + endPosZ));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(endPosX + startPosZ));
|
||||
minDistance = Math.min(minDistance, (int) Math.sqrt(endPosX + endPosZ));
|
||||
return minDistance;
|
||||
}
|
||||
}
|
||||
|
||||
public static int minDistance(byte detailLevel, int posX, int posZ, int playerPosX, int playerPosZ, int xRegion, int zRegion)
|
||||
{
|
||||
int width = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
|
||||
int newPosX = xRegion * width + posX;
|
||||
int newPosZ = zRegion * width + posZ;
|
||||
return minDistance(detailLevel, newPosX, newPosZ, playerPosX, playerPosZ);
|
||||
}
|
||||
|
||||
public static int compareDistance(int firstDistance, int secondDistance)
|
||||
{
|
||||
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(
|
||||
firstDistance,
|
||||
secondDistance);
|
||||
}
|
||||
return compareResult;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static String toString(int[] levelPos)
|
||||
{
|
||||
return (getDetailLevel(levelPos) + " "
|
||||
+ getPosX(levelPos) + " "
|
||||
+ getPosZ(levelPos));
|
||||
}
|
||||
|
||||
public static String toString(byte detailLevel, int posX, int posZ)
|
||||
{
|
||||
return (detailLevel + " " + posX + " " + posZ);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
/**
|
||||
* Just a simple ThreadFactory to name ExecutorService
|
||||
* threads, which can be helpful when debugging.
|
||||
* @author James Seibel
|
||||
* @version 8-15-2021
|
||||
*/
|
||||
public class LodThreadFactory implements ThreadFactory
|
||||
{
|
||||
public final String threadName;
|
||||
|
||||
|
||||
public LodThreadFactory(String newThreadName)
|
||||
{
|
||||
threadName = newThreadName + " Thread";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r)
|
||||
{
|
||||
return new Thread(r, threadName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,427 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.enums.config.VanillaOverdraw;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.objects.lod.RegionPos;
|
||||
import com.seibel.lod.core.objects.opengl.DefaultLodVertexFormats;
|
||||
import com.seibel.lod.core.objects.opengl.LodVertexFormat;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This class holds methods and constants that may be used in multiple places.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-13-2021
|
||||
*/
|
||||
public class LodUtil
|
||||
{
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonHandler.get(IMinecraftRenderWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
/**
|
||||
* Vanilla render distances less than or equal to this will not allow partial
|
||||
* overdraw. The VanillaOverdraw will either be ALWAYS or NEVER.
|
||||
*/
|
||||
public static final int MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW = 4;
|
||||
|
||||
/**
|
||||
* Vanilla render distances less than or equal to this will cause the overdraw to
|
||||
* run at a smaller fraction of the vanilla render distance.
|
||||
*/
|
||||
public static final int MINIMUM_RENDER_DISTANCE_FOR_FAR_OVERDRAW = 11;
|
||||
|
||||
|
||||
|
||||
|
||||
/** The maximum number of LODs that can be rendered vertically */
|
||||
public static final int MAX_NUMBER_OF_VERTICAL_LODS = 32;
|
||||
|
||||
/**
|
||||
* alpha used when drawing chunks in debug mode
|
||||
*/
|
||||
public static final int DEBUG_ALPHA = 255; // 0 - 255
|
||||
public static final Color COLOR_DEBUG_BLACK = new Color(0, 0, 0, DEBUG_ALPHA);
|
||||
public static final Color COLOR_DEBUG_WHITE = new Color(255, 255, 255, DEBUG_ALPHA);
|
||||
public static final Color COLOR_INVISIBLE = new Color(0, 0, 0, 0);
|
||||
|
||||
public static final int CEILED_DIMENSION_MAX_RENDER_DISTANCE = 64; // 0 - 255
|
||||
|
||||
/**
|
||||
* In order of nearest to farthest: <br>
|
||||
* Red, Orange, Yellow, Green, Cyan, Blue, Magenta, white, gray, black
|
||||
*/
|
||||
public static final Color[] DEBUG_DETAIL_LEVEL_COLORS = new Color[] { Color.RED, Color.ORANGE, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.WHITE, Color.GRAY, Color.BLACK };
|
||||
|
||||
|
||||
public static final byte DETAIL_OPTIONS = 10;
|
||||
|
||||
/** 512 blocks wide */
|
||||
public static final byte REGION_DETAIL_LEVEL = DETAIL_OPTIONS - 1;
|
||||
/** 16 blocks wide */
|
||||
public static final byte CHUNK_DETAIL_LEVEL = 4;
|
||||
/** 1 block wide */
|
||||
public static final byte BLOCK_DETAIL_LEVEL = 0;
|
||||
|
||||
public static final short MAX_VERTICAL_DATA = 4;
|
||||
|
||||
/**
|
||||
* measured in Blocks <br>
|
||||
* detail level max - 1
|
||||
*/
|
||||
public static final short REGION_WIDTH = 1 << REGION_DETAIL_LEVEL;
|
||||
/**
|
||||
* measured in Blocks <br>
|
||||
* detail level 4
|
||||
*/
|
||||
public static final short CHUNK_WIDTH = 16;
|
||||
/**
|
||||
* measured in Blocks <br>
|
||||
* detail level 0
|
||||
*/
|
||||
public static final short BLOCK_WIDTH = 1;
|
||||
|
||||
|
||||
/** number of chunks wide */
|
||||
public static final int REGION_WIDTH_IN_CHUNKS = REGION_WIDTH / CHUNK_WIDTH;
|
||||
|
||||
|
||||
/**
|
||||
* 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 = "[\\\\/:*?\"<>|]";
|
||||
|
||||
/**
|
||||
* 64 MB by default is the maximum amount of memory that
|
||||
* can be directly allocated. <br><br>
|
||||
* <p>
|
||||
* James knows there are commands to change that amount
|
||||
* (specifically "-XX:MaxDirectMemorySize"), but
|
||||
* He has no idea how to access that amount. <br>
|
||||
* So for now this will be the hard limit. <br><br>
|
||||
* <p>
|
||||
* https://stackoverflow.com/questions/50499238/bytebuffer-allocatedirect-and-xmx
|
||||
*/
|
||||
public static final int MAX_ALLOCATABLE_DIRECT_MEMORY = 64 * 1024 * 1024;
|
||||
|
||||
/** the format of data stored in the GPU buffers */
|
||||
public static final LodVertexFormat LOD_VERTEX_FORMAT = DefaultLodVertexFormats.POSITION_COLOR;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the ServerWorld for the relevant dimension.
|
||||
* @return null if there is no ServerWorld for the given dimension
|
||||
*/
|
||||
public static IWorldWrapper getServerWorldFromDimension(IDimensionTypeWrapper newDimension)
|
||||
{
|
||||
if(!MC.hasSinglePlayerServer())
|
||||
return null;
|
||||
|
||||
Iterable<IWorldWrapper> worlds = MC.getAllServerWorlds();
|
||||
IWorldWrapper returnWorld = null;
|
||||
|
||||
for (IWorldWrapper world : worlds)
|
||||
{
|
||||
if (world.getDimensionType() == newDimension)
|
||||
{
|
||||
returnWorld = world;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return returnWorld;
|
||||
}
|
||||
|
||||
/** Convert a 2D absolute position into a quad tree relative position. */
|
||||
public static RegionPos convertGenericPosToRegionPos(int x, int z, int detailLevel)
|
||||
{
|
||||
int relativePosX = Math.floorDiv(x, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
|
||||
int relativePosZ = Math.floorDiv(z, 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel));
|
||||
|
||||
return new RegionPos(relativePosX, relativePosZ);
|
||||
}
|
||||
|
||||
/** Convert a 2D absolute position into a quad tree relative position. */
|
||||
public static int convertLevelPos(int pos, int currentDetailLevel, int targetDetailLevel)
|
||||
{
|
||||
return pos / (1 << (targetDetailLevel - currentDetailLevel));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If on single player this will return the name of the user's
|
||||
* world, if in multiplayer it will return the server name, IP,
|
||||
* and game version.
|
||||
*/
|
||||
public static String getWorldID(IWorldWrapper world)
|
||||
{
|
||||
if (MC.hasSinglePlayerServer())
|
||||
{
|
||||
// chop off the dimension ID as it is not needed/wanted
|
||||
String dimId = getDimensionIDFromWorld(world);
|
||||
|
||||
// get the world name
|
||||
int saveIndex = dimId.indexOf("saves") + 1 + "saves".length();
|
||||
int slashIndex = dimId.indexOf(File.separatorChar, saveIndex);
|
||||
dimId = dimId.substring(saveIndex, slashIndex);
|
||||
return dimId;
|
||||
}
|
||||
else
|
||||
{
|
||||
return getServerId();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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, 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(IWorldWrapper world)
|
||||
{
|
||||
if (MC.hasSinglePlayerServer())
|
||||
{
|
||||
// this will return the world save location
|
||||
// and the dimension folder
|
||||
|
||||
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(world.getDimensionType());
|
||||
if (serverWorld == null)
|
||||
throw new NullPointerException("getDimensionIDFromWorld wasn't able to get the WorldWrapper for the dimension " + world.getDimensionType().getDimensionName());
|
||||
|
||||
return serverWorld.getSaveFolder().toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return getServerId() + File.separatorChar + "dim_" + world.getDimensionType().getDimensionName() + File.separatorChar;
|
||||
}
|
||||
}
|
||||
|
||||
/** returns the server name, IP and game version. */
|
||||
public static String getServerId()
|
||||
{
|
||||
String serverName = MC.getCurrentServerName().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
|
||||
String serverIp = MC.getCurrentServerIp().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
|
||||
String serverMcVersion = MC.getCurrentServerVersion().replaceAll(INVALID_FILE_CHARACTERS_REGEX, "");
|
||||
|
||||
return serverName + ", IP " + serverIp + ", GameVersion " + serverMcVersion;
|
||||
}
|
||||
|
||||
|
||||
/** Convert a BlockColors int into a Color object */
|
||||
public static Color intToColor(int num)
|
||||
{
|
||||
int filter = 0b11111111;
|
||||
|
||||
int red = (num >> 16) & filter;
|
||||
int green = (num >> 8) & filter;
|
||||
int blue = num & filter;
|
||||
|
||||
return new Color(red, green, blue);
|
||||
}
|
||||
|
||||
/** Convert a Color into a BlockColors object. */
|
||||
public static int colorToInt(Color color)
|
||||
{
|
||||
return color.getRGB();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clamps the given value between the min and max values.
|
||||
* May behave strangely if min > max.
|
||||
*/
|
||||
public static int clamp(int min, int value, int max)
|
||||
{
|
||||
return Math.min(max, Math.max(value, min));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps the given value between the min and max values.
|
||||
* May behave strangely if min > max.
|
||||
*/
|
||||
public static float clamp(float min, float value, float max)
|
||||
{
|
||||
return Math.min(max, Math.max(value, min));
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamps the given value between the min and max values.
|
||||
* May behave strangely if min > max.
|
||||
*/
|
||||
public static double clamp(double min, double value, double max)
|
||||
{
|
||||
return Math.min(max, Math.max(value, min));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a HashSet of all ChunkPos within the normal render distance
|
||||
* that should not be rendered.
|
||||
*/
|
||||
public static HashSet<AbstractChunkPosWrapper> getNearbyLodChunkPosToSkip(LodDimension lodDim, AbstractBlockPosWrapper blockPosWrapper)
|
||||
{
|
||||
int chunkRenderDist = MC_RENDER.getRenderDistance();
|
||||
AbstractChunkPosWrapper centerChunk = FACTORY.createChunkPos(blockPosWrapper);
|
||||
|
||||
int skipRadius;
|
||||
VanillaOverdraw overdraw = CONFIG.client().graphics().advancedGraphics().getVanillaOverdraw();
|
||||
HorizontalResolution drawRes = CONFIG.client().graphics().quality().getDrawResolution();
|
||||
|
||||
// apply distance based rules for dynamic overdraw
|
||||
if (overdraw == VanillaOverdraw.DYNAMIC
|
||||
&& chunkRenderDist <= MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW)
|
||||
{
|
||||
// The vanilla render distance isn't far enough
|
||||
// for partial skipping to make sense...
|
||||
if (!lodDim.dimension.hasCeiling() && (drawRes == HorizontalResolution.BLOCK))
|
||||
{
|
||||
// ...and the dimension is open, so we don't have to worry about
|
||||
// LODs rendering on top of the player,
|
||||
// and the user is using a high horizontal resolution,
|
||||
// so the overdraw shouldn't be noticeable
|
||||
overdraw = VanillaOverdraw.ALWAYS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ...but we are underground, so we don't want
|
||||
// LODs rendering on top of the player,
|
||||
// Or the user is using a LOW horizontal resolution
|
||||
// and overdraw would be very noticeable.
|
||||
overdraw = VanillaOverdraw.NEVER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// determine the skipping type based
|
||||
// on the overdraw type
|
||||
switch (overdraw)
|
||||
{
|
||||
case ALWAYS:
|
||||
// don't skip any positions
|
||||
return new HashSet<>();
|
||||
|
||||
case DYNAMIC:
|
||||
|
||||
if (chunkRenderDist > MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW
|
||||
&& chunkRenderDist <= MINIMUM_RENDER_DISTANCE_FOR_FAR_OVERDRAW)
|
||||
{
|
||||
// This is a small render distance (but greater than the minimum partial
|
||||
// distance), skip positions that are greater than 2/3 the render distance
|
||||
skipRadius = (int) Math.ceil(chunkRenderDist * (2.0/3.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is a large render distance. Skip positions that are greater than
|
||||
// 4/5ths the render distance
|
||||
skipRadius = (int) Math.ceil(chunkRenderDist * (4.0 / 5.0));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
case BORDER:
|
||||
case NEVER:
|
||||
// skip chunks in render distance that are rendered
|
||||
// by vanilla minecraft
|
||||
skipRadius = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// get the chunks that are going to be rendered by Minecraft
|
||||
HashSet<AbstractChunkPosWrapper> posToSkip = MC_RENDER.getRenderedChunks();
|
||||
|
||||
|
||||
// remove everything outside the skipRadius,
|
||||
// if the skipRadius is being used
|
||||
if (skipRadius != 0)
|
||||
{
|
||||
for (int x = centerChunk.getX() - chunkRenderDist; x < centerChunk.getX() + chunkRenderDist; x++)
|
||||
{
|
||||
for (int z = centerChunk.getZ() - chunkRenderDist; z < centerChunk.getZ() + chunkRenderDist; z++)
|
||||
{
|
||||
if (x <= centerChunk.getX() - skipRadius || x >= centerChunk.getX() + skipRadius
|
||||
|| z <= centerChunk.getZ() - skipRadius || z >= centerChunk.getZ() + skipRadius)
|
||||
posToSkip.remove(FACTORY.createChunkPos(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
return posToSkip;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method find if a given chunk is a border chunk of the renderable ones
|
||||
* @param vanillaRenderedChunks matrix of the vanilla rendered chunks
|
||||
* @param x relative (to the matrix) x chunk to check
|
||||
* @param z relative (to the matrix) z chunk to check
|
||||
* @return true if and only if the chunk is a border of the renderable chunks
|
||||
*/
|
||||
public static boolean isBorderChunk(boolean[][] vanillaRenderedChunks, int x, int z)
|
||||
{
|
||||
if (x < 0 || z < 0 || x >= vanillaRenderedChunks.length || z >= vanillaRenderedChunks[0].length)
|
||||
return false;
|
||||
int tempX;
|
||||
int tempZ;
|
||||
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
{
|
||||
tempX = x + Box.DIRECTION_NORMAL_MAP.get(lodDirection).x;
|
||||
tempZ = z + Box.DIRECTION_NORMAL_MAP.get(lodDirection).z;
|
||||
if (vanillaRenderedChunks[x][z] || (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length)
|
||||
&& !vanillaRenderedChunks[tempX][tempZ]))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** This is copied from Minecraft's MathHelper class */
|
||||
public static float fastInvSqrt(float numb)
|
||||
{
|
||||
float half = 0.5F * numb;
|
||||
int i = Float.floatToIntBits(numb);
|
||||
i = 1597463007 - (i >> 1);
|
||||
numb = Float.intBitsToFloat(i);
|
||||
return numb * (1.5F - half * numb * numb);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class takes care of dependency injection
|
||||
* for singletons.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public class SingletonHandler
|
||||
{
|
||||
private static final Map<Class<?>, Object> singletons = new HashMap<Class<?>, Object>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Adds the given singleton so it can be referenced later.
|
||||
*
|
||||
* @param objectClass
|
||||
* @param singletonReference
|
||||
* @throws IllegalStateException
|
||||
*/
|
||||
public static void bind(Class<?> interfaceClass, Object singletonReference) throws IllegalStateException
|
||||
{
|
||||
// make sure we haven't already bound this singleton
|
||||
if (singletons.containsKey(interfaceClass))
|
||||
{
|
||||
throw new IllegalStateException("The singleton [" + interfaceClass.getSimpleName() + "] has already been bound.");
|
||||
}
|
||||
|
||||
|
||||
// make sure the given singleton implements the interface
|
||||
boolean singletonImplementsInterface = false;
|
||||
for (Class<?> singletonInterface : singletonReference.getClass().getInterfaces())
|
||||
{
|
||||
if (singletonInterface.equals(interfaceClass))
|
||||
{
|
||||
singletonImplementsInterface = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!singletonImplementsInterface)
|
||||
{
|
||||
throw new IllegalStateException("The singleton [" + interfaceClass.getSimpleName() + "] doesn't implement the interface [" + interfaceClass.getSimpleName() + "].");
|
||||
}
|
||||
|
||||
|
||||
singletons.put(interfaceClass, singletonReference);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a singleton of type T
|
||||
* if one has been bound.
|
||||
*
|
||||
* @param <T> class of the singleton
|
||||
* @param objectClass class of the singleton, but as a parameter!
|
||||
* @return the singleton of type T
|
||||
* @throws NullPointerException if no singleton of type T has been bound.
|
||||
* @throws ClassCastException if the singleton isn't able to be cast to type T. (this shouldn't normally happen, unless the bound object changed somehow)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T get(Class<T> objectClass) throws NullPointerException, ClassCastException
|
||||
{
|
||||
// throw an error if the given singleton doesn't exist.
|
||||
if (!singletons.containsKey(objectClass))
|
||||
{
|
||||
throw new NullPointerException("The singleton [" + objectClass.getSimpleName() + "] was never bound. If you are calling [bind], make sure it is happening before you call [get].");
|
||||
}
|
||||
|
||||
|
||||
return (T) singletons.get(objectClass);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.objects.Box;
|
||||
|
||||
/**
|
||||
* Holds data used by specific threads so
|
||||
* the data doesn't have to be recreated every
|
||||
* time it is needed.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @version 9-25-2021
|
||||
*/
|
||||
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[]> projectionArrayMap = 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<>();
|
||||
|
||||
|
||||
//________________________//
|
||||
// used in BufferBuilder //
|
||||
//________________________//
|
||||
|
||||
public static final ConcurrentMap<String, boolean[]> adjShadeDisabled = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, Map<LodDirection, long[]>> adjDataMap = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentMap<String, Box> boxMap = new ConcurrentHashMap<>();
|
||||
|
||||
|
||||
|
||||
/** returns the array NOT cleared every time */
|
||||
public static boolean[] getAdjShadeDisabledArray()
|
||||
{
|
||||
if (!adjShadeDisabled.containsKey(Thread.currentThread().getName())
|
||||
|| (adjShadeDisabled.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
adjShadeDisabled.put(Thread.currentThread().getName(), new boolean[Box.DIRECTIONS.length]);
|
||||
}
|
||||
Arrays.fill(adjShadeDisabled.get(Thread.currentThread().getName()), false);
|
||||
return adjShadeDisabled.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
/** returns the array NOT cleared every time */
|
||||
public static Map<LodDirection, long[]> getAdjDataArray(int verticalData)
|
||||
{
|
||||
if (!adjDataMap.containsKey(Thread.currentThread().getName())
|
||||
|| (adjDataMap.get(Thread.currentThread().getName()) == null)
|
||||
|| (adjDataMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH) == null)
|
||||
|| (adjDataMap.get(Thread.currentThread().getName()).get(LodDirection.NORTH).length != verticalData))
|
||||
{
|
||||
adjDataMap.put(Thread.currentThread().getName(), new HashMap<>());
|
||||
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.UP, new long[1]);
|
||||
adjDataMap.get(Thread.currentThread().getName()).put(LodDirection.DOWN, new long[1]);
|
||||
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
adjDataMap.get(Thread.currentThread().getName()).put(lodDirection, new long[verticalData]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (LodDirection lodDirection : Box.ADJ_DIRECTIONS)
|
||||
Arrays.fill(adjDataMap.get(Thread.currentThread().getName()).get(lodDirection), DataPointUtil.EMPTY_DATA);
|
||||
}
|
||||
return adjDataMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
public static Box getBox()
|
||||
{
|
||||
if (!boxMap.containsKey(Thread.currentThread().getName())
|
||||
|| (boxMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
boxMap.put(Thread.currentThread().getName(), new Box());
|
||||
}
|
||||
boxMap.get(Thread.currentThread().getName()).reset();
|
||||
return boxMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
//________________________//
|
||||
// used in DataPointUtil //
|
||||
// mergeVerticalData //
|
||||
//________________________//
|
||||
|
||||
|
||||
//________________________//
|
||||
// used in DataPointUtil //
|
||||
// mergeSingleData //
|
||||
//________________________//
|
||||
|
||||
|
||||
|
||||
/** returns the array filled with 0's */
|
||||
public static long[] getBuilderVerticalArray(int detailLevel)
|
||||
{
|
||||
if (!threadBuilderVerticalArrayMap.containsKey(Thread.currentThread().getName()) || (threadBuilderVerticalArrayMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
long[][] array = new long[5][];
|
||||
int size;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
size = 1 << i;
|
||||
array[i] = new long[size * size * (DataPointUtil.worldHeight / 2 + 1)];
|
||||
}
|
||||
threadBuilderVerticalArrayMap.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
Arrays.fill(threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel], 0);
|
||||
return threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel];
|
||||
}
|
||||
|
||||
/** returns the array NOT cleared every time */
|
||||
public static byte[] getSaveContainer(int detailLevel)
|
||||
{
|
||||
if (!saveContainer.containsKey(Thread.currentThread().getName()) || (saveContainer.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
byte[][] array = new byte[LodUtil.DETAIL_OPTIONS][];
|
||||
int size = 1;
|
||||
for (int i = LodUtil.DETAIL_OPTIONS - 1; i >= 0; i--)
|
||||
{
|
||||
array[i] = new byte[2 + 8 * size * size * DetailDistanceUtil.getMaxVerticalData(i)];
|
||||
size = size << 1;
|
||||
}
|
||||
saveContainer.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
//Arrays.fill(threadBuilderVerticalArrayMap.get(Thread.currentThread().getName())[detailLevel], 0);
|
||||
return saveContainer.get(Thread.currentThread().getName())[detailLevel];
|
||||
}
|
||||
|
||||
|
||||
/** returns the array filled with 0's */
|
||||
public static long[] getVerticalDataArray(int arrayLength)
|
||||
{
|
||||
if (!threadVerticalAddDataMap.containsKey(Thread.currentThread().getName()) || (threadVerticalAddDataMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
threadVerticalAddDataMap.put(Thread.currentThread().getName(), new long[arrayLength]);
|
||||
}
|
||||
else
|
||||
{
|
||||
Arrays.fill(threadVerticalAddDataMap.get(Thread.currentThread().getName()), 0);
|
||||
}
|
||||
return threadVerticalAddDataMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** returns the array NOT cleared every time */
|
||||
public static short[] getHeightAndDepth(int arrayLength)
|
||||
{
|
||||
if (!heightAndDepthMap.containsKey(Thread.currentThread().getName()) || (heightAndDepthMap.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
heightAndDepthMap.put(Thread.currentThread().getName(), new short[arrayLength]);
|
||||
}
|
||||
return heightAndDepthMap.get(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
|
||||
/** returns the array filled with 0's */
|
||||
public static long[] getVerticalUpdateArray(int detailLevel)
|
||||
{
|
||||
if (!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null))
|
||||
{
|
||||
long[][] array = new long[LodUtil.DETAIL_OPTIONS][];
|
||||
for (int i = 1; i < LodUtil.DETAIL_OPTIONS; i++)
|
||||
array[i] = new long[DetailDistanceUtil.getMaxVerticalData(i - 1) * 4];
|
||||
verticalUpdate.put(Thread.currentThread().getName(), array);
|
||||
}
|
||||
else
|
||||
{
|
||||
Arrays.fill(verticalUpdate.get(Thread.currentThread().getName())[detailLevel], 0);
|
||||
}
|
||||
return verticalUpdate.get(Thread.currentThread().getName())[detailLevel];
|
||||
}
|
||||
|
||||
/** clears all arrays so they will have to be rebuilt */
|
||||
public static void clearMaps()
|
||||
{
|
||||
adjShadeDisabled.clear();
|
||||
adjDataMap.clear();
|
||||
boxMap.clear();
|
||||
threadSingleUpdateMap.clear();
|
||||
threadBuilderArrayMap.clear();
|
||||
threadBuilderVerticalArrayMap.clear();
|
||||
threadVerticalAddDataMap.clear();
|
||||
saveContainer.clear();
|
||||
projectionArrayMap.clear();
|
||||
heightAndDepthMap.clear();
|
||||
singleDataToMergeMap.clear();
|
||||
verticalUpdate.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
/**
|
||||
* This handles creating abstract wrapper objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-18-2021
|
||||
*/
|
||||
public interface IWrapperFactory
|
||||
{
|
||||
public AbstractBlockPosWrapper createBlockPos();
|
||||
public AbstractBlockPosWrapper createBlockPos(int x, int y, int z);
|
||||
|
||||
|
||||
public AbstractChunkPosWrapper createChunkPos();
|
||||
public AbstractChunkPosWrapper createChunkPos(int x, int z);
|
||||
public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos);
|
||||
public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
|
||||
public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper);
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.block;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
|
||||
/**
|
||||
* BlockPos needs to be abstract instead of a interfaces
|
||||
* so that we can define its constructors.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public abstract class AbstractBlockPosWrapper
|
||||
{
|
||||
public AbstractBlockPosWrapper() { }
|
||||
public AbstractBlockPosWrapper(int x, int y, int z) { }
|
||||
|
||||
|
||||
|
||||
public abstract void set(int x, int y, int z);
|
||||
|
||||
public abstract int getX();
|
||||
public abstract int getY();
|
||||
public abstract int getZ();
|
||||
|
||||
public abstract int get(LodDirection.Axis axis);
|
||||
|
||||
/** returns itself */
|
||||
public abstract AbstractBlockPosWrapper offset(int x, int y, int z);
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.block;
|
||||
|
||||
/**
|
||||
* Contains methods that would have been static in BlockColorWrapper.
|
||||
* Since interfaces can't create/implement static methods we have
|
||||
* to split the object up in two.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-17-2021
|
||||
*/
|
||||
public interface IBlockColorSingletonWrapper
|
||||
{
|
||||
/** @returns the base color of water (grey) */
|
||||
public IBlockColorWrapper getWaterColor();
|
||||
}
|
||||
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.block;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-17-2021
|
||||
*/
|
||||
public interface IBlockColorWrapper
|
||||
{
|
||||
//--------------//
|
||||
//Colors getters//
|
||||
//--------------//
|
||||
|
||||
public boolean hasColor();
|
||||
|
||||
public int getColor();
|
||||
|
||||
|
||||
//------------//
|
||||
//Tint getters//
|
||||
//------------//
|
||||
|
||||
public boolean hasTint();
|
||||
|
||||
public boolean hasGrassTint();
|
||||
|
||||
public boolean hasFolliageTint();
|
||||
|
||||
public boolean hasWaterTint();
|
||||
|
||||
}
|
||||
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.block;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public interface IBlockShapeWrapper
|
||||
{
|
||||
public boolean ofBlockToAvoid();
|
||||
|
||||
//-----------------//
|
||||
//Avoidance getters//
|
||||
//-----------------//
|
||||
|
||||
public boolean isNonFull();
|
||||
|
||||
public boolean hasNoCollision();
|
||||
|
||||
public boolean isToAvoid();
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.chunk;
|
||||
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
|
||||
/**
|
||||
* This is abstract instead of a interface so
|
||||
* we can define its constructors.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-18-2021
|
||||
*/
|
||||
public abstract class AbstractChunkPosWrapper
|
||||
{
|
||||
public AbstractChunkPosWrapper(AbstractChunkPosWrapper newChunkPos) { }
|
||||
public AbstractChunkPosWrapper(AbstractBlockPosWrapper blockPos) { }
|
||||
public AbstractChunkPosWrapper(int chunkX, int chunkZ) { }
|
||||
public AbstractChunkPosWrapper() { }
|
||||
|
||||
|
||||
|
||||
public abstract int getX();
|
||||
public abstract int getZ();
|
||||
|
||||
public abstract int getMinBlockX();
|
||||
public abstract int getMinBlockZ();
|
||||
|
||||
public abstract int getRegionX();
|
||||
public abstract int getRegionZ();
|
||||
|
||||
public abstract AbstractBlockPosWrapper getWorldPosition();
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.chunk;
|
||||
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockColorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.IBlockShapeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-17-2021
|
||||
*/
|
||||
public interface IChunkWrapper
|
||||
{
|
||||
public int getHeight();
|
||||
|
||||
public boolean isPositionInWater(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public int getHeightMapValue(int xRel, int zRel);
|
||||
|
||||
public IBiomeWrapper getBiome(int xRel, int yAbs, int zRel);
|
||||
|
||||
public IBlockColorWrapper getBlockColorWrapper(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public IBlockShapeWrapper getBlockShapeWrapper(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public AbstractChunkPosWrapper getPos();
|
||||
|
||||
public boolean isLightCorrect();
|
||||
|
||||
public boolean isWaterLogged(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public int getEmittedBrightness(AbstractBlockPosWrapper blockPos);
|
||||
}
|
||||
-426
@@ -1,426 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.config;
|
||||
|
||||
import com.seibel.lod.core.enums.config.BlocksToAvoid;
|
||||
import com.seibel.lod.core.enums.config.BufferRebuildTimes;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.enums.config.GenerationPriority;
|
||||
import com.seibel.lod.core.enums.config.GpuUploadMethod;
|
||||
import com.seibel.lod.core.enums.config.HorizontalQuality;
|
||||
import com.seibel.lod.core.enums.config.HorizontalResolution;
|
||||
import com.seibel.lod.core.enums.config.HorizontalScale;
|
||||
import com.seibel.lod.core.enums.config.LodTemplate;
|
||||
import com.seibel.lod.core.enums.config.VanillaOverdraw;
|
||||
import com.seibel.lod.core.enums.config.VerticalQuality;
|
||||
import com.seibel.lod.core.enums.rendering.DebugMode;
|
||||
import com.seibel.lod.core.enums.rendering.FogDistance;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawOverride;
|
||||
import com.seibel.lod.core.objects.MinDefaultMax;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
|
||||
/**
|
||||
* This holds the config defaults, setters/getters
|
||||
* that should be hooked into the host mod loader (Fabric, Forge, etc.), and
|
||||
* the options that should be implemented in a configWrapperSingleton.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-21-2021
|
||||
*/
|
||||
public interface ILodConfigWrapperSingleton
|
||||
{
|
||||
public IClient client();
|
||||
|
||||
|
||||
public interface IClient
|
||||
{
|
||||
public IGraphics graphics();
|
||||
public IWorldGenerator worldGenerator();
|
||||
public IAdvanced advanced();
|
||||
|
||||
|
||||
//==================//
|
||||
// Graphics Configs //
|
||||
//==================//
|
||||
public interface IGraphics
|
||||
{
|
||||
public static final String DESC = "These settings control how the mod will look in game";
|
||||
|
||||
public IQuality quality();
|
||||
public IFogQuality fogQuality();
|
||||
public IAdvancedGraphics advancedGraphics();
|
||||
|
||||
|
||||
public interface IQuality
|
||||
{
|
||||
public static final String DESC = "These settings control how detailed the fake chunks will be.";
|
||||
|
||||
HorizontalResolution DRAW_RESOLUTION_DEFAULT = HorizontalResolution.BLOCK;
|
||||
public static final String DRAW_RESOLUTION_DESC = ""
|
||||
+ " What is the maximum detail fake chunks should be drawn at? \n"
|
||||
+ " " + 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";
|
||||
public HorizontalResolution getDrawResolution();
|
||||
public void setDrawResolution(HorizontalResolution newHorizontalResolution);
|
||||
|
||||
MinDefaultMax<Integer> LOD_CHUNK_RENDER_DISTANCE_MIN_DEFAULT_MAX = new MinDefaultMax<Integer>(16, 64, 1024);
|
||||
String LOD_CHUNK_RENDER_DISTANCE_DESC = ""
|
||||
+ " The mod's render distance, measured in chunks. \n";
|
||||
public int getLodChunkRenderDistance();
|
||||
public void setLodChunkRenderDistance(int newLodChunkRenderDistance);
|
||||
|
||||
VerticalQuality VERTICAL_QUALITY_DEFAULT = VerticalQuality.MEDIUM;
|
||||
String VERTICAL_QUALITY_DESC = ""
|
||||
+ " This indicates how detailed fake chunks will represent \n"
|
||||
+ " overhangs, caves, floating islands, ect. \n"
|
||||
+ " Higher options will use more memory and increase GPU usage. \n"
|
||||
+ " " + VerticalQuality.LOW + ": uses at max 2 columns per position. \n"
|
||||
+ " " + VerticalQuality.MEDIUM + ": uses at max 4 columns per position. \n"
|
||||
+ " " + VerticalQuality.HIGH + ": uses at max 8 columns per position. \n";
|
||||
public VerticalQuality getVerticalQuality();
|
||||
public void setVerticalQuality(VerticalQuality newVerticalQuality);
|
||||
|
||||
HorizontalScale HORIZONTAL_SCALE_DEFAULT = HorizontalScale.MEDIUM;
|
||||
String HORIZONTAL_SCALE_DESC = ""
|
||||
+ " This indicates how quickly fake chunks drop off in quality. \n"
|
||||
+ " " + HorizontalScale.LOW + ": quality drops every " + HorizontalScale.LOW.distanceUnit / 16 + " chunks. \n"
|
||||
+ " " + HorizontalScale.MEDIUM + ": quality drops every " + HorizontalScale.MEDIUM.distanceUnit / 16 + " chunks. \n"
|
||||
+ " " + HorizontalScale.HIGH + ": quality drops every " + HorizontalScale.HIGH.distanceUnit / 16 + " chunks. \n";
|
||||
public HorizontalScale getHorizontalScale();
|
||||
public void setHorizontalScale(HorizontalScale newHorizontalScale);
|
||||
|
||||
HorizontalQuality HORIZONTAL_QUALITY_DEFAULT = HorizontalQuality.MEDIUM;
|
||||
String HORIZONTAL_QUALITY_DESC = ""
|
||||
+ " This indicates the exponential base of the quadratic drop-off \n"
|
||||
+ " " + HorizontalQuality.LOWEST + ": base " + HorizontalQuality.LOWEST.quadraticBase + ". \n"
|
||||
+ " " + HorizontalQuality.LOW + ": base " + HorizontalQuality.LOW.quadraticBase + ". \n"
|
||||
+ " " + HorizontalQuality.MEDIUM + ": base " + HorizontalQuality.MEDIUM.quadraticBase + ". \n"
|
||||
+ " " + HorizontalQuality.HIGH + ": base " + HorizontalQuality.HIGH.quadraticBase + ". \n";
|
||||
public HorizontalQuality getHorizontalQuality();
|
||||
public void setHorizontalQuality(HorizontalQuality newHorizontalQuality);
|
||||
}
|
||||
|
||||
public interface IFogQuality
|
||||
{
|
||||
String DESC = "These settings control the fog quality.";
|
||||
|
||||
FogDistance FOG_DISTANCE_DEFAULT = FogDistance.FAR;
|
||||
String FOG_DISTANCE_DESC = ""
|
||||
+ " At what distance should Fog be drawn on the fake chunks? \n"
|
||||
+ " If the fog cuts off abruptly or you are using Optifine's \"fast\" fog option \n"
|
||||
+ " set this to " + FogDistance.NEAR + " or " + FogDistance.FAR + ". \n";
|
||||
public FogDistance getFogDistance();
|
||||
public void setFogDistance(FogDistance newFogDistance);
|
||||
|
||||
FogDrawOverride FOG_DRAW_OVERRIDE_DEFAULT = FogDrawOverride.FANCY;
|
||||
String FOG_DRAW_OVERRIDE_DESC = ""
|
||||
+ " When should fog be drawn? \n"
|
||||
+ " " + FogDrawOverride.OPTIFINE_SETTING + ": Use whatever Fog setting Optifine is using. If Optifine isn't installed this defaults to " + FogDrawOverride.FANCY + ". \n"
|
||||
+ " " + FogDrawOverride.NO_FOG + ": Never draw fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.FAST + ": Always draw fast fog on the LODs \n"
|
||||
+ " " + FogDrawOverride.FANCY + ": Always draw fancy fog on the LODs (if your graphics card supports it) \n";
|
||||
public FogDrawOverride getFogDrawOverride();
|
||||
public void setFogDrawOverride(FogDrawOverride newFogDrawOverride);
|
||||
|
||||
boolean DISABLE_VANILLA_FOG_DEFAULT = false;
|
||||
String DISABLE_VANILLA_FOG_DESC = ""
|
||||
+ " If true disable Minecraft's fog. \n\n"
|
||||
+ ""
|
||||
+ " Experimental! May cause issues with Sodium. \n\n"
|
||||
+ ""
|
||||
+ " Unlike Optifine or Sodium's fog disabling option this won't change \n"
|
||||
+ " performance (we don't actually disable the fog, we just tell it to render a infinite distance away). \n"
|
||||
+ " May or may not play nice with other mods that edit fog. \n";
|
||||
public boolean getDisableVanillaFog();
|
||||
public void setDisableVanillaFog(boolean newDisableVanillaFog);
|
||||
}
|
||||
|
||||
public interface IAdvancedGraphics
|
||||
{
|
||||
String DESC = "Graphics options that are a bit more technical.";
|
||||
|
||||
LodTemplate LOD_TEMPLATE_DEFAULT = LodTemplate.CUBIC;
|
||||
String LOD_TEMPLATE_DESC = ""
|
||||
+ " How should the LODs be drawn? \n"
|
||||
+ " NOTE: Currently only " + LodTemplate.CUBIC + " is implemented! \n"
|
||||
+ " \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 each other, \n"
|
||||
+ " " + " unless a neighboring chunk is at a significantly different height. \n";
|
||||
public LodTemplate getLodTemplate();
|
||||
public void setLodTemplate(LodTemplate newLodTemplate);
|
||||
|
||||
boolean DISABLE_DIRECTIONAL_CULLING_DEFAULT = false;
|
||||
String DISABLE_DIRECTIONAL_CULLING_DESC = ""
|
||||
+ " If false fake chunks 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 disappearing. \n"
|
||||
+ " (Which may happen if you are using a camera mod) \n";
|
||||
public boolean getDisableDirectionalCulling();
|
||||
public void setDisableDirectionalCulling(boolean newDisableDirectionalCulling);
|
||||
|
||||
boolean ALWAYS_DRAW_AT_MAD_QUALITY_DEFAULT = false;
|
||||
String ALWAYS_DRAW_AT_MAD_QUALITY_DESC = ""
|
||||
+ " Disable quality falloff, \n"
|
||||
+ " all fake chunks will be drawn at the highest \n"
|
||||
+ " available detail level. \n\n"
|
||||
+ " "
|
||||
+ " WARNING: \n"
|
||||
+ " This could cause a Out Of Memory crash on render \n"
|
||||
+ " distances higher than 128 \n";
|
||||
public boolean getAlwaysDrawAtMaxQuality();
|
||||
public void setAlwaysDrawAtMaxQuality(boolean newAlwaysDrawAtMaxQuality);
|
||||
|
||||
VanillaOverdraw VANILLA_OVERDRAW_DEFAULT = VanillaOverdraw.DYNAMIC;
|
||||
String VANILLA_OVERDRAW_DESC = ""
|
||||
+ " How often should LODs be drawn on top of regular chunks? \n"
|
||||
+ " HALF and ALWAYS will prevent holes in the world, but may look odd for transparent blocks or in caves. \n\n"
|
||||
+ " " + VanillaOverdraw.NEVER + ": LODs won't render on top of vanilla chunks. \n"
|
||||
+ " " + VanillaOverdraw.BORDER + ": LODs will render only on the border of vanilla chunks preventing only some holes in the world. \n"
|
||||
+ " " + VanillaOverdraw.DYNAMIC + ": LODs will render on top of distant vanilla chunks to hide delayed loading. \n"
|
||||
+ " " + " More effective on higher render distances. \n"
|
||||
+ " " + " For vanilla render distances less than or equal to " + LodUtil.MINIMUM_RENDER_DISTANCE_FOR_PARTIAL_OVERDRAW + " \n"
|
||||
+ " " + " " + VanillaOverdraw.NEVER + " or " + VanillaOverdraw.ALWAYS + " may be used depending on the dimension. \n"
|
||||
+ " " + VanillaOverdraw.ALWAYS + ": LODs will render on all vanilla chunks preventing holes in the world. \n";
|
||||
public VanillaOverdraw getVanillaOverdraw();
|
||||
public void setVanillaOverdraw(VanillaOverdraw newVanillaOverdraw);
|
||||
|
||||
GpuUploadMethod GPU_UPLOAD_METHOD_DEFAULT = GpuUploadMethod.BUFFER_STORAGE;
|
||||
String GPU_UPLOAD_METHOD_DESC = ""
|
||||
+ " What method should be used to upload geometry to the GPU? \n"
|
||||
+ " Listed in the suggested order of best to worst. \n\n"
|
||||
+ ""
|
||||
+ " " + GpuUploadMethod.BUFFER_STORAGE + ": Default if OpenGL 4.5 is supported. Fast rendering, no stuttering. \n"
|
||||
+ " " + GpuUploadMethod.SUB_DATA + ": Default if OpenGL 4.5 is NOT supported. Fast rendering but may stutter when uploading. \n"
|
||||
+ " " + GpuUploadMethod.DATA + ": Fast rendering but will stutter when uploading. \n"
|
||||
+ " " + GpuUploadMethod.BUFFER_MAPPING + ": Slow rendering but won't stutter when uploading. Possibly better than " + GpuUploadMethod.SUB_DATA + " if using a integrated GPU. \n";
|
||||
public GpuUploadMethod getGpuUploadMethod();
|
||||
public void setGpuUploadMethod(GpuUploadMethod newDisableVanillaFog);
|
||||
|
||||
boolean USE_EXTENDED_NEAR_CLIP_PLANE_DEFAULT = false;
|
||||
String USE_EXTENDED_NEAR_CLIP_PLANE_DESC = ""
|
||||
+ " Will prevent some overdraw issues, but may cause nearby fake chunks to render incorrectly \n"
|
||||
+ " especially when in/near an ocean. \n";
|
||||
public boolean getUseExtendedNearClipPlane();
|
||||
public void setUseExtendedNearClipPlane(boolean newUseExtendedNearClipPlane);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//========================//
|
||||
// WorldGenerator Configs //
|
||||
//========================//
|
||||
public interface IWorldGenerator
|
||||
{
|
||||
String DESC = "These settings control how fake chunks outside your normal view range are generated.";
|
||||
|
||||
GenerationPriority GENERATION_PRIORITY_DEFAULT = GenerationPriority.FAR_FIRST;
|
||||
String GENERATION_PRIORITY_DESC = ""
|
||||
+ " " + GenerationPriority.FAR_FIRST + " \n"
|
||||
+ " LODs are generated from low to high detail \n"
|
||||
+ " with a small priority for far away regions. \n"
|
||||
+ " This fills in the world fastest. \n\n"
|
||||
+ ""
|
||||
+ " " + GenerationPriority.NEAR_FIRST + " \n"
|
||||
+ " LODs are generated around the player \n"
|
||||
+ " in a spiral, similar to vanilla minecraft. \n";
|
||||
public GenerationPriority getGenerationPriority();
|
||||
public void setGenerationPriority(GenerationPriority newGenerationPriority);
|
||||
|
||||
DistanceGenerationMode DISTANCE_GENERATION_MODE_DEFAULT = DistanceGenerationMode.SURFACE;
|
||||
String DISTANCE_GENERATION_MODE_DESC = ""
|
||||
+ " Note: The times listed here are the amount of time it took \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 + " \n"
|
||||
+ " Don't run the distance generator. \n\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 + " \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"
|
||||
+ " use predetermined heights to simulate having height data. \n"
|
||||
+ " Multithreaded - Fastest (2-5 ms) \n\n"
|
||||
+ ""
|
||||
+ " " + DistanceGenerationMode.SURFACE + " \n"
|
||||
+ " Generate the world surface, \n"
|
||||
+ " this does NOT include trees, \n"
|
||||
+ " or structures. \n"
|
||||
+ " Multithreaded - Faster (10-20 ms) \n\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.FULL + " \n"
|
||||
+ " Ask the local server to generate/load each chunk. \n"
|
||||
+ " This will show player made structures, which can \n"
|
||||
+ " be useful if you are adding the mod to a pre-existing world. \n"
|
||||
+ " This is the most compatible, but causes server/simulation lag. \n"
|
||||
+ " SingleThreaded - Slow (15-50 ms, with spikes up to 200 ms) \n";
|
||||
public DistanceGenerationMode getDistanceGenerationMode();
|
||||
public void setDistanceGenerationMode(DistanceGenerationMode newDistanceGenerationMode);
|
||||
|
||||
boolean ALLOW_UNSTABLE_FEATURE_GENERATION_DEFAULT = false;
|
||||
String ALLOW_UNSTABLE_FEATURE_GENERATION_DESC = ""
|
||||
+ " 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"
|
||||
+ " improving stability, but decreasing how many features are \n"
|
||||
+ " actually generated. \n"
|
||||
+ " (for example: some tree generation is unstable, \n"
|
||||
+ " so some trees may not be generated.) \n"
|
||||
+ " By setting this to true, all features will be generated, \n"
|
||||
+ " but your game will be more unstable and crashes may occur. \n"
|
||||
+ " \n"
|
||||
+ " I would love to remove this option and always generate everything, \n"
|
||||
+ " but I'm not sure how to do that. \n"
|
||||
+ " If you are a Java wizard, check out the git issue here: \n"
|
||||
+ " https://gitlab.com/jeseibel/minecraft-lod-mod/-/issues/35 \n";
|
||||
public boolean getAllowUnstableFeatureGeneration();
|
||||
public void setAllowUnstableFeatureGeneration(boolean newAllowUnstableFeatureGeneration);
|
||||
|
||||
BlocksToAvoid BLOCKS_TO_AVOID_DEFAULT = BlocksToAvoid.BOTH;
|
||||
String BLOCKS_TO_AVOID_DESC = ""
|
||||
+ " " + BlocksToAvoid.NONE + ": Use all blocks when generating fake chunks \n\n"
|
||||
+ ""
|
||||
+ " " + BlocksToAvoid.NON_FULL + ": Only use full blocks when generating fake chunks (ignores slabs, lanterns, torches, grass, etc.) \n\n"
|
||||
+ ""
|
||||
+ " " + BlocksToAvoid.NO_COLLISION + ": Only use solid blocks when generating fake chunks (ignores grass, torches, etc.) \n"
|
||||
+ ""
|
||||
+ " " + BlocksToAvoid.BOTH + ": Only use full solid blocks when generating fake chunks \n";
|
||||
public BlocksToAvoid getBlocksToAvoid();
|
||||
public void setBlockToAvoid(BlocksToAvoid newBlockToAvoid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//============================//
|
||||
// AdvancedModOptions Configs //
|
||||
//============================//
|
||||
public interface IAdvanced
|
||||
{
|
||||
public static final String DESC = "Advanced mod settings";
|
||||
|
||||
public IThreading threading();
|
||||
public IDebugging debugging();
|
||||
public IBuffers buffers();
|
||||
|
||||
|
||||
public interface IThreading
|
||||
{
|
||||
String DESC = "These settings control how many CPU threads the mod uses for different tasks.";
|
||||
|
||||
MinDefaultMax<Integer> NUMBER_OF_WORLD_GENERATION_THREADS_DEFAULT
|
||||
= new MinDefaultMax<Integer>(1,
|
||||
Runtime.getRuntime().availableProcessors() / 2,
|
||||
Runtime.getRuntime().availableProcessors());
|
||||
String NUMBER_OF_WORLD_GENERATION_THREADS_DESC = ""
|
||||
+ " This is how many threads are used when generating LODs outside \n"
|
||||
+ " the normal render distance. \n"
|
||||
+ " If you experience stuttering when generating distant LODs, decrease \n"
|
||||
+ " this number. If you want to increase LOD generation speed, \n"
|
||||
+ " increase this number. \n\n"
|
||||
+ ""
|
||||
+ " The maximum value is the number of logical processors on your CPU. \n"
|
||||
+ " Requires a restart to take effect. \n";
|
||||
public int getNumberOfWorldGenerationThreads();
|
||||
public void setNumberOfWorldGenerationThreads(int newNumberOfWorldGenerationThreads);
|
||||
|
||||
MinDefaultMax<Integer> NUMBER_OF_BUFFER_BUILDER_THREADS_MIN_DEFAULT_MAX
|
||||
= new MinDefaultMax<Integer>(1,
|
||||
Runtime.getRuntime().availableProcessors() / 2,
|
||||
Runtime.getRuntime().availableProcessors());
|
||||
String NUMBER_OF_BUFFER_BUILDER_THREADS_DESC = ""
|
||||
+ " This is how many threads are used when building vertex buffers \n"
|
||||
+ " (The things sent to your GPU to draw the fake chunks). \n"
|
||||
+ " If you experience high CPU usage when NOT generating distant \n"
|
||||
+ " fake chunks, lower this number. \n"
|
||||
+ " \n"
|
||||
+ " The maximum value is the number of logical processors on your CPU. \n"
|
||||
+ " Requires a restart to take effect. \n";
|
||||
public int getNumberOfBufferBuilderThreads();
|
||||
public void setNumberOfBufferBuilderThreads(int newNumberOfWorldBuilderThreads);
|
||||
}
|
||||
|
||||
public interface IDebugging
|
||||
{
|
||||
String DESC = "These settings can be used to look for bugs, or see how certain aspects of the mod work.";
|
||||
|
||||
boolean DRAW_LODS_DEFAULT = true;
|
||||
String DRAW_LODS_DESC = ""
|
||||
+ " If true, the mod is enabled and fake chunks will be drawn. \n"
|
||||
+ " If false, the mod will still generate fake chunks, \n"
|
||||
+ " but they won't be rendered. \n";
|
||||
public boolean getDrawLods();
|
||||
public void setDrawLods(boolean newDrawLods);
|
||||
|
||||
DebugMode DEBUG_MODE_DEFAULT = DebugMode.OFF;
|
||||
String DEBUG_MODE_DESC = ""
|
||||
+ " " + DebugMode.OFF + ": Fake chunks will be drawn with their normal colors. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL + ": Fake chunks color will be based on their detail level. \n"
|
||||
+ " " + DebugMode.SHOW_DETAIL_WIREFRAME + ": Fake chunks color will be based on their detail level, drawn as a wireframe. \n";
|
||||
public DebugMode getDebugMode();
|
||||
public void setDebugMode(DebugMode newDebugMode);
|
||||
|
||||
boolean DEBUG_KEYBINDINGS_ENABLED_DEFAULT = true;
|
||||
String DEBUG_KEYBINDINGS_ENABLED_DESC = ""
|
||||
+ " 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.";
|
||||
public boolean getDebugKeybindingsEnabled();
|
||||
public void setDebugKeybindingsEnabled(boolean newEnableDebugKeybindings);
|
||||
}
|
||||
|
||||
public interface IBuffers
|
||||
{
|
||||
String DESC = "These settings affect how often geometry is rebuilt.";
|
||||
|
||||
String REBUILD_TIMES_DESC = ""
|
||||
+ " How frequently should geometry be rebuilt and sent to the GPU? \n"
|
||||
+ " Higher settings may cause stuttering, but will prevent holes in the world \n";
|
||||
BufferRebuildTimes REBUILD_TIMES_DEFAULT = BufferRebuildTimes.NORMAL;
|
||||
public BufferRebuildTimes getRebuildTimes();
|
||||
public void setRebuildTimes(BufferRebuildTimes newBufferRebuildTimes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.minecraft;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
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.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
|
||||
/**
|
||||
* Contains everything related to
|
||||
* rendering in Minecraft.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-18-2021
|
||||
*/
|
||||
public interface IMinecraftRenderWrapper
|
||||
{
|
||||
public Vec3f getLookAtVector();
|
||||
|
||||
public AbstractBlockPosWrapper getCameraBlockPosition();
|
||||
|
||||
public boolean playerHasBlindnessEffect();
|
||||
|
||||
public Vec3d getCameraExactPosition();
|
||||
|
||||
public Mat4f getDefaultProjectionMatrix(float partialTicks);
|
||||
|
||||
public double getGamma();
|
||||
|
||||
public double getFov(float partialTicks);
|
||||
|
||||
/** Measured in chunks */
|
||||
public int getRenderDistance();
|
||||
|
||||
public int getScreenWidth();
|
||||
public int getScreenHeight();
|
||||
|
||||
/**
|
||||
* This method returns the ChunkPos of all chunks that Minecraft
|
||||
* is going to render this frame.
|
||||
*/
|
||||
public HashSet<AbstractChunkPosWrapper> getRenderedChunks();
|
||||
}
|
||||
-157
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.minecraft;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.seibel.lod.core.enums.LodDirection;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* Contains everything related to the Minecraft object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 9-16-2021
|
||||
*/
|
||||
public interface IMinecraftWrapper
|
||||
{
|
||||
//================//
|
||||
// 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>
|
||||
* <p>
|
||||
* LightMaps and other time sensitive objects fall in this category. <br> <br>
|
||||
* <p>
|
||||
* This doesn't affect OpenGL objects in any way.
|
||||
*/
|
||||
public void clearFrameObjectCache();
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// method wrappers //
|
||||
//=================//
|
||||
|
||||
public float getShade(LodDirection lodDirection);
|
||||
|
||||
public boolean hasSinglePlayerServer();
|
||||
|
||||
public String getCurrentServerName();
|
||||
public String getCurrentServerIp();
|
||||
public String getCurrentServerVersion();
|
||||
|
||||
/** Returns the dimension the player is currently in */
|
||||
public IDimensionTypeWrapper getCurrentDimension();
|
||||
|
||||
public String getCurrentDimensionId();
|
||||
|
||||
/** This texture changes every frame */
|
||||
ILightMapWrapper getCurrentLightMap();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// Simple gets //
|
||||
//=============//
|
||||
|
||||
public boolean playerExists();
|
||||
|
||||
public AbstractBlockPosWrapper getPlayerBlockPos();
|
||||
|
||||
public AbstractChunkPosWrapper getPlayerChunkPos();
|
||||
|
||||
/**
|
||||
* Attempts to get the ServerWorld for the dimension
|
||||
* the user is currently in.
|
||||
* @returns null if no ServerWorld is available
|
||||
*/
|
||||
public IWorldWrapper getWrappedServerWorld();
|
||||
|
||||
public IWorldWrapper getWrappedClientWorld();
|
||||
|
||||
public File getGameDirectory();
|
||||
|
||||
public IProfilerWrapper getProfiler();
|
||||
|
||||
public float getSkyDarken(float partialTicks);
|
||||
|
||||
boolean connectedToServer();
|
||||
|
||||
/** Returns all worlds available to the server */
|
||||
public ArrayList<IWorldWrapper> getAllServerWorlds();
|
||||
|
||||
|
||||
|
||||
public void sendChatMessage(String string);
|
||||
|
||||
/**
|
||||
* Crashes Minecraft, displaying the given errorMessage <br> <br>
|
||||
* In the following format: <br>
|
||||
*
|
||||
* The game crashed whilst <strong>errorMessage</strong> <br>
|
||||
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
|
||||
* Exit Code: -1 <br>
|
||||
*/
|
||||
public void crashMinecraft(String errorMessage, Throwable exception);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.minecraft;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public interface IProfilerWrapper
|
||||
{
|
||||
public void push(String newSection);
|
||||
|
||||
public void popPush(String newSection);
|
||||
|
||||
public void pop();
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.misc;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public interface ILightMapWrapper
|
||||
{
|
||||
public int getLightValue(int skyLight, int blockLight);
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.world;
|
||||
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
|
||||
/**
|
||||
* Contains everything related to biome colors.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-15-2021
|
||||
*/
|
||||
public interface IBiomeColorWrapperSingleton
|
||||
{
|
||||
public IBiomeColorWrapperSingleton getInstance();
|
||||
|
||||
public int getGrassColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
public int getWaterColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
public int getFoliageColor(IWorldWrapper world, AbstractBlockPosWrapper blockPos);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.world;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-15-2021
|
||||
*/
|
||||
public interface IBiomeWrapper
|
||||
{
|
||||
/** Returns a color int for the given biome. */
|
||||
public int getColorForBiome(int x, int z);
|
||||
|
||||
public int getGrassTint(int x, int z);
|
||||
|
||||
public int getFolliageTint();
|
||||
|
||||
public int getWaterTint();
|
||||
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.world;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 11-15-2021
|
||||
*/
|
||||
public interface IDimensionTypeWrapper
|
||||
{
|
||||
public String getDimensionName();
|
||||
|
||||
public boolean hasCeiling();
|
||||
|
||||
public boolean hasSkyLight();
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.world;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.seibel.lod.core.enums.WorldType;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
|
||||
/**
|
||||
* Can be either a Server world or a Client world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public interface IWorldWrapper
|
||||
{
|
||||
public IDimensionTypeWrapper getDimensionType();
|
||||
|
||||
public WorldType getWorldType();
|
||||
|
||||
public int getBlockLight(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public int getSkyLight(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public IBiomeWrapper getBiome(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
public boolean hasCeiling();
|
||||
|
||||
public boolean hasSkyLight();
|
||||
|
||||
public boolean isEmpty();
|
||||
|
||||
public int getHeight();
|
||||
|
||||
public int getSeaLevel();
|
||||
|
||||
/** @throws UnsupportedOperationException if the WorldWrapper isn't for a ServerWorld */
|
||||
public File getSaveFolder() throws UnsupportedOperationException;
|
||||
|
||||
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly 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.core.wrapperInterfaces.worldGeneration;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This is used for generating chunks
|
||||
* in a variety of detail and threading levels.
|
||||
* <p>
|
||||
* Abstract instead of a interface so
|
||||
* we can define its constructors.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public abstract class AbstractWorldGeneratorWrapper
|
||||
{
|
||||
public AbstractWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { }
|
||||
|
||||
|
||||
public abstract void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode);
|
||||
|
||||
public abstract void generateSurface(AbstractChunkPosWrapper pos);
|
||||
|
||||
public abstract void generateFeatures(AbstractChunkPosWrapper pos);
|
||||
|
||||
public abstract void generateFull(AbstractChunkPosWrapper pos);
|
||||
}
|
||||
Reference in New Issue
Block a user