Bring over improvements and changes from the master branch

This commit is contained in:
James Seibel
2021-02-26 10:31:34 -06:00
parent cb04b2df09
commit cfda8c9655
19 changed files with 1379 additions and 608 deletions
@@ -15,6 +15,10 @@ import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
/**
* Initialize and setup the Mod.
* <br>
* If you are looking for the real start of the mod
* check out the ClientProxy.
*
* @author James Seibel
* @version 02-07-2021
@@ -0,0 +1,223 @@
package com.backsun.lod.builders;
import java.awt.Color;
import java.util.concurrent.Callable;
import org.lwjgl.opengl.GL11;
import com.backsun.lod.objects.NearFarBuffer;
import com.backsun.lod.util.enums.FogDistance;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.util.math.AxisAlignedBB;
/**
* This object is used to create NearFarBuffer objects
* in a thread independent way, so multiple of these objects can be
* created and executed in parallel to populate BufferBuilders.
*
* @author James Seibel
* @version 02-23-2021
*/
public class BuildBufferThread implements Callable<NearFarBuffer>
{
public BufferBuilder nearBuffer;
public BufferBuilder farBuffer;
public FogDistance distanceMode;
public AxisAlignedBB[][] lods;
public Color[][] colors;
private int startLodIndex = 0;
private int endLodIndex = -1;
public BuildBufferThread()
{
}
public BuildBufferThread(BufferBuilder newNearBufferBuilder,
BufferBuilder newFarBufferBuilder, AxisAlignedBB[][] newLods,
Color[][] newColors, FogDistance newDistanceMode, int newStartingIndex,
int numberOfRowsToGenerate)
{
setNewData(newNearBufferBuilder, newFarBufferBuilder, distanceMode,
newLods, newColors, newStartingIndex, numberOfRowsToGenerate);
}
public void setNewData(BufferBuilder newNearBufferBuilder,
BufferBuilder newFarBufferBuilder, FogDistance newDistanceMode,
AxisAlignedBB[][] newLods, Color[][] newColors,
int newStartingIndex, int numberOfRowsToGenerate)
{
nearBuffer = newNearBufferBuilder;
farBuffer = newFarBufferBuilder;
distanceMode = newDistanceMode;
lods = newLods;
colors = newColors;
startLodIndex = newStartingIndex;
endLodIndex = newStartingIndex + numberOfRowsToGenerate;
}
@Override
public NearFarBuffer call()
{
nearBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
farBuffer.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_COLOR);
int numbChunksWide = lods.length;
BufferBuilder currentBuffer;
AxisAlignedBB bb;
int red;
int green;
int blue;
int alpha;
// this is done if the FogDistance is either
// NEAR or FAR, if it is NEAR_AND_FAR
// the buffer is determined for each LOD
if (distanceMode == FogDistance.NEAR)
{
currentBuffer = nearBuffer;
}
else // if (distanceMode == FogDistance.FAR)
{
currentBuffer = farBuffer;
}
// x axis
for (int i = startLodIndex; i < endLodIndex; i++)
{
// z axis
for (int j = 0; j < numbChunksWide; j++)
{
if (lods[i][j] == null || colors[i][j] == null)
continue;
bb = lods[i][j];
// get the color of this LOD object
red = colors[i][j].getRed();
green = colors[i][j].getGreen();
blue = colors[i][j].getBlue();
alpha = colors[i][j].getAlpha();
if (distanceMode == FogDistance.NEAR_AND_FAR)
{
if (isCoordinateInNearFogArea(i, j, numbChunksWide / 2))
currentBuffer = nearBuffer;
else
currentBuffer = farBuffer;
}
if (bb.minY != bb.maxY)
{
// top (facing up)
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
// bottom (facing down)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
// south (facing -Z)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
// north (facing +Z)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
// west (facing -X)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.maxY, bb.minZ, red, green, blue, alpha);
// east (facing +X)
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.maxY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
}
else
{
// render this LOD as one block thick
// top (facing up)
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
// bottom (facing down)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
// south (facing -Z)
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
// north (facing +Z)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
// west (facing -X)
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.minX, bb.minY+1, bb.minZ, red, green, blue, alpha);
// east (facing +X)
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.minZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY+1, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.maxZ, red, green, blue, alpha);
addPosAndColor(currentBuffer, bb.maxX, bb.minY, bb.minZ, red, green, blue, alpha);
}
} // z axis
} // x axis
nearBuffer.finishDrawing();
farBuffer.finishDrawing();
return new NearFarBuffer(nearBuffer, farBuffer);
}
private void addPosAndColor(BufferBuilder buffer, double x, double y, double z, int red, int green, int blue, int alpha)
{
buffer.pos(x, y, z).color(red, green, blue, alpha).endVertex();
}
/**
* 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).
*/
private static boolean isCoordinateInNearFogArea(int chunkX, int chunkZ, int lodRadius)
{
int halfRadius = lodRadius / 2;
return (chunkX >= lodRadius - halfRadius
&& chunkX <= lodRadius + halfRadius)
&&
(chunkZ >= lodRadius - halfRadius
&& chunkZ <= lodRadius + halfRadius);
}
}
@@ -0,0 +1,132 @@
package com.backsun.lod.builders;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.backsun.lod.handlers.LodDimensionFileHandler;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.LodWorld;
import com.backsun.lod.util.LodUtils;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
/**
* This object is in charge of creating Lod
* related objects.
* (specifically: Lod World, Dimension, Region, and Chunk objects)
*
* @author James Seibel
* @version 2-22-2021
*/
public class LodBuilder
{
private ExecutorService lodGenThreadPool = Executors.newSingleThreadExecutor();
public volatile LodWorld lodWorld;
/** Default size of any LOD regions we use */
public int regionWidth = 5;
public LodBuilder()
{
}
/**
* Returns LodWorld so that it can be passed
* to the LodRenderer.
*/
public LodWorld generateLodChunkAsync(Chunk chunk)
{
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
// (Minecraft often gives back empty
// or null chunks in this method)
if (chunk == null || !isValidChunk(chunk))
return lodWorld;
DimensionType dim = chunk.getWorld().getDimensionType();
World world = LodUtils.getServerWorldFromDimension(dim);
if (world == null)
return lodWorld;
Thread thread = new Thread(() ->
{
try
{
LodChunk lod = new LodChunk(chunk, world);
LodDimension lodDim;
if (lodWorld == null)
{
lodWorld = new LodWorld(LodDimensionFileHandler.getWorldName());
}
else
{
// if we have a lodWorld make sure
// it is for this minecraft world
if (!lodWorld.worldName.equals(LodDimensionFileHandler.getWorldName()))
{
// this lodWorld isn't for this minecraft world
// delete it so we can get a new one
lodWorld = null;
// skip this frame
// we'll get this set up next time
return;
}
}
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, regionWidth);
lodWorld.addLodDimension(lodDim);
}
else
{
lodDim = lodWorld.getLodDimension(dim);
}
lodDim.addLod(lod);
}
catch(IllegalArgumentException | NullPointerException e)
{
// 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);
return lodWorld;
}
/**
* Return whether the given chunk
* has any data in it.
*/
public boolean isValidChunk(Chunk chunk)
{
ChunkSection[] blockStorage = chunk.getSections();
for(ChunkSection section : blockStorage)
{
if(section != null && !section.isEmpty())
{
return true;
}
}
return false;
}
}
@@ -1,4 +1,4 @@
package com.backsun.lod.util;
package com.backsun.lod.handlers;
import java.io.BufferedReader;
import java.io.File;
@@ -11,8 +11,10 @@ import java.util.concurrent.Executors;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.LodRegion;
import com.backsun.lod.util.LodUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.world.server.ServerWorld;
/**
* This object handles creating LodRegions
@@ -22,26 +24,29 @@ import net.minecraft.client.Minecraft;
* @author James Seibel
* @version 01-30-2021
*/
public class LodFileHandler
public class LodDimensionFileHandler
{
// TODO this object needs to be changed to use NBT data instead of writing to files
private static final boolean IMPLEMENTED = false;
private Minecraft mc = Minecraft.getInstance();
private LodDimension loadedRegion = null;
private LodDimension loadedDimension = null;
public long regionLastWriteTime[][];
private ExecutorService fileWritingThreadPool = Executors.newSingleThreadExecutor();
private File saveFolder;
private String saveDir;
private final String FILE_NAME_PREFIX = "lod";
private final String FILE_EXTENSION = ".txt";
private ExecutorService fileWritingThreadPool = Executors.newFixedThreadPool(1);
public LodFileHandler(LodDimension newLoadedRegion)
public LodDimensionFileHandler(File newSaveFolder, LodDimension newLoadedDimension)
{
loadedRegion = newLoadedRegion;
saveFolder = newSaveFolder;
loadedDimension = newLoadedDimension;
// these two variable are used in sync with the LodDimension
regionLastWriteTime = new long[loadedRegion.getWidth()][loadedRegion.getWidth()];
for(int i = 0; i < loadedRegion.getWidth(); i++)
for(int j = 0; j < loadedRegion.getWidth(); j++)
regionLastWriteTime = new long[loadedDimension.getWidth()][loadedDimension.getWidth()];
for(int i = 0; i < loadedDimension.getWidth(); i++)
for(int j = 0; j < loadedDimension.getWidth(); j++)
regionLastWriteTime[i][j] = -1;
}
@@ -61,15 +66,15 @@ public class LodFileHandler
*/
public LodRegion loadRegionFromFile(int regionX, int regionZ)
{
if (!IMPLEMENTED)
return null;
// we don't currently support reading or writing
// files when connected to a server
if (!mc.isIntegratedServerRunning())
if (!Minecraft.getInstance().isIntegratedServerRunning())
return null;
String fileName = "";
if (!readyToReadAndWrite())
return null;
String fileName = getFileNameForRegion(regionX, regionZ);
File f = new File(fileName);
@@ -131,47 +136,53 @@ public class LodFileHandler
// Save to File //
//==============//
public synchronized void saveDirtyRegionsToFile()
/**
* Save all dirty regions in this LodDimension to file.
*/
public synchronized void saveDirtyRegionsToFileAsync()
{
if (!IMPLEMENTED)
return;
// we don't currently support reading or writing
// files when connected to a server
if (!mc.isIntegratedServerRunning())
if (!Minecraft.getInstance().isIntegratedServerRunning())
return;
if (!readyToReadAndWrite())
// we aren't ready to read and write yet
return;
fileWritingThreadPool.execute(saveDirtyRegionsThread);
}
private Thread saveDirtyRegionsThread = new Thread(() ->
{
for(int i = 0; i < loadedRegion.getWidth(); i++)
for(int i = 0; i < loadedDimension.getWidth(); i++)
{
for(int j = 0; j < loadedRegion.getWidth(); j++)
for(int j = 0; j < loadedDimension.getWidth(); j++)
{
if(loadedRegion.isRegionDirty[i][j])
if(loadedDimension.isRegionDirty[i][j])
{
saveRegionToDisk(loadedRegion.regions[i][j]);
loadedRegion.isRegionDirty[i][j] = false;
saveRegionToDisk(loadedDimension.regions[i][j]);
loadedDimension.isRegionDirty[i][j] = false;
}
}
}
});
/**
* Save a specific region to disk.<br>
* Note: it will save to the LodDimension that this
* handler is associated with.
*/
private void saveRegionToDisk(LodRegion region)
{
if (!IMPLEMENTED)
if (!readyToReadAndWrite() || region == null)
return;
if (region == null)
return;
// convert chunk coordinates to region
// coordinates
int x = region.x;
int z = region.z;
// int x = region.x;
// int z = region.z;
File f = new File("");
File f = new File(getFileNameForRegion(x, z));
try
{
@@ -207,6 +218,33 @@ public class LodFileHandler
//================//
/**
* 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 ready to read and write.
*/
private String getFileNameForRegion(int regionX, int regionZ)
{
if (!readyToReadAndWrite())
return null;
return saveDir + "\\lod_data\\DIM" + loadedDimension.dimension.toString() + "\\" +
FILE_NAME_PREFIX + "." + regionX + "." + regionZ + FILE_EXTENSION;
}
/**
* Returns if this FileHandler is ready to read
* and write files.
* <br>
* This returns true when the world save directory is known.
*/
public boolean readyToReadAndWrite()
{
return saveFolder != null;
}
/**
@@ -220,7 +258,11 @@ public class LodFileHandler
if(mc.isIntegratedServerRunning())
{
return mc.getIntegratedServer().getName();
ServerWorld world = LodUtils.getFirstValidServerWorld();
if(world != null)
return world.getServer().getDataDirectory().toString();
return "";
}
else
{
@@ -229,4 +271,26 @@ public class LodFileHandler
}
/**
* Gets the canonical path to the world save folder.
* <br>
* Returns null if there was an IO Exception
*/
private String getWorldSaveDirectory()
{
ServerWorld world = LodUtils.getFirstValidServerWorld();
try
{
if(world != null)
return world.getServer().getDataDirectory().getCanonicalPath();
}
catch (IOException e)
{
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,99 @@
package com.backsun.lod.handlers;
import java.lang.reflect.Field;
import com.backsun.lod.util.enums.FogQuality;
import net.minecraft.client.Minecraft;
/**
* This object is used to get variables from methods
* where they are private. Specifically the fog setting
* in Optifine.
*
* @author James Seibel
* @version 09-21-2020
*/
public class ReflectionHandler
{
private Minecraft mc = Minecraft.getInstance();
public Field ofFogField = null;
public ReflectionHandler()
{
setupFogField();
}
/**
* Similar to setupFovMethod.
*/
private void setupFogField()
{
// get every variable from the entity renderer
Field[] vars = mc.gameSettings.getClass().getDeclaredFields();
// try and find the ofFogType variable in gameSettings
for(Field f : vars)
{
if(f.getName().equals("ofFogType"))
{
ofFogField = f;
return;
}
}
// we didn't find the field,
// either optifine isn't installed, or
// optifine changed the name of the variable
ofFogField = null;
}
/**
* Get what type of fog optifine is currently set to render.
*/
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(mc.gameSettings);
}
catch (IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
switch (returnNum)
{
case 0:
return FogQuality.FAST;
case 1:
return FogQuality.FAST;
case 2:
return FogQuality.FANCY;
case 3:
return FogQuality.OFF;
default:
return FogQuality.FAST;
}
}
}
@@ -3,6 +3,7 @@ package com.backsun.lod.objects;
import java.awt.Color;
import com.backsun.lod.util.enums.ColorDirection;
import com.backsun.lod.util.enums.LodCorner;
import com.backsun.lod.util.enums.LodLocation;
import net.minecraft.block.Blocks;
@@ -71,7 +72,7 @@ public class LodChunk
//==============//
/**
* Create an empty LodChunk
* Create an empty invisible LodChunk at (0,0)
*/
public LodChunk()
{
@@ -200,11 +201,11 @@ public class LodChunk
}
/**
* Illegal argument is thrown if either the
* chunk or world is null. The reason the world
* can't be null is because it's required to determine
* a block's color.
* @throws IllegalArgumentException
* Creates a LodChunk for a chunk in the given world. <br>
* Note: The world is required to determine each block's color
*
* @throws IllegalArgumentException
* thrown if either the chunk or world is null.
*/
public LodChunk(Chunk chunk, World world) throws IllegalArgumentException
{
@@ -251,6 +252,8 @@ public class LodChunk
/**
* Generate the height for the given LodLocation, either the top or bottom.
* <br><br>
* If invalid/null/empty chunks are given
* crashes may occur.
*/
@@ -316,7 +319,16 @@ public class LodChunk
else
return determineBottomPoint(chunkSections, startX, endX, startZ, endZ);
}
/** GENERATE_TOP, GENERATE_BOTTOM */
private enum SectionGenerationMode
{
GENERATE_TOP,
GENERATE_BOTTOM;
}
/**
* Find the lowest valid point from the bottom.
*/
private short determineBottomPoint(ChunkSection[] chunkSections, int startX, int endX, int startZ, int endZ)
{
// search from the bottom up
@@ -333,15 +345,16 @@ public class LodChunk
// LOD point
return (short) (y + (i * CHUNK_DATA_HEIGHT));
}
} // y
} // data
}
}
// we never found a valid LOD point
return -1;
}
/**
* Find the highest valid point from the Top
*/
private short determineTopPoint(ChunkSection[] chunkSections, int startX, int endX, int startZ, int endZ)
{
// search from the top down
@@ -357,10 +370,8 @@ public class LodChunk
// LOD point
return (short) (y + (i * CHUNK_DATA_HEIGHT));
}
} // y
} // data
}
}
// we never found a valid LOD point
return -1;
@@ -410,9 +421,11 @@ public class LodChunk
return false;
}
private Color generateLodColorSection(Chunk chunk, World world, ColorDirection colorDir)
/**
* Generate the color of the given ColorDirection at the given chunk
* in the given world.
*/
private Color generateLodColorSection(Chunk chunk, World world, ColorDirection colorDir)
{
Minecraft mc = Minecraft.getInstance();
BlockColors bc = mc.getBlockColors();
@@ -439,10 +452,17 @@ public class LodChunk
}
/**
* Only accepts TOP and BOTTOM as ColorPositions
* Generates the color of the top or bottom of a given chunk in the given world.
*
* @throws IllegalArgumentException if given a ColorDirection other than TOP or BOTTOM
*/
private Color generateLodColorVertical(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
{
if(colorDir != ColorDirection.TOP && colorDir != ColorDirection.BOTTOM)
{
throw new IllegalArgumentException("generateLodColorVertical only accepts the ColorDirection TOP or BOTTOM");
}
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
@@ -517,9 +537,19 @@ public class LodChunk
return new Color(red, green, blue);
}
/**
* Generates the color of the side of a given chunk in the given world for the given ColorDirection.
*
* @throws IllegalArgumentException if given a ColorDirection other than N, S, W, E (North, South, East, West)
*/
private Color generateLodColorHorizontal(Chunk chunk, ColorDirection colorDir, World world, BlockColors bc)
{
if(colorDir != ColorDirection.N && colorDir != ColorDirection.S && colorDir != ColorDirection.E && colorDir != ColorDirection.W)
{
throw new IllegalArgumentException("generateLodColorHorizontal only accepts the ColorDirection N (North), S (South), E (East), or W (West)");
}
ChunkSection[] chunkSections = chunk.getSections();
int numbOfBlocks = 0;
@@ -669,6 +699,30 @@ public class LodChunk
//================//
// misc functions //
//================//
/**
* If this LOD is either invisible from every
* direction or doesn't have a valid height
* it is empty.
*/
public boolean isLodEmpty()
{
for(LodCorner corner : LodCorner.values())
if(top[corner.value] != -1 || bottom[corner.value] != -1)
// at least one corner is valid
return false;
Color invisible = new Color(0,0,0,0);
for(ColorDirection dir : ColorDirection.values())
if(!colors[dir.value].equals(invisible))
// at least one direction has a non-invisible color
return false;
return true;
}
@@ -725,28 +779,7 @@ public class LodChunk
s += "x: " + x + " z: " + z + "\t";
// s += "top: ";
// for(int i = 0; i < top.length; i++)
// {
// s += top[i] + " ";
// }
// s += "\t";
// s += "bottom: ";
// for(int i = 0; i < bottom.length; i++)
// {
// s += bottom[i] + " ";
// }
// s += "\t";
// s += "colors ";
// for(int i = 0; i < colors.length; i++)
// {
// if(colors[i] != null)
// s += "(" + colors[i].getRed() + ", " + colors[i].getGreen() + ", " + colors[i].getBlue() + "), ";
// }
s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + "), ";
s += "(" + colors[ColorDirection.TOP.value].getRed() + ", " + colors[ColorDirection.TOP.value].getGreen() + ", " + colors[ColorDirection.TOP.value].getBlue() + ")";
return s;
}
@@ -1,21 +1,23 @@
package com.backsun.lod.objects;
import com.backsun.lod.util.LodFileHandler;
import com.backsun.lod.handlers.LodDimensionFileHandler;
import com.backsun.lod.util.LodUtils;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerChunkProvider;
/**
* This object holds all loaded LOD regions
* for a given dimension.
*
* @author James Seibel
* @version 01-31-2021
* @version 02-23-2021
*/
public class LodDimension
{
public final DimensionType dimension;
private volatile int width; // if this ever changes make sure to update the halfWidth too
private volatile int width;
private volatile int halfWidth;
public LodRegion regions[][];
@@ -24,15 +26,16 @@ public class LodDimension
private int centerX;
private int centerZ;
private LodFileHandler rfHandler;
private LodDimensionFileHandler fileHandler;
public LodDimension(DimensionType newDimension, int newMaxWidth)
{
dimension = newDimension;
width = newMaxWidth;
// dimension 0 works here since we are just looking for the save handler anyway
rfHandler = new LodFileHandler(this);
ServerChunkProvider provider = LodUtils.getServerWorldFromDimension(newDimension).getChunkProvider();
fileHandler = new LodDimensionFileHandler(provider.getSavedData().folder, this);
regions = new LodRegion[width][width];
isRegionDirty = new boolean[width][width];
@@ -49,7 +52,10 @@ public class LodDimension
}
/**
* Move the center of this LodDimension and move all owned
* regions over by the given x and z offset.
*/
public void move(int xOffset, int zOffset)
{
// if the x or z offset is equal to or greater than
@@ -143,22 +149,16 @@ public class LodDimension
}
public int getCenterX()
{
return centerX;
}
public int getCenterZ()
{
return centerZ;
}
/**
* 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 regionX, int regionZ)
{
int xIndex = (regionX - centerX) + halfWidth;
@@ -200,11 +200,15 @@ public class LodDimension
/**
* Add the given LOD to this dimension at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
public void addLod(LodChunk lod)
{
int regionX = (lod.x + centerX) / LodRegion.SIZE;
int regionZ = (lod.z + centerZ) / LodRegion.SIZE;
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)
@@ -235,20 +239,20 @@ public class LodDimension
int xIndex = (regionX - centerX) + halfWidth;
int zIndex = (regionZ - centerZ) + halfWidth;
isRegionDirty[xIndex][zIndex] = true;
rfHandler.saveDirtyRegionsToFile();
fileHandler.saveDirtyRegionsToFileAsync();
}
/**
* Returns null if the LodChunk isn't loaded
* Get the LodChunk 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 LodChunk getLodFromCoordinates(int chunkX, int chunkZ)
{
// (chunkX + centerX) % width
int regionX = (chunkX + centerX) / LodRegion.SIZE;
int regionZ = (chunkZ + centerZ) / LodRegion.SIZE;
int regionX = chunkX / LodRegion.SIZE;
int regionZ = chunkZ / LodRegion.SIZE;
// prevent issues if chunkX/Z is negative and less than width
if (chunkX < 0)
@@ -262,7 +266,6 @@ public class LodDimension
LodRegion region = getRegion(regionX, regionZ);
// TODO fix small render distances sometimes not having all regions loaded
if(region == null)
return null;
@@ -270,11 +273,13 @@ public class LodDimension
}
/**
* Get the region at the given X and Z coordinates from the
* RegionFileHandler.
*/
public LodRegion getRegionFromFile(int regionX, int regionZ)
{
return rfHandler.loadRegionFromFile(regionX, regionZ);
return fileHandler.loadRegionFromFile(regionX, regionZ);
}
@@ -292,6 +297,22 @@ public class LodDimension
public int getCenterX()
{
return centerX;
}
public int getCenterZ()
{
return centerZ;
}
public int getWidth()
{
return width;
@@ -310,6 +331,18 @@ public class LodDimension
for(int j = 0; j < width; j++)
isRegionDirty[i][j] = false;
}
@Override
public String toString()
{
String s = "";
s += "dim: " + dimension.toString() + "\t";
s += "(" + centerX + "," + centerZ + ")";
return s;
}
}
@@ -7,7 +7,7 @@ package com.backsun.lod.objects;
* one file in the file system.
*
* @author James Seibel
* @version 1-20-2021
* @version 1-22-2021
*/
public class LodRegion
{
@@ -31,6 +31,11 @@ public class LodRegion
}
/**
* Add the given LOD to this region at the coordinate
* stored in the LOD. If an LOD already exists at the given
* coordinates it will be overwritten.
*/
public void addLod(LodChunk lod)
{
// we use ABS since LODs can be negative, but if they are
@@ -43,6 +48,13 @@ public class LodRegion
chunks[xIndex][zIndex] = lod;
}
/**
* Get the LodChunk at the given X and Z coordinates
* in this region.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public LodChunk getLod(int chunkX, int chunkZ)
{
// since we add LOD's with ABS, we get them the same way
@@ -56,7 +68,9 @@ public class LodRegion
}
/**
* Returns all LodChunks in this region
*/
public LodChunk[][] getAllLods()
{
return chunks;
@@ -1,8 +1,7 @@
package com.backsun.lod.objects;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import net.minecraft.world.DimensionType;
@@ -10,7 +9,7 @@ import net.minecraft.world.DimensionType;
* This stores all LODs for a given world.
*
* @author James Seibel
* @version 01-31-2021
* @version 02-22-2021
*/
public class LodWorld
{
@@ -19,13 +18,13 @@ public class LodWorld
/**
* Key = Dimension id (as an int)
*/
private Dictionary<DimensionType, LodDimension> lodDimensions;
private Map<DimensionType, LodDimension> lodDimensions;
public LodWorld(String newWorldName)
{
worldName = newWorldName;
lodDimensions = new Hashtable<>();
lodDimensions = new Hashtable<DimensionType, LodDimension>();
}
@@ -40,12 +39,27 @@ public class LodWorld
return lodDimensions.get(dimension);
}
/**
* Resizes the max width in regions that each LodDimension
* should use.
*/
public void resizeDimensionRegionWidth(int newWidth)
{
Enumeration<DimensionType> keys = lodDimensions.keys();
for(DimensionType key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newWidth);
}
@Override
public String toString()
{
String s = "";
while(keys.hasMoreElements())
lodDimensions.get(keys.nextElement()).setRegionWidth(newWidth);
s += worldName + "\t - dimensions: ";
for(DimensionType key : lodDimensions.keySet())
s += lodDimensions.get(key).dimension.toString() + ", ";
return s;
}
}
@@ -0,0 +1,25 @@
package com.backsun.lod.objects;
import net.minecraft.client.renderer.BufferBuilder;
/**
* This object is just a replacement for an array
* to make things easier to understand in the LodRenderer
* and BuildBufferThread.
*
* @author James Seibel
* @version 02-21-2021
*/
public class NearFarBuffer
{
public BufferBuilder nearBuffer;
public BufferBuilder farBuffer;
public NearFarBuffer(BufferBuilder newNearBuffer, BufferBuilder newFarBuffer)
{
nearBuffer = newNearBuffer;
farBuffer = newFarBuffer;
}
}
@@ -1,10 +1,8 @@
package com.backsun.lod.proxy;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.lwjgl.opengl.GL11;
import com.backsun.lod.builders.LodBuilder;
import com.backsun.lod.objects.LodChunk;
import com.backsun.lod.objects.LodDimension;
import com.backsun.lod.objects.LodRegion;
@@ -12,39 +10,33 @@ import com.backsun.lod.objects.LodWorld;
import com.backsun.lod.renderer.LodRenderer;
import com.backsun.lod.renderer.RenderGlobalHook;
import com.backsun.lod.util.LodConfig;
import com.backsun.lod.util.LodFileHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.world.DimensionType;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.ChunkSection;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
//TODO Find a way to replace getIntegratedServer so this mod could be used on non-local worlds.
// Minecraft.getInstance().getIntegratedServer()
// Minecraft.getMinecraft().getIntegratedServer()
/**
* This is used by the client.
* This handles all events sent to the client,
* and is the starting point for most of this program.
*
* @author James_Seibel
* @version 01-31-2021
* @version 02-23-2021
*/
public class ClientProxy
{
private LodRenderer renderer;
private LodWorld lodWorld;
private ExecutorService lodGenThreadPool = Executors.newFixedThreadPool(1);
private LodBuilder lodBuilder;
Minecraft mc = Minecraft.getInstance();
/** Default size of any LOD regions we use */
private int regionWidth = 5;
public ClientProxy()
{
lodBuilder = new LodBuilder();
}
@@ -66,13 +58,17 @@ public class ClientProxy
GL11.glDisable(GL11.GL_STENCIL_TEST);
}
/**
* Do any setup that is required to draw LODs
* and then tell the LodRenderer to draw.
*/
public void renderLods(float partialTicks)
{
int newWidth = Math.max(4, (mc.gameSettings.renderDistanceChunks * LodChunk.WIDTH * 2) / LodRegion.SIZE);
if (lodWorld != null && regionWidth != newWidth)
if (lodWorld != null && lodBuilder.regionWidth != newWidth)
{
lodWorld.resizeDimensionRegionWidth(newWidth);
regionWidth = newWidth;
lodBuilder.regionWidth = newWidth;
// skip this frame, hopefully the lodWorld
// should have everything set up by then
@@ -86,8 +82,6 @@ public class ClientProxy
if (lodDim == null)
return;
mc.getProfiler().endSection();
mc.getProfiler().startSection("LOD");
double playerX = mc.player.getPosX();
double playerZ = mc.player.getPosZ();
@@ -100,7 +94,6 @@ public class ClientProxy
lodDim.move(xOffset, zOffset);
}
// we wait to create the renderer until the first frame
// to make sure that the EntityRenderer has
// been created, that way we can get the fovModifer
@@ -113,118 +106,41 @@ public class ClientProxy
{
renderer.drawLODs(lodDim, partialTicks, mc.getProfiler());
}
// end of profiler tracking
mc.getProfiler().endSection();
}
//===============//
// update events //
//===============//
@SubscribeEvent
public void chunkLoadEvent(ChunkEvent.Load event)
public void chunkLoadEvent(ChunkEvent event)
{
if (mc != null && event != null)
{
World world = mc.world;
if(world != null)
{
generateLodChunk((Chunk)event.getChunk());
}
}
}
private void generateLodChunk(Chunk chunk)
{
// don't try to create an LOD object
// if for some reason we aren't
// given a valid chunk object
// (Minecraft often gives back empty
// or null chunks in this method)
if (chunk == null || chunk.getWorld() == null || !isValidChunk(chunk))
return;
Thread thread = new Thread(() ->
{
try
{
DimensionType dim = chunk.getWorldForge().getDimensionType();
World world = chunk.getWorld();
LodChunk lod = new LodChunk(chunk, world);
LodDimension lodDim;
if (lodWorld == null)
{
lodWorld = new LodWorld(LodFileHandler.getWorldName());
}
else
{
// if we have a lodWorld make sure
// it is for this minecraft world
if (!lodWorld.worldName.equals(LodFileHandler.getWorldName()))
{
// this lodWorld isn't for this minecraft world
// delete it so we can get a new one
lodWorld = null;
// skip this frame
// we'll get this set up next time
return;
}
}
if (lodWorld.getLodDimension(dim) == null)
{
lodDim = new LodDimension(dim, regionWidth);
lodWorld.addLodDimension(lodDim);
}
else
{
lodDim = lodWorld.getLodDimension(dim);
}
lodDim.addLod(lod);
}
catch(IllegalArgumentException | NullPointerException e)
{
// 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);
lodWorld = lodBuilder.generateLodChunkAsync((Chunk) event.getChunk());
}
/**
* Return whether the given chunk
* has any data in it.
* this event is called whenever a chunk is created for the first time.
*/
private boolean isValidChunk(Chunk chunk)
{
ChunkSection[] sections = chunk.getSections();
for(ChunkSection section : sections)
{
if(section != null && !section.isEmpty())
{
return true;
}
}
return false;
}
// @SubscribeEvent
// public void onChunkPopulate(PopulateChunkEvent event)
// {
// Minecraft mc = Minecraft.getMinecraft();
// if (mc != null && event != null)
// {
// WorldClient world = mc.world;
//
// if(world != null)
// {
// lodWorld = lodBuilder.generateLodChunkAsync(world.getChunkFromChunkCoords(event.getChunkX(), event.getChunkZ()));
// }
// }
// }
}
@@ -0,0 +1,12 @@
package com.backsun.lod.proxy;
/**
* This handles any events sent to the server.
*
* @author James_Seibel
* @version 08-31-2020
*/
public class CommonProxy
{
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,55 @@
package com.backsun.lod.util;
import net.minecraft.client.Minecraft;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
/**
* This class holds methods that may be used in multiple places.
*
* @author James Seibel
* @version 02-26-2021
*/
public class LodUtils
{
private static Minecraft mc = Minecraft.getInstance();
/**
* Gets the first valid ServerWorld.
*
* @return null if there are no ServerWorlds
*/
public static ServerWorld getFirstValidServerWorld()
{
Iterable<ServerWorld> worlds = mc.getIntegratedServer().getWorlds();
for (ServerWorld world : worlds)
return world;
return null;
}
/**
* Gets the ServerWorld for the relevant dimension.
*
* @return null if there is no ServerWorld for the given dimension
*/
public static ServerWorld getServerWorldFromDimension(DimensionType dimension)
{
Iterable<ServerWorld> worlds = mc.getIntegratedServer().getWorlds();
ServerWorld returnWorld = null;
for (ServerWorld world : worlds)
{
if(world.getDimensionType() == dimension)
{
returnWorld = world;
break;
}
}
return returnWorld;
}
}
@@ -1,6 +1,7 @@
package com.backsun.lod.util;
/**
* This holds meta information about the mod.
*
* @author James Seibel
* @version 04-16-2020
@@ -14,7 +15,7 @@ public class Reference
/** the mod's version */
public static final String VERSION = "1.0";
/** the version of minecraft this mod is built for */
public static final String ACCEPTED_VERSIONS = "[1.12.2]";
public static final String ACCEPTED_VERSIONS = "[1.16.4]";
/** where the client proxy class is */
public static final String CLIENT_PROXY_CLASS = "com.backsun.lod.proxy.ClientProxy";
@@ -1,10 +1,10 @@
package com.backsun.lod.util.enums;
/**
* TOP, N, S, E, W, BOTTOM
*
* @author James Seibel
* @version 10-17-2020
*
* TOP, N, S, E, W, BOTTOM
*/
public enum ColorDirection
{
@@ -8,9 +8,9 @@ package com.backsun.lod.util.enums;
*/
public enum FogDistance
{
/** valid for both fast and fancy fog qualities. */
/** good for fast or fancy fog qualities. */
NEAR,
/** valid for both fast and fancy fog qualities. */
/** good for fast or fancy fog qualities. */
FAR,
/** only looks good if the fog quality is set to Fancy. */
NEAR_AND_FAR;
@@ -0,0 +1,28 @@
package com.backsun.lod.util.enums;
/**
* NE, SE, SW, NW
*
* @author James Seibel
* @version 1-20-2020
*/
public enum LodCorner
{
// used for position
/** -Z, +X */
NE(0),
/** +Z, +X */
SE(1),
/** +Z, -X */
SW(2),
/** -Z, -X */
NW(3);
public final int value;
private LodCorner(int newValue)
{
value = newValue;
}
}
@@ -1,4 +1,4 @@
# Note: to update code in eclipse run the "eclipse" commad in graldew
# Note: to update code in eclipse run the "eclipse" command in graldew
# make public the method getFOVModifier
@@ -16,6 +16,9 @@ public net.minecraft.client.renderer.GameRenderer field_228376_w_ # cameraYaw
# make public the cameraPitch in the GameRenderer
public net.minecraft.client.renderer.GameRenderer field_228377_x_ # cameraPitch
# make public the folder in the DimensionSavedDataManager
public net.minecraft.world.storage.DimensionSavedDataManager field_215759_d # folder
#=====================#
# Examples from Forge #