Add LODs generating outside the player's view distance
It isn't fast enough to keep up with flying creating or spectator players; but it does function without causing heavy server or client lag. LODs are generated in lines starting far away from the player and moving towards them, in the future they should be generated close to the player first. Also add a RegionPos object and a way to convert from ChunkPos objects in LodUtils
This commit is contained in:
+3
-1
@@ -3,7 +3,9 @@ buildscript {
|
||||
maven { url = 'https://files.minecraftforge.net/maven' }
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url='https://dist.creeper.host/Sponge/maven' }
|
||||
maven { url = 'https://repo.spongepowered.org/maven/' }
|
||||
// potential replacement in case of problems:
|
||||
// https://dist.creeper.host/Sponge/maven
|
||||
}
|
||||
dependencies {
|
||||
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '3.+', changing: true
|
||||
|
||||
@@ -18,7 +18,7 @@ import net.minecraft.world.chunk.ChunkSection;
|
||||
* and color data for an LOD object.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-13-2021
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class LodChunk
|
||||
{
|
||||
@@ -57,7 +57,9 @@ public class LodChunk
|
||||
public Color colors[];
|
||||
|
||||
|
||||
|
||||
/** If true that means this LodChunk is just a placeholder and
|
||||
* no LOD has been generated for this chunk location */
|
||||
private boolean emptyPlaceholder = false;
|
||||
|
||||
|
||||
|
||||
@@ -73,6 +75,8 @@ public class LodChunk
|
||||
*/
|
||||
public LodChunk()
|
||||
{
|
||||
emptyPlaceholder = true;
|
||||
|
||||
x = 0;
|
||||
z = 0;
|
||||
|
||||
@@ -104,6 +108,8 @@ public class LodChunk
|
||||
* 5,8, 4,4,4,4, 0,0,0,0, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255, 255,255,255,
|
||||
*/
|
||||
|
||||
emptyPlaceholder = false;
|
||||
|
||||
// make sure there are the correct number of entries
|
||||
// in the data string (28)
|
||||
int count = 0;
|
||||
@@ -215,6 +221,7 @@ public class LodChunk
|
||||
throw new IllegalArgumentException("LodChunk constructor given a null world");
|
||||
}
|
||||
|
||||
emptyPlaceholder = false;
|
||||
|
||||
x = chunk.getPos().x;
|
||||
z = chunk.getPos().z;
|
||||
@@ -261,6 +268,10 @@ public class LodChunk
|
||||
// world height is 256)
|
||||
ChunkSection[] chunkSections = chunk.getSections();
|
||||
|
||||
// if this LodChunk was a empltyPlaceholder before
|
||||
// it will hold some data after this method's completion,
|
||||
// make sure it is handled like a normal LodChunk
|
||||
emptyPlaceholder = false;
|
||||
|
||||
|
||||
int startX = 0;
|
||||
@@ -694,12 +705,23 @@ public class LodChunk
|
||||
//================//
|
||||
|
||||
/**
|
||||
* If this LOD is either invisible from every
|
||||
* direction or doesn't have a valid height
|
||||
* it is empty.
|
||||
* Returns true if this LodChunk is an emptyPlaceholder
|
||||
*/
|
||||
public boolean isPlaceholder()
|
||||
{
|
||||
return emptyPlaceholder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this LOD is either invisible
|
||||
* from every direction, doesn't have a valid height,
|
||||
* or is an emptyPlaceholder.
|
||||
*/
|
||||
public boolean isLodEmpty()
|
||||
{
|
||||
if (emptyPlaceholder)
|
||||
return true;
|
||||
|
||||
for(LodCorner corner : LodCorner.values())
|
||||
if(top[corner.value] != -1 || bottom[corner.value] != -1)
|
||||
// at least one corner is valid
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.backsun.lod.objects;
|
||||
import com.backsun.lod.handlers.LodDimensionFileHandler;
|
||||
import com.backsun.lod.util.LodUtils;
|
||||
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
|
||||
@@ -11,7 +12,7 @@ import net.minecraft.world.server.ServerChunkProvider;
|
||||
* for a given dimension.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-23-2021
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class LodDimension
|
||||
{
|
||||
@@ -207,39 +208,35 @@ public class LodDimension
|
||||
*/
|
||||
public void addLod(LodChunk lod)
|
||||
{
|
||||
int regionX = lod.x / LodRegion.SIZE;
|
||||
int regionZ = lod.z / LodRegion.SIZE;
|
||||
|
||||
// prevent issues if X/Z is negative and less than 16
|
||||
if (lod.x < 0)
|
||||
{
|
||||
regionX = (Math.abs(regionX) * -1) - 1;
|
||||
}
|
||||
if (lod.z < 0)
|
||||
{
|
||||
regionZ = (Math.abs(regionZ) * -1) - 1;
|
||||
}
|
||||
RegionPos pos = LodUtils.convertChunkPosToRegionPos(new ChunkPos(lod.x, lod.z));
|
||||
|
||||
// don't continue if the region can't be saved
|
||||
if (!regionIsInRange(regionX, regionZ))
|
||||
if (!regionIsInRange(pos.x, pos.z))
|
||||
{
|
||||
System.out.println(pos.x + " " + pos.z + " out of range");
|
||||
return;
|
||||
|
||||
LodRegion region = getRegion(regionX, regionZ);
|
||||
}
|
||||
|
||||
LodRegion region = getRegion(pos.x, pos.z);
|
||||
|
||||
if (region == null)
|
||||
{
|
||||
// if no region exists, create it
|
||||
region = new LodRegion(regionX, regionZ);
|
||||
region = new LodRegion(pos.x, pos.z);
|
||||
setRegion(region);
|
||||
}
|
||||
|
||||
region.addLod(lod);
|
||||
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (regionX - centerX) + halfWidth;
|
||||
int zIndex = (regionZ - centerZ) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
fileHandler.saveDirtyRegionsToFileAsync();
|
||||
// don't save empty place holders to disk
|
||||
if (!lod.isPlaceholder())
|
||||
{
|
||||
// mark the region as dirty so it will be saved to disk
|
||||
int xIndex = (pos.x - centerX) + halfWidth;
|
||||
int zIndex = (pos.z - centerZ) + halfWidth;
|
||||
isRegionDirty[xIndex][zIndex] = true;
|
||||
fileHandler.saveDirtyRegionsToFileAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,20 +248,9 @@ public class LodDimension
|
||||
*/
|
||||
public LodChunk getLodFromCoordinates(int chunkX, int chunkZ)
|
||||
{
|
||||
int regionX = chunkX / LodRegion.SIZE;
|
||||
int regionZ = chunkZ / LodRegion.SIZE;
|
||||
RegionPos pos = LodUtils.convertChunkPosToRegionPos(new ChunkPos(chunkX, chunkZ));
|
||||
|
||||
// prevent issues if chunkX/Z is negative and less than width
|
||||
if (chunkX < 0)
|
||||
{
|
||||
regionX = (Math.abs(regionX) * -1) - 1;
|
||||
}
|
||||
if (chunkZ < 0)
|
||||
{
|
||||
regionZ = (Math.abs(regionZ) * -1) - 1;
|
||||
}
|
||||
|
||||
LodRegion region = getRegion(regionX, regionZ);
|
||||
LodRegion region = getRegion(pos.x, pos.z);
|
||||
|
||||
if(region == null)
|
||||
return null;
|
||||
@@ -287,7 +273,7 @@ public class LodDimension
|
||||
* Returns whether the region at the given X and Z coordinates
|
||||
* is within the loaded range.
|
||||
*/
|
||||
private boolean regionIsInRange(int regionX, int regionZ)
|
||||
public boolean regionIsInRange(int regionX, int regionZ)
|
||||
{
|
||||
int xIndex = (regionX - centerX) + halfWidth;
|
||||
int zIndex = (regionZ - centerZ) + halfWidth;
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.backsun.lod.objects;
|
||||
|
||||
/**
|
||||
* This object is similar to ChunkPos or BlockPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class RegionPos
|
||||
{
|
||||
public int x;
|
||||
public int z;
|
||||
|
||||
|
||||
/**
|
||||
* Default Constructor <br>
|
||||
*
|
||||
* Sets x and z to 0
|
||||
*/
|
||||
public RegionPos()
|
||||
{
|
||||
x = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
public RegionPos(int newX, int newZ)
|
||||
{
|
||||
x = newX;
|
||||
z = newZ;
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
* and is the starting point for most of this program.
|
||||
*
|
||||
* @author James_Seibel
|
||||
* @version 02-23-2021
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class ClientProxy
|
||||
{
|
||||
@@ -35,10 +35,11 @@ public class ClientProxy
|
||||
private LodBuilder lodBuilder;
|
||||
Minecraft mc = Minecraft.getInstance();
|
||||
|
||||
|
||||
public ClientProxy()
|
||||
{
|
||||
lodBuilder = new LodBuilder();
|
||||
renderer = new LodRenderer();
|
||||
renderer = new LodRenderer(lodBuilder);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,7 +93,6 @@ public class ClientProxy
|
||||
return;
|
||||
|
||||
|
||||
|
||||
// offset the regions
|
||||
double playerX = mc.player.getPosX();
|
||||
double playerZ = mc.player.getPosZ();
|
||||
@@ -113,11 +113,13 @@ public class ClientProxy
|
||||
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// lod creation events //
|
||||
//=====================//
|
||||
|
||||
// TODO add on chunk changed event
|
||||
// issue #10
|
||||
|
||||
@SubscribeEvent
|
||||
public void chunkLoadEvent(ChunkEvent.Load event)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2020.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package com.backsun.lod.proxy;
|
||||
|
||||
import com.backsun.lod.builders.LodBuilder;
|
||||
import com.backsun.lod.objects.LodDimension;
|
||||
import com.backsun.lod.objects.LodRegion;
|
||||
import com.backsun.lod.renderer.LodRenderer;
|
||||
import com.backsun.lod.util.LodUtils;
|
||||
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
import net.minecraftforge.common.WorldWorkerManager.IWorker;
|
||||
|
||||
/**
|
||||
* This is used to generate a LodChunk at a given ChunkPos.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class SingleLodChunkGenWorker implements IWorker
|
||||
{
|
||||
private ServerWorld serverWorld;
|
||||
private ChunkPos pos;
|
||||
private LodDimension lodDim;
|
||||
private LodBuilder lodBuilder;
|
||||
private LodRenderer lodRenderer;
|
||||
|
||||
public SingleLodChunkGenWorker(ChunkPos newPos, LodRenderer newLodRenderer, LodBuilder newLodBuilder, LodDimension newLodDimension)
|
||||
{
|
||||
serverWorld = LodUtils.getServerWorldFromDimension(newLodDimension.dimension);
|
||||
pos = newPos;
|
||||
lodDim = newLodDimension;
|
||||
lodBuilder = newLodBuilder;
|
||||
lodRenderer = newLodRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWork()
|
||||
{
|
||||
return pos != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doWork()
|
||||
{
|
||||
if (pos != null)
|
||||
{
|
||||
int x = pos.x;
|
||||
int z = pos.z;
|
||||
|
||||
// only generate LodChunks if they can
|
||||
// be added to the current LodDimension
|
||||
if (lodDim.regionIsInRange(pos.x / LodRegion.SIZE, pos.z / LodRegion.SIZE))
|
||||
{
|
||||
IChunk chunk = serverWorld.getChunk(x, z, ChunkStatus.EMPTY, true);
|
||||
|
||||
chunk = serverWorld.getChunk(x, z, ChunkStatus.FULL);
|
||||
|
||||
lodBuilder.generateLodChunkAsync((Chunk) chunk);
|
||||
// this is called so that the new LOD chunk is drawn
|
||||
// after it is generated
|
||||
lodRenderer.regenerateLODsNextFrame();
|
||||
|
||||
// useful for debugging
|
||||
//if (lodDim.getLodFromCoordinates(x, z) != null)
|
||||
// System.out.println(x + " " + z + " Success!");
|
||||
//else
|
||||
// System.out.println(x + " " + z);
|
||||
}
|
||||
// can be used for debugging
|
||||
//else
|
||||
//{
|
||||
// System.out.println("Out of range " + x + " " + z);
|
||||
//}
|
||||
|
||||
pos = null;
|
||||
}
|
||||
|
||||
if (pos == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,13 @@ import java.util.concurrent.Executors;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import com.backsun.lod.builders.LodBufferBuilder;
|
||||
import com.backsun.lod.builders.LodBuilder;
|
||||
import com.backsun.lod.handlers.ReflectionHandler;
|
||||
import com.backsun.lod.objects.LodChunk;
|
||||
import com.backsun.lod.objects.LodDimension;
|
||||
import com.backsun.lod.objects.NearFarBuffer;
|
||||
import com.backsun.lod.objects.NearFarFogSetting;
|
||||
import com.backsun.lod.proxy.SingleLodChunkGenWorker;
|
||||
import com.backsun.lod.util.LodConfig;
|
||||
import com.backsun.lod.util.enums.ColorDirection;
|
||||
import com.backsun.lod.util.enums.FogDistance;
|
||||
@@ -36,18 +38,20 @@ import net.minecraft.client.renderer.vertex.VertexFormat;
|
||||
import net.minecraft.potion.Effects;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
import net.minecraftforge.common.WorldWorkerManager;
|
||||
|
||||
|
||||
/**
|
||||
* This is where all the magic happens.
|
||||
* This is where all the magic happens. <br>
|
||||
* This is where LODs are draw to the world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2-27-2021
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class LodRenderer
|
||||
{
|
||||
@@ -112,17 +116,18 @@ public class LodRenderer
|
||||
* and are waiting to be swapped with the drawable buffers*/
|
||||
private volatile boolean switchBuffers = false;
|
||||
|
||||
private LodBuilder lodBuilder;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public LodRenderer()
|
||||
public LodRenderer(LodBuilder newLodBuilder)
|
||||
{
|
||||
mc = Minecraft.getInstance();
|
||||
gameRender = mc.gameRenderer;
|
||||
|
||||
reflectionHandler = new ReflectionHandler();
|
||||
lodBuilder = newLodBuilder;
|
||||
}
|
||||
|
||||
|
||||
@@ -215,8 +220,6 @@ public class LodRenderer
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// create the LODs //
|
||||
//=================//
|
||||
@@ -542,6 +545,14 @@ public class LodRenderer
|
||||
// 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()
|
||||
{
|
||||
regen = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Returns -1 if there are no valid points
|
||||
@@ -622,7 +633,7 @@ public class LodRenderer
|
||||
int chunkZ = j + (startZ / LodChunk.WIDTH);
|
||||
|
||||
LodChunk lod = lodDimension.getLodFromCoordinates(chunkX, chunkZ);
|
||||
if (lod == null)
|
||||
if (lod == null || lod.isLodEmpty())
|
||||
{
|
||||
// note: for some reason if any color or lod objects are set here
|
||||
// it causes the game to use 100% gpu;
|
||||
@@ -631,17 +642,22 @@ public class LodRenderer
|
||||
colorArray[i][j] = null;
|
||||
lodArray[i][j] = null;
|
||||
|
||||
// This is something I would like to have done someday
|
||||
// but currently world generation is too slow to have it
|
||||
// here, maybe it could be put in a loop
|
||||
// that happens every tick for a specific number of chunks?
|
||||
// LodChunk tmpLod = new LodChunk();
|
||||
// tmpLod.x = chunkX;
|
||||
// tmpLod.z = chunkZ;
|
||||
// lodDimension.addLod(tmpLod);
|
||||
//
|
||||
// world.getChunkProvider().getChunk(chunkX, chunkZ, true);
|
||||
// System.out.println(chunkX + "," + chunkZ);
|
||||
if (lod == null)
|
||||
{
|
||||
// no LOD exists for this location,
|
||||
// have the server generate one when it is
|
||||
// convenient
|
||||
|
||||
// add a placeholder LOD so we don't try to
|
||||
// generate the same chunk multiple times
|
||||
LodChunk placeholder = new LodChunk();
|
||||
placeholder.x = chunkX;
|
||||
placeholder.z = chunkZ;
|
||||
lodDimension.addLod(placeholder);
|
||||
|
||||
SingleLodChunkGenWorker genWorker = new SingleLodChunkGenWorker(new ChunkPos(chunkX, chunkZ), this, lodBuilder, lodDimension);
|
||||
WorldWorkerManager.addWorker(genWorker);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
@@ -685,6 +701,7 @@ public class LodRenderer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generate our new buildable buffers
|
||||
NearFarBuffer nearFarBuffers = lodBufferBuilder.createBuffers(
|
||||
buildableNearBuffer, buildableFarBuffer,
|
||||
@@ -694,7 +711,7 @@ public class LodRenderer
|
||||
buildableNearBuffer = nearFarBuffers.nearBuffer;
|
||||
buildableFarBuffer = nearFarBuffers.farBuffer;
|
||||
|
||||
// mark the buildable buffers as ready to swap
|
||||
// mark that the buildable buffers as ready to swap
|
||||
regenerating = false;
|
||||
switchBuffers = true;
|
||||
});
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package com.backsun.lod.util;
|
||||
|
||||
import com.backsun.lod.objects.LodRegion;
|
||||
import com.backsun.lod.objects.RegionPos;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.server.integrated.IntegratedServer;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.DimensionType;
|
||||
import net.minecraft.world.server.ServerWorld;
|
||||
|
||||
@@ -9,7 +13,7 @@ import net.minecraft.world.server.ServerWorld;
|
||||
* This class holds methods that may be used in multiple places.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 02-26-2021
|
||||
* @version 03-19-2021
|
||||
*/
|
||||
public class LodUtils
|
||||
{
|
||||
@@ -60,4 +64,26 @@ public class LodUtils
|
||||
|
||||
return returnWorld;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given ChunkPos into a RegionPos.
|
||||
*/
|
||||
public static RegionPos convertChunkPosToRegionPos(ChunkPos pos)
|
||||
{
|
||||
RegionPos rPos = new RegionPos();
|
||||
rPos.x = pos.x / LodRegion.SIZE;
|
||||
rPos.z = pos.z / LodRegion.SIZE;
|
||||
|
||||
// prevent issues if X/Z is negative and less than 16
|
||||
if (pos.x < 0)
|
||||
{
|
||||
rPos.x = (Math.abs(rPos.x) * -1) - 1;
|
||||
}
|
||||
if (pos.z < 0)
|
||||
{
|
||||
rPos.z = (Math.abs(rPos.z) * -1) - 1;
|
||||
}
|
||||
|
||||
return rPos;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user