Merge changes from 1.17.1-Forge (11-10-2021)

This commit is contained in:
James Seibel
2021-11-10 22:19:31 -06:00
parent a5f98b02d4
commit ff398ee6cc
36 changed files with 1251 additions and 599 deletions
@@ -56,23 +56,20 @@ import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.LightType;
/**
* This object is used to create NearFarBuffer objects.
* @author James Seibel
* @version 10-23-2021
* @version 11-8-2021
*/
public class LodBufferBuilder
{
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** The thread used to generate new LODs off the main thread. */
public static final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(LodBufferBuilder.class.getSimpleName() + " - main"));
@@ -141,11 +138,11 @@ public class LodBufferBuilder
private volatile RegionPos center;
/**
* This is the ChunkPos the player was at the last time the buffers were built.
* 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 ChunkPos drawableCenterChunkPos = new ChunkPos(0, 0);
private volatile ChunkPos buildableCenterChunkPos = new ChunkPos(0, 0);
private volatile ChunkPosWrapper drawableCenterChunkPos = new ChunkPosWrapper(0, 0);
private volatile ChunkPosWrapper buildableCenterChunkPos = new ChunkPosWrapper(0, 0);
@@ -167,7 +164,7 @@ public class LodBufferBuilder
* swapped with the drawable buffers in the LodRenderer to be drawn.
*/
public void generateLodBuffersAsync(LodRenderer renderer, LodDimension lodDim,
BlockPos playerBlockPos, boolean fullRegen)
BlockPosWrapper playerBlockPos, boolean fullRegen)
{
// only allow one generation process to happen at a time
@@ -190,15 +187,15 @@ public class LodBufferBuilder
// 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,
BlockPos playerBlockPos, boolean fullRegen)
BlockPosWrapper playerBlockPos, boolean fullRegen)
{
bufferLock.lock();
try
{
// round the player's block position down to the nearest chunk BlockPos
ChunkPos playerChunkPos = new ChunkPos(playerBlockPos);
BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition();
ChunkPosWrapper playerChunkPos = new ChunkPosWrapper(playerBlockPos);
BlockPosWrapper playerBlockPosRounded = playerChunkPos.getWorldPosition();
//long startTime = System.currentTimeMillis();
@@ -232,8 +229,7 @@ public class LodBufferBuilder
// create the nodeToRenderThreads //
//================================//
ClientWorld world = mc.getClientWorld();
skyLightPlayer = world.getBrightness(LightType.SKY, playerBlockPos);
skyLightPlayer = MinecraftWrapper.INSTANCE.getWrappedClientLevel().getSkyLight(playerBlockPos);
for (int xRegion = 0; xRegion < lodDim.getWidth(); xRegion++)
{
@@ -315,8 +311,8 @@ public class LodBufferBuilder
posX = posToRender.getNthPosX(index);
posZ = posToRender.getNthPosZ(index);
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z;
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
@@ -343,8 +339,8 @@ public class LodBufferBuilder
xAdj = posX + Box.DIRECTION_NORMAL_MAP.get(direction).getX();
zAdj = posZ + Box.DIRECTION_NORMAL_MAP.get(direction).getZ();
long data;
chunkXdist = LevelPosUtil.getChunkPos(detailLevel, xAdj) - playerChunkPos.x;
chunkZdist = LevelPosUtil.getChunkPos(detailLevel, zAdj) - playerChunkPos.z;
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
@@ -476,13 +472,13 @@ public class LodBufferBuilder
}
}
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, ChunkPos playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
private boolean isThisPositionGoingToBeRendered(byte detailLevel, int posX, int posZ, ChunkPosWrapper playerChunkPos, boolean[][] vanillaRenderedChunks, int gameChunkRenderDistance){
// skip any chunks that Minecraft is going to render
int chunkXdist = LevelPosUtil.getChunkPos(detailLevel, posX) - playerChunkPos.x;
int chunkZdist = LevelPosUtil.getChunkPos(detailLevel, posZ) - playerChunkPos.z;
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 (LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get() == VanillaOverdraw.BORDER)
@@ -519,13 +515,16 @@ public class LodBufferBuilder
*/
public void setupBuffers(LodDimension lodDimension)
{
GlProxy glProxy = GlProxy.getInstance();
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 BufferBuilder[numbRegionsWide][numbRegionsWide][];
@@ -616,6 +615,7 @@ public class LodBufferBuilder
}
}
glProxy.setGlContext(oldContext);
bufferLock.unlock();
}
@@ -778,7 +778,11 @@ public class LodBufferBuilder
for (int i = 0; i < buildableBuffers[x][z].length; i++)
{
ByteBuffer uploadBuffer = buildableBuffers[x][z][i].popNextBuffer().getSecond();
vboUpload(buildableVbos[x][z][i], buildableStorageBufferIds[x][z][i], uploadBuffer, true, uploadMethod);
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);
}
}
@@ -793,7 +797,7 @@ public class LodBufferBuilder
}
finally
{
GL11.glFinish();
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
@@ -810,14 +814,13 @@ public class LodBufferBuilder
if (vbo.id != -1 && GlProxy.getInstance().getGlContext() == GlProxyContext.LOD_BUILDER)
{
// this is how many points will be rendered
vbo.vertexCount = (uploadBuffer.capacity() / vbo.format.getVertexSize());
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)
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);
@@ -898,7 +901,6 @@ public class LodBufferBuilder
// hybrid subData/bufferData //
// less stutter, low GPU usage
//long size = GL31.glGetBufferParameteri64(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE); // hopefully just a int should be long enough
long size = GL15.glGetBufferParameteri(GL15.GL_ARRAY_BUFFER, GL15.GL_BUFFER_SIZE);
if (size < uploadBuffer.capacity() * BUFFER_EXPANSION_MULTIPLIER)
{
@@ -953,9 +955,9 @@ public class LodBufferBuilder
{
public final VertexBuffer[][][] vbos;
public final int[][][] storageBufferIds;
public final ChunkPos drawableCenterChunkPos;
public final ChunkPosWrapper drawableCenterChunkPos;
public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, ChunkPos newDrawableCenterChunkPos)
public VertexBuffersAndOffset(VertexBuffer[][][] newVbos, int[][][] newStorageBufferIds, ChunkPosWrapper newDrawableCenterChunkPos)
{
vbos = newVbos;
storageBufferIds = newStorageBufferIds;
@@ -23,10 +23,10 @@ import java.util.Map;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
/**
* This is the abstract class used to create different
@@ -38,7 +38,7 @@ public abstract class AbstractLodTemplate
{
/** Uploads the given LOD to the buffer. */
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
public abstract void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled);
/** add the given position and color to the buffer */
@@ -28,10 +28,10 @@ import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3i;
/**
@@ -74,35 +74,58 @@ public class Box
public static final Map<Direction, int[][]> DIRECTION_VERTEX_MAP = new HashMap<Direction, int[][]>()
{{
put(Direction.UP, new int[][] {
{ 0, 1, 0 },
{ 0, 1, 1 },
{ 1, 1, 1 },
{ 1, 1, 0 } });
{ 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(Direction.DOWN, new int[][] {
{ 1, 0, 0 },
{ 1, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 } });
{ 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(Direction.EAST, new int[][] {
{ 1, 1, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 0, 0 } });
{ 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(Direction.WEST, new int[][] {
{ 0, 0, 0 },
{ 0, 0, 1 },
{ 0, 1, 1 },
{ 0, 1, 0 } });
{ 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(Direction.SOUTH, new int[][] {
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 1, 1 },
{ 0, 0, 1 } });
{ 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(Direction.NORTH, new int[][] {
{ 0, 0, 0 },
{ 0, 1, 0 },
{ 1, 1, 0 },
{ 1, 0, 0 } });
{ 0, 0, 0 }, // 0
{ 0, 1, 0 }, // 1
{ 1, 1, 0 }, // 2
{ 0, 0, 0 }, // 0
{ 1, 1, 0 }, // 2
{ 1, 0, 0 } // 3
});
}};
@@ -226,7 +249,7 @@ public class Box
for (Direction direction : DIRECTIONS)
{
if (!adjShadeDisabled[DIRECTION_INDEX.get(direction)])
colorMap[DIRECTION_INDEX.get(direction)] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true));
colorMap[DIRECTION_INDEX.get(direction)] = ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientLevel().getShade(direction, true));
else
colorMap[DIRECTION_INDEX.get(direction)] = color;
}
@@ -241,7 +264,7 @@ public class Box
if (LodConfig.CLIENT.advancedModOptions.debugging.debugMode.get() != DebugMode.SHOW_DETAIL)
return colorMap[DIRECTION_INDEX.get(direction)];
else
return ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientWorld().getShade(direction, true));
return ColorUtil.applyShade(color, MinecraftWrapper.INSTANCE.getClientLevel().getShade(direction, true));
}
/**
@@ -279,7 +302,7 @@ public class Box
}
/** determine which faces should be culled */
public void setUpCulling(int cullingDistance, BlockPos playerPos)
public void setUpCulling(int cullingDistance, BlockPosWrapper playerPos)
{
for (Direction direction : DIRECTIONS)
{
@@ -25,15 +25,16 @@ import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.DataPointUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
/**
* Builds LODs as rectangular prisms.
* @author James Seibel
* @version 10-10-2021
* @version 11-8-2021
*/
public class CubicLodTemplate extends AbstractLodTemplate
{
@@ -44,7 +45,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
}
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
{
if (box == null)
@@ -79,7 +80,7 @@ public class CubicLodTemplate extends AbstractLodTemplate
private void generateBoundingBox(Box box,
int height, int depth, int width,
double xOffset, double yOffset, double zOffset,
BlockPos bufferCenterBlockPos,
BlockPosWrapper bufferCenterBlockPos,
Map<Direction, long[]> adjData,
int color,
int skyLight,
@@ -118,10 +119,11 @@ public class CubicLodTemplate extends AbstractLodTemplate
{
if(box.isCulled(direction))
continue;
int verticalFaceIndex = 0;
while (box.shouldRenderFace(direction, verticalFaceIndex))
{
for (int vertexIndex = 0; vertexIndex < 4; vertexIndex++)
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
{
color = box.getColor(direction);
skyLight = box.getSkyLight(direction, verticalFaceIndex);
@@ -24,9 +24,9 @@ import java.util.Map;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
/**
* TODO DynamicLodTemplate
@@ -39,7 +39,7 @@ import net.minecraft.util.math.BlockPos;
public class DynamicLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
@@ -24,9 +24,9 @@ import java.util.Map;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
/**
* TODO #21 TriangularLodTemplate
@@ -37,7 +37,7 @@ import net.minecraft.util.math.BlockPos;
public class TriangularLodTemplate extends AbstractLodTemplate
{
@Override
public void addLodToBuffer(BufferBuilder buffer, BlockPos bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
public void addLodToBuffer(BufferBuilder buffer, BlockPosWrapper bufferCenterBlockPos, long data, Map<Direction, long[]> adjData,
byte detailLevel, int posX, int posZ, Box box, DebugMode debugging, boolean[] adjShadeDisabled)
{
ClientProxy.LOGGER.error(DynamicLodTemplate.class.getSimpleName() + " is not implemented!");
@@ -41,9 +41,8 @@ import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Block.BlockShapeWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import com.seibel.lod.wrappers.World.BiomeColorWrapper;
import com.seibel.lod.wrappers.World.BiomeWrapper;
import com.seibel.lod.wrappers.World.WorldWrapper;
import com.seibel.lod.wrappers.World.LevelWrapper;
import net.minecraft.world.DimensionType;
import net.minecraft.world.IWorld;
@@ -109,7 +108,7 @@ public class LodBuilder
{
// we need a loaded client world in order to
// get the textures for blocks
if (mc.getClientWorld() == null)
if (mc.getClientLevel() == null)
return;
// don't try to generate LODs if the user isn't in the world anymore
@@ -171,7 +170,7 @@ public class LodBuilder
return;
// this happens if a LOD is generated after the user leaves the world.
if (MinecraftWrapper.INSTANCE.getWrappedClientWorld() == null)
if (MinecraftWrapper.INSTANCE.getWrappedClientLevel() == null)
return;
// determine how many LODs to generate horizontally
@@ -231,8 +230,8 @@ public class LodBuilder
int xAbs;
int yAbs;
int zAbs;
boolean hasCeiling = mc.getClientWorld().dimensionType().hasCeiling();
boolean hasSkyLight = mc.getClientWorld().dimensionType().hasSkyLight();
boolean hasCeiling = mc.getClientLevel().dimensionType().hasCeiling();
boolean hasSkyLight = mc.getClientLevel().dimensionType().hasSkyLight();
boolean isDefault;
BlockPosWrapper blockPos = new BlockPosWrapper();
int index;
@@ -387,7 +386,7 @@ public class LodBuilder
// 1 means the lighting is a guess
int isDefault = 0;
WorldWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerWorld();
LevelWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerLevel();
int blockBrightness = chunk.getEmittedBrightness(blockPos);
// get the air block above or below this block
@@ -415,7 +414,7 @@ public class LodBuilder
{
// we are on predicted terrain, and we don't know what the light here is,
// lets just take a guess
if (blockPos.getY() >= mc.getClientWorld().getSeaLevel() - 5)
if (blockPos.getY() >= mc.getClientLevel().getSeaLevel() - 5)
{
skyLight = 12;
isDefault = 1;
@@ -426,7 +425,7 @@ public class LodBuilder
}
else
{
world = MinecraftWrapper.INSTANCE.getWrappedClientWorld();
world = MinecraftWrapper.INSTANCE.getWrappedClientLevel();
if (world.isEmpty())
return 0;
// client world sky light (almost never accurate)
@@ -448,7 +447,7 @@ public class LodBuilder
{
// we don't know what the light here is,
// lets just take a guess
if (blockPos.getY() >= mc.getClientWorld().getSeaLevel() - 5)
if (blockPos.getY() >= mc.getClientLevel().getSeaLevel() - 5)
{
skyLight = 12;
isDefault = 1;
@@ -485,10 +484,7 @@ public class LodBuilder
BlockShapeWrapper blockShapeWrapper = chunk.getBlockShapeWrapper(blockPos);
if (chunk.isWaterLogged(blockPos))
{
BiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
return biome.getWaterTint();
}
blockColorWrapper = BlockColorWrapper.getWaterColor();
else
blockColorWrapper = chunk.getBlockColorWrapper(blockPos);
@@ -500,20 +496,16 @@ public class LodBuilder
if (blockColorWrapper.hasTint())
{
WorldWrapper world = MinecraftWrapper.INSTANCE.getWrappedServerWorld();
if (world == null || world.isEmpty())
world = MinecraftWrapper.INSTANCE.getWrappedClientWorld();
BiomeWrapper biome = chunk.getBiome(xRel, y, zRel);
int tintValue;
if (blockColorWrapper.hasGrassTint())
// grass and green plants
tintValue = BiomeColorWrapper.getGrassColor(world, blockPos);
tintValue = biome.getGrassTint(0,0);
else if (blockColorWrapper.hasFolliageTint())
tintValue = BiomeColorWrapper.getFoliageColor(world, blockPos);
tintValue = biome.getFolliageTint();
else
//we can reintroduce this with the wrappers
tintValue = BiomeColorWrapper.getWaterColor(world, blockPos);
tintValue = biome.getWaterTint();
colorInt = ColorUtil.multiplyRGBcolors(tintValue | 0xFF000000, colorOfBlock);
}
@@ -37,10 +37,10 @@ import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.palette.UpgradeData;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.biome.Biome;
@@ -90,7 +90,7 @@ public class LodGenWorker implements IWorker
public LodGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
public LodGenWorker(ChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
LodBuilder newLodBuilder,
LodDimension newLodDimension, ServerWorld newServerWorld)
{
@@ -161,9 +161,9 @@ public class LodGenWorker implements IWorker
public final DistanceGenerationMode generationMode;
public final LodBuilder lodBuilder;
private final ChunkPos pos;
private final ChunkPosWrapper pos;
public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode,
public LodChunkGenThread(ChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
LodBuilder newLodBuilder,
LodDimension newLodDimension, ServerWorld newServerWorld)
{
@@ -177,13 +177,13 @@ public class LodGenWorker implements IWorker
@Override
public void run()
{
try
{
//try
//{
// only generate LodChunks if they can
// be added to the current LodDimension
/* TODO I must disable this 'if', if I will find a way to replace it */
if (lodDim.regionIsInRange(pos.x / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.z / LodUtil.REGION_WIDTH_IN_CHUNKS))
if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS))
{
//
//{
@@ -270,20 +270,20 @@ public class LodGenWorker implements IWorker
// System.out.println(endTime - startTime);
}// if in range
}
catch (Exception e)
{
ClientProxy.LOGGER.error(LodChunkGenThread.class.getSimpleName() + ": ran into an error: " + e.getMessage());
e.printStackTrace();
}
finally
{
//}
//catch (Exception e)
//{
// ClientProxy.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
@@ -295,7 +295,7 @@ public class LodGenWorker implements IWorker
private void generateUsingBiomesOnly()
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
ServerChunkProvider chunkSource = serverWorld.getChunkSource();
@@ -407,7 +407,7 @@ public class LodGenWorker implements IWorker
private void generateUsingSurface()
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
@@ -447,7 +447,7 @@ public class LodGenWorker implements IWorker
private void generateUsingFeatures()
{
List<IChunk> chunkList = new LinkedList<>();
ChunkPrimer chunk = new ChunkPrimer(pos, UpgradeData.EMPTY);
ChunkPrimer chunk = new ChunkPrimer(pos.getChunkPos(), UpgradeData.EMPTY);
chunkList.add(chunk);
LodServerWorld lodServerWorld = new LodServerWorld(serverWorld, chunk);
@@ -565,7 +565,7 @@ public class LodGenWorker implements IWorker
*/
private void generateWithServer()
{
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.x, pos.z, ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.SERVER));
lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(serverWorld.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FEATURES)), new LodBuilderConfig(DistanceGenerationMode.SERVER));
}
@@ -35,9 +35,9 @@ import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodThreadFactory;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.WorldWorkerManager;
@@ -72,7 +72,7 @@ public class LodWorldGenerator
*/
public final AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0);
public final Set<ChunkPos> positionsWaitingToBeGenerated = new HashSet<>();
public final Set<ChunkPosWrapper> positionsWaitingToBeGenerated = new HashSet<>();
/**
* Singleton copy of this object
@@ -142,7 +142,7 @@ public class LodWorldGenerator
posZ = posToGenerate.getNthPosZ(nearIndex, true);
nearIndex++;
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
ChunkPosWrapper chunkPos = new ChunkPosWrapper(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
// prevent generating the same chunk multiple times
if (positionsWaitingToBeGenerated.contains(chunkPos))
@@ -167,7 +167,7 @@ public class LodWorldGenerator
posZ = posToGenerate.getNthPosZ(farIndex, false);
farIndex++;
ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
ChunkPosWrapper chunkPos = new ChunkPosWrapper(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
// don't add more to the generation queue then allowed
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
@@ -24,7 +24,7 @@ import java.io.File;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.world.ClientWorld;
import com.seibel.lod.wrappers.World.LevelWrapper;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.chunk.IChunk;
import net.minecraft.world.chunk.storage.ChunkSerializer;
@@ -39,10 +39,10 @@ public class ChunkLoader
{
public static IChunk getChunkFromFile(ChunkPos pos){
ClientWorld clientWorld = MinecraftWrapper.INSTANCE.getClientWorld();
if (clientWorld == null)
LevelWrapper clientLevel = MinecraftWrapper.INSTANCE.getWrappedClientLevel();
if (clientLevel == null)
return null;
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientWorld.dimensionType());
ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(clientLevel.getWorld().dimensionType());
try
{
File file = new File(serverWorld.getChunkSource().getDataStorage().dataFolder.getParent() + File.separatorChar + "region", "r." + (pos.x >> 5) + "." + (pos.z >> 5) + ".mca");
@@ -19,11 +19,9 @@
package com.seibel.lod.handlers;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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;
@@ -236,9 +234,7 @@ public class LodDimensionFileHandler
// Save to File //
//==============//
/**
* Save all dirty regions in this LodDimension to file.
*/
/** Save all dirty regions in this LodDimension to file */
public void saveDirtyRegionsToFileAsync()
{
fileWritingThreadPool.execute(saveDirtyRegionsThread);
@@ -289,6 +285,7 @@ public class LodDimensionFileHandler
}
File oldFile = new File(fileName);
//ClientProxy.LOGGER.info("saving region [" + region.regionPosX + ", " + region.regionPosZ + "] to file.");
byte[] temp = region.getLevel(detailLevel).toDataString();
try
{
@@ -308,9 +305,12 @@ public class LodDimensionFileHandler
// (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)
@@ -326,11 +326,17 @@ public class LodDimensionFileHandler
// 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
ClientProxy.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))
@@ -339,7 +345,7 @@ public class LodDimensionFileHandler
outputStream.write(LOD_SAVE_FILE_VERSION);
// add each LodChunk to the file
outputStream.write(region.getLevel(detailLevel).toDataString());
outputStream.write(temp);
outputStream.close();
// overwrite the old file with the new one
@@ -366,6 +372,22 @@ public class LodDimensionFileHandler
// 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)
{
ClientProxy.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
@@ -37,7 +37,6 @@ import java.lang.reflect.Method;
public class ReflectionHandler
{
public static final ReflectionHandler INSTANCE = new ReflectionHandler();
private final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
public Field ofFogField = null;
public Method vertexBufferUploadMethod = null;
@@ -56,7 +55,7 @@ public class ReflectionHandler
private void setupFogField()
{
// get every variable from the entity renderer
Field[] optionFields = mc.getOptions().getClass().getDeclaredFields();
Field[] optionFields = MinecraftWrapper.INSTANCE.getOptions().getClass().getDeclaredFields();
// try and find the ofFogType variable in gameSettings
for (Field field : optionFields)
@@ -93,7 +92,7 @@ public class ReflectionHandler
try
{
returnNum = (int) ofFogField.get(mc.getOptions());
returnNum = (int) ofFogField.get(MinecraftWrapper.INSTANCE.getOptions());
}
catch (IllegalArgumentException | IllegalAccessException e)
{
@@ -24,6 +24,7 @@ import java.util.Map;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.wrappers.World.DimensionTypeWrapper;
import net.minecraft.world.DimensionType;
/**
@@ -39,7 +40,7 @@ public class LodWorld
private String worldName;
/** dimensions in this world */
private Map<DimensionType, LodDimension> lodDimensions;
private Map<DimensionTypeWrapper, LodDimension> lodDimensions;
/** If true then the LOD world is setup and ready to use */
private boolean isWorldLoaded = false;
@@ -107,7 +108,7 @@ public class LodWorld
if (lodDimensions == null)
return;
lodDimensions.put(newDimension.dimension, newDimension);
lodDimensions.put(DimensionTypeWrapper.getDimensionTypeWrapper(newDimension.dimension), newDimension);
}
/**
@@ -118,7 +119,7 @@ public class LodWorld
if (lodDimensions == null)
return null;
return lodDimensions.get(dimension);
return lodDimensions.get(DimensionTypeWrapper.getDimensionTypeWrapper(dimension));
}
/**
@@ -132,7 +133,7 @@ public class LodWorld
saveAllDimensions();
for (DimensionType key : lodDimensions.keySet())
for (DimensionTypeWrapper key : lodDimensions.keySet())
lodDimensions.get(key).setRegionWidth(newRegionWidth);
}
@@ -148,7 +149,7 @@ public class LodWorld
// but that requires a LodDimension.hasDirtyRegions() method or something similar
ClientProxy.LOGGER.info("Saving LODs");
for (DimensionType key : lodDimensions.keySet())
for (DimensionTypeWrapper key : lodDimensions.keySet())
lodDimensions.get(key).saveDirtyRegionsToFileAsync();
}
@@ -20,9 +20,8 @@
package com.seibel.lod.objects;
import com.seibel.lod.util.LodUtil;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
/**
* This object is similar to ChunkPos or BlockPos.
@@ -55,28 +54,28 @@ public class RegionPos
}
/** Converts from a BlockPos to a RegionPos */
public RegionPos(BlockPos pos)
public RegionPos(BlockPosWrapper pos)
{
this(new ChunkPos(pos));
this(new ChunkPosWrapper(pos));
}
/** Converts from a ChunkPos to a RegionPos */
public RegionPos(ChunkPos pos)
public RegionPos(ChunkPosWrapper pos)
{
x = Math.floorDiv(pos.x, LodUtil.REGION_WIDTH_IN_CHUNKS);
z = Math.floorDiv(pos.z, LodUtil.REGION_WIDTH_IN_CHUNKS);
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 ChunkPos chunkPos()
public ChunkPosWrapper chunkPos()
{
return new ChunkPos(
return new ChunkPosWrapper(
(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 BlockPos blockPos()
public BlockPosWrapper blockPos()
{
return chunkPos().getWorldPosition()
.offset(LodUtil.CHUNK_WIDTH / 2, 0, LodUtil.CHUNK_WIDTH / 2);
@@ -129,7 +129,7 @@ public class VerticalLevelContainer implements LevelContainer
long newData;
detailLevel = inputData[index];
index++;
maxVerticalData = inputData[index];
maxVerticalData = inputData[index] & 0b01111111;
index++;
size = 1 << (LodUtil.REGION_DETAIL_LEVEL - detailLevel);
int x = size * size * maxVerticalData;
@@ -181,24 +181,31 @@ public class VerticalLevelContainer implements LevelContainer
public byte[] toDataString()
{
int index = 0;
int x = size * size * maxVerticalData;
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++)
{
current = dataContainer[i];
for (tempIndex = 0; tempIndex < 8; tempIndex++)
tempData[index + tempIndex] = (byte) (current >>> (8 * tempIndex));
index += 8;
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;
}
@@ -19,10 +19,10 @@
package com.seibel.lod.proxy;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL15;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder;
@@ -40,8 +40,10 @@ import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.util.ThreadMapUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkWrapper;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.text.StringTextComponent;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.event.TickEvent;
@@ -54,7 +56,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent;
* This handles all events sent to the client,
* and is the starting point for most of the mod.
* @author James_Seibel
* @version 10-23-2021
* @version 11-8-2021
*/
public class ClientProxy
{
@@ -99,7 +101,7 @@ public class ClientProxy
//==============//
/** Do any setup that is required to draw LODs and then tell the LodRenderer to draw. */
public void renderLods(MatrixStack mcMatrixStack, float partialTicks)
public void renderLods(MatrixStack mcModelViewMatrix, float partialTicks)
{
// comment out when creating a release
// applyConfigOverrides();
@@ -129,6 +131,15 @@ public class ClientProxy
lodDim.expandOrLoadRegionsAsync((int) mc.getPlayer().getX(), (int) mc.getPlayer().getZ());
// get the default projection matrix, so we can
// reset it after drawing the LODs
float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
mcProjectionMatrix.transpose();
// Note to self:
// if "unspecified" shows up in the pie chart, it is
// possibly because the amount of time between sections
@@ -137,7 +148,8 @@ public class ClientProxy
profiler.pop(); // get out of "terrain"
profiler.push("LOD");
renderer.drawLODs(lodDim, mcMatrixStack, partialTicks, mc.getProfiler());
renderer.drawLODs(lodDim, mcModelViewMatrix, mcProjectionMatrix, partialTicks, mc.getProfiler());
profiler.pop(); // end LOD
profiler.push("terrain"); // go back into "terrain"
@@ -161,18 +173,19 @@ public class ClientProxy
// remind the developer(s) that the config override is active
if (!configOverrideReminderPrinted)
{
// TODO add a send message method to the MC wrapper
// mc.getPlayer().sendMessage(new StringTextComponent("LOD experimental build 1.5.1"), mc.getPlayer().getUUID());
// mc.getPlayer().sendMessage(new StringTextComponent("Here be dragons!"), mc.getPlayer().getUUID());
mc.getPlayer().sendMessage(new StringTextComponent("Debug settings enabled!"), mc.getPlayer().getUUID());
configOverrideReminderPrinted = true;
}
// LodConfig.CLIENT.graphics.drawResolution.set(HorizontalResolution.BLOCK);
// LodConfig.CLIENT.worldGenerator.generationResolution.set(HorizontalResolution.BLOCK);
// requires a world restart?
// LodConfig.CLIENT.worldGenerator.lodQualityMode.set(VerticalQuality.VOXEL);
// LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.set(FogDistance.FAR);
// LodConfig.CLIENT.graphics.fogQualityOption.fogDrawOverride.set(FogDrawOverride.FANCY);
// LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.set(true);
@@ -181,7 +194,7 @@ public class ClientProxy
// LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.set(VanillaOverdraw.DYNAMIC);
// LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.set(GpuUploadMethod.BUFFER_STORAGE);
// LodConfig.CLIENT.worldGenerator.distanceGenerationMode.set(DistanceGenerationMode.SURFACE);
// LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.set(128);
// LodConfig.CLIENT.worldGenerator.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR);
@@ -250,23 +263,25 @@ public class ClientProxy
{
// 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();
//LodNodeGenWorker.restartExecutorService(); // TODO why was this commented out? -James
//ThreadMapUtil.clearMaps();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
lodWorld.deselectWorld();
// hopefully this should reduce issues related to the buffer builder
// prevent issues related to the buffer builder
// breaking when changing worlds.
renderer.destroyBuffers();
recalculateWidths = true;
renderer = new LodRenderer(lodBufferBuilder);
// make sure the nilled objects are freed.
// make sure the nulled objects are freed.
// (this prevents an out of memory error when
// changing worlds)
System.gc();
@@ -314,7 +329,7 @@ public class ClientProxy
private void playerMoveEvent(LodDimension lodDim)
{
// make sure the dimension is centered
RegionPos playerRegionPos = new RegionPos(mc.getPlayer().blockPosition());
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)
{
@@ -330,7 +345,7 @@ public class ClientProxy
{
// calculate how wide the dimension(s) should be in regions
int chunksWide;
if (mc.getClientWorld().dimensionType().hasCeiling())
if (mc.getClientLevel().dimensionType().hasCeiling())
chunksWide = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
else
chunksWide = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * 2 + 1;
@@ -347,7 +362,7 @@ public class ClientProxy
// update the dimensions to fit the new width
lodWorld.resizeDimensionRegionWidth(newWidth);
lodBuilder.defaultDimensionWidthInRegions = newWidth;
renderer.setupBuffers(lodWorld.getLodDimension(mc.getClientWorld().dimensionType()));
renderer.setupBuffers(lodWorld.getLodDimension(mc.getClientLevel().dimensionType()));
recalculateWidths = false;
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
@@ -368,6 +383,7 @@ public class ClientProxy
/** this method reset some static data every time we change world */
private void resetMod()
{
// TODO when should this be used?
ThreadMapUtil.clearMaps();
LodGenWorker.restartExecutorService();
+132 -55
View File
@@ -21,11 +21,16 @@ package com.seibel.lod.proxy;
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.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.ModInfo;
import com.seibel.lod.enums.GlProxyContext;
import com.seibel.lod.render.shader.LodShader;
import com.seibel.lod.render.shader.LodShaderProgram;
import com.seibel.lod.wrappers.MinecraftWrapper;
/**
@@ -40,29 +45,31 @@ import com.seibel.lod.wrappers.MinecraftWrapper;
* https://gamedev.stackexchange.com/questions/91995/edit-vbo-data-or-create-a-new-one <br><br>
*
* @author James Seibel
* @version 10-23-2021
* @version 11-8-2021
*/
public class GlProxy
{
private static GlProxy instance = null;
private static final MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
private static MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
/** Minecraft's GLFW window */
public final long minecraftGlContext;
/** Minecraft's GL context */
/** Minecraft's GL capabilities */
public final GLCapabilities minecraftGlCapabilities;
/** the LodBuilder's GLFW window */
public final long lodBuilderGlContext;
/** the LodBuilder's GL context */
/** the LodBuilder's GL capabilities */
public final GLCapabilities lodBuilderGlCapabilities;
/**
* This is just used for debugging, hopefully it can be removed once
* the context switching is more stable.
*/
public Thread lodBuilderOwnerThread = null;
/** 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;
/** Does this computer's GPU support fancy fog? */
public final boolean fancyFogAvailable;
@@ -74,6 +81,8 @@ public class GlProxy
public final boolean mapBufferRangeSupported;
private GlProxy()
{
ClientProxy.LOGGER.error("Creating " + GlProxy.class.getSimpleName() + "... If this is the last message you see in the log there must have been a OpenGL error.");
@@ -93,54 +102,58 @@ public class GlProxy
minecraftGlContext = GLFW.glfwGetCurrentContext();
minecraftGlCapabilities = GL.getCapabilities();
// create the LodBuilder's context
// Hopefully this shouldn't cause any issues with other mods that need custom contexts
// (although the number that do should be relatively few)
// 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 an invisible window to hold the context
lodBuilderGlContext = GLFW.glfwCreateWindow(640, 480, "LOD window", 0L, minecraftGlContext);
// create the LodBuilder context
lodBuilderGlContext = GLFW.glfwCreateWindow(64, 48, "LOD Builder Window", 0L, minecraftGlContext);
GLFW.glfwMakeContextCurrent(lodBuilderGlContext);
lodBuilderGlCapabilities = GL.createCapabilities();
// Since this is called on the render thread, make sure the Minecraft context is used in the end
GLFW.glfwMakeContextCurrent(minecraftGlContext);
GL.setCapabilities(minecraftGlCapabilities);
//==============================//
// determine the OpenGL version //
//==============================//
bufferStorageSupported = minecraftGlCapabilities.OpenGL45;
mapBufferRangeSupported = minecraftGlCapabilities.OpenGL30;
if (!minecraftGlCapabilities.OpenGL15)
{
String errorMessage = ModInfo.READABLE_NAME + " was initializing " + GlProxy.class.getSimpleName() + " and discoverd this GPU doesn't support OpenGL 1.5 or greater.";
mc.crashMinecraft(errorMessage + " Sorry I couldn't tell you sooner :(", new UnsupportedOperationException("This GPU doesn't support OpenGL 1.5 or greater."));
}
if (!bufferStorageSupported)
{
String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5";
ClientProxy.LOGGER.error("This GPU doesn't support OpenGL 4.5, falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
}
//==================================//
// get any GPU related capabilities //
//==================================//
// see if this GPU can run fancy fog
fancyFogAvailable = GL.getCapabilities().GL_NV_fog_distance;
ClientProxy.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 = false; //lodBuilderGlCapabilities.glBufferStorage != 0;
mapBufferRangeSupported = lodBuilderGlCapabilities.glMapBufferRange != 0;
fancyFogAvailable = minecraftGlCapabilities.GL_NV_fog_distance;
// display the capabilities
if (!bufferStorageSupported)
{
String fallBackVersion = mapBufferRangeSupported ? "3.0" : "1.5";
ClientProxy.LOGGER.error("This GPU doesn't support Buffer Storage (OpenGL 4.5), falling back to OpenGL " + fallBackVersion + ". This may cause stuttering and reduced performance.");
}
if (!fancyFogAvailable)
ClientProxy.LOGGER.info("This GPU does not support GL_NV_fog_distance. This means that the fancy fog option will not be available.");
@@ -148,11 +161,79 @@ public class GlProxy
//==============//
// shader setup //
//==============//
//setGlContext(GlProxyContext.LOD_RENDER);
setGlContext(GlProxyContext.MINECRAFT);
createShaderProgram();
// Note: VAO objects can not be shared between contexts,
// this must be created on the LOD 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
ClientProxy.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)
{
ClientProxy.LOGGER.error("Unable to compile shaders. Error: " + e.getMessage());
}
}
/**
@@ -163,12 +244,12 @@ public class GlProxy
{
GlProxyContext currentContext = getGlContext();
// we don't have to change the context, we're already there.
// we don't have to change the context, we are already there.
if (currentContext == newContext)
return;
long contextPointer = 0L;
long contextPointer;
GLCapabilities newGlCapabilities = null;
// get the pointer(s) for this context
@@ -187,20 +268,13 @@ public class GlProxy
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);
// used for debugging
if (newContext == GlProxyContext.LOD_BUILDER)
lodBuilderOwnerThread = Thread.currentThread();
else if (newContext == GlProxyContext.NONE && currentContext == GlProxyContext.LOD_BUILDER)
lodBuilderOwnerThread = null;
}
@@ -220,9 +294,12 @@ public class GlProxy
else if (currentContext == 0L)
return GlProxyContext.NONE;
else
// hopefully this shouldn't happen, but
// at least now we will be notified if it does happen
throw new IllegalStateException(Thread.currentThread().getName() + " has a unknown OpenGl context: [" + currentContext + "]. Minecraft context [" + minecraftGlContext + "], LodBuilder context [" + lodBuilderGlContext + "], no context [0].");
// hopefully this shouldn't happen
throw new IllegalStateException(Thread.currentThread().getName() +
" has a unknown OpenGl context: [" + currentContext + "]. "
+ "Minecraft context [" + minecraftGlContext + "], "
+ "LodBuilder context [" + lodBuilderGlContext + "], "
+ "no context [0].");
}
@@ -19,44 +19,52 @@
package com.seibel.lod.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.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder;
import com.seibel.lod.builders.bufferBuilding.LodBufferBuilder.VertexBuffersAndOffset;
import com.seibel.lod.config.LodConfig;
import com.seibel.lod.enums.*;
import com.seibel.lod.enums.DebugMode;
import com.seibel.lod.enums.FogDistance;
import com.seibel.lod.enums.FogDrawOverride;
import com.seibel.lod.enums.FogQuality;
import com.seibel.lod.enums.GpuUploadMethod;
import com.seibel.lod.handlers.ReflectionHandler;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.NearFarFogSettings;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.proxy.GlProxy;
import com.seibel.lod.render.shader.LodShaderProgram;
import com.seibel.lod.util.DetailDistanceUtil;
import com.seibel.lod.util.LevelPosUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import net.minecraft.client.renderer.ActiveRenderInfo;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.potion.Effects;
import net.minecraft.profiler.IProfiler;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL15C;
import org.lwjgl.opengl.NVFogDistance;
import java.util.HashSet;
import net.minecraft.util.math.vector.Vector3f;
/**
* This is where all the magic happens. <br>
* This is where LODs are draw to the world.
*
* @author James Seibel
* @version 10-25-2021
* @version 11-8-2021
*/
public class LodRenderer
{
@@ -90,7 +98,7 @@ public class LodRenderer
*/
private int[][][] storageBufferIds;
private ChunkPos vbosCenter = new ChunkPos(0, 0);
private ChunkPosWrapper vbosCenter = new ChunkPosWrapper(0, 0);
/** This is used to determine if the LODs should be regenerated */
@@ -124,8 +132,6 @@ public class LodRenderer
public boolean vanillaRenderedChunksEmptySkip = false;
public int vanillaBlockRenderedDistance;
final boolean vivecraftDetected = ReflectionHandler.INSTANCE.detectVivecraft();
@@ -147,11 +153,11 @@ public class LodRenderer
* 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 mcMatrixStack This matrix stack should come straight from MC's renderChunkLayer (or future equivalent) method
* @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.
*/
@SuppressWarnings("deprecation")
public void drawLODs(LodDimension lodDim, MatrixStack mcMatrixStack, float partialTicks, IProfiler newProfiler)
public void drawLODs(LodDimension lodDim, MatrixStack mcModelViewMatrix, Matrix4f mcProjectionMatrix, float partialTicks, IProfiler newProfiler)
{
//=================================//
// determine if LODs should render //
@@ -176,14 +182,6 @@ public class LodRenderer
//===============//
// initial setup //
//===============//
profiler = newProfiler;
profiler.push("LOD setup");
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// starting here...
determineIfLodsShouldRegenerate(lodDim, partialTicks);
@@ -197,25 +195,17 @@ public class LodRenderer
// 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 (LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get())
if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvailable())
{
if (lodBufferBuilder.buildableBuffers == null)
lodBufferBuilder.setupBuffers(lodDim);
// generate the LODs on a separate thread to prevent stuttering or freezing
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayerBlockPos(), true);
if ((partialRegen || fullRegen) && !lodBufferBuilder.generatingBuffers && !lodBufferBuilder.newBuffersAvailable())
{
// generate the LODs on a separate thread to prevent stuttering or freezing
lodBufferBuilder.generateLodBuffersAsync(this, lodDim, mc.getPlayer().blockPosition(), fullRegen);
// the regen process has been started,
// it will be done when lodBufferBuilder.newBuffersAvailable()
// is true
fullRegen = false;
partialRegen = false;
}
// the regen process has been started,
// it will be done when lodBufferBuilder.newBuffersAvailable()
// is true
fullRegen = false;
partialRegen = false;
}
else
lodBufferBuilder.destroyBuffers();
// TODO move the buffer regeneration logic into its own class (probably called in the client proxy instead)
// ...ending here
@@ -226,9 +216,19 @@ public class LodRenderer
}
//===========================//
// GL settings for rendering //
//===========================//
//===============//
// initial setup //
//===============//
profiler = newProfiler;
profiler.push("LOD setup");
GlProxy glProxy = GlProxy.getInstance();
// set the required open GL settings
@@ -237,49 +237,47 @@ public class LodRenderer
else
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
GL15.glDisable(GL15.GL_TEXTURE_2D);
GL15.glEnable(GL15.GL_CULL_FACE);
GL15.glEnable(GL15.GL_COLOR_MATERIAL);
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);
// disable the lights Minecraft uses
GL15.glDisable(GL15.GL_LIGHT0);
GL15.glDisable(GL15.GL_LIGHT1);
// get MC's shader program
int currentProgram = GL20.glGetInteger(GL20.GL_CURRENT_PROGRAM);
// get the default projection matrix, so we can
// reset it after drawing the LODs
float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Matrix4f mcProjectionMatrix = new Matrix4f(mcProjMatrixRaw);
// OpenGl outputs their matrices in col,row form instead of row,col
// (or maybe vice versa I have no idea :P)
mcProjectionMatrix.transpose();
Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcMatrixStack, partialTicks);
Matrix4f modelViewMatrix = offsetTheModelViewMatrix(mcModelViewMatrix, partialTicks);
vanillaBlockRenderedDistance = mc.getRenderDistance() * LodUtil.CHUNK_WIDTH;
// required for setupFog and setupProjectionMatrix
if (mc.getClientWorld().dimensionType().hasCeiling())
if (mc.getClientLevel().dimensionType().hasCeiling())
farPlaneBlockDistance = Math.min(LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH;
else
farPlaneBlockDistance = LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get() * LodUtil.CHUNK_WIDTH;
setupProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks);
Matrix4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, partialTicks);
// commented out until we can add shaders to handle lighting
//setupLighting(lodDim, partialTicks);
NearFarFogSettings fogSettings = determineFogSettings();
// determine the current fog settings, so they can be
// reset after drawing the LODs
float defaultFogStartDist = 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.getInstance().fancyFogAvailable ? GL15.glGetInteger(NVFogDistance.GL_FOG_DISTANCE_MODE_NV) : -1;
// // 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 //
@@ -287,13 +285,14 @@ public class LodRenderer
profiler.popPush("LOD draw");
if (vbos != null && LodConfig.CLIENT.advancedModOptions.debugging.drawLods.get())
if (vbos != null)
{
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
Vector3d cameraDir = new Vector3d(renderInfo.getLookVector());
ActiveRenderInfo camera = mc.getGameRenderer().getMainCamera();
Vector3f cameraDir = camera.getLookVector();
boolean cullingDisabled = LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && GlProxy.getInstance().bufferStorageSupported;
// TODO re-enable once rendering is totally working
boolean cullingDisabled = true; //LodConfig.CLIENT.graphics.advancedGraphicsOption.disableDirectionalCulling.get();
boolean renderBufferStorage = LodConfig.CLIENT.graphics.advancedGraphicsOption.gpuUploadMethod.get() == GpuUploadMethod.BUFFER_STORAGE && glProxy.bufferStorageSupported;
// used to determine what type of fog to render
int halfWidth = vbos.length / 2;
@@ -303,6 +302,32 @@ public class LodRenderer
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++)
@@ -311,27 +336,34 @@ public class LodRenderer
x + vboCenterRegionPos.x - (lodDim.getWidth() / 2),
z + vboCenterRegionPos.z - (lodDim.getWidth() / 2));
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(renderInfo.getBlockPosition(), cameraDir, vboPos.blockPos()))
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(camera.getBlockPosition(), cameraDir, vboPos.blockPos()))
{
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);
// 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++)
drawStorageBuffer(vbos[x][z][i], storageBufferIds[x][z][i], modelViewMatrix);
else
// 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++)
drawVertexBuffer(vbos[x][z][i], modelViewMatrix);
drawArrays(vbos[x][z][i].id, vbos[x][z][i].vertexCount, posAttrib, colAttrib);
}
}
}
GL20.glDisableVertexAttribArray(posAttrib);
GL20.glDisableVertexAttribArray(colAttrib);
}
//=========//
// cleanup //
//=========//
@@ -339,23 +371,12 @@ public class LodRenderer
profiler.popPush("LOD cleanup");
GL15.glPolygonMode(GL15.GL_FRONT_AND_BACK, GL15.GL_FILL);
GL15.glEnable(GL15.GL_TEXTURE_2D);
GL15.glDisable(LOD_GL_LIGHT_NUMBER);
GL15.glDisable(GL15.GL_BLEND);
// re-enable the lights Minecraft uses
GL15.glEnable(GL15.GL_LIGHT0);
GL15.glEnable(GL15.GL_LIGHT1);
RenderSystem.disableLighting();
GL15.glDisable(GL15.GL_BLEND); // TODO: what should this be reset to?
// reset the fog settings so the normal chunks
// will be drawn correctly
cleanupFog(fogSettings, defaultFogStartDist, defaultFogEndDist, defaultFogMode, defaultFogDistance);
GL20.glUseProgram(currentProgram);
//RenderSystem.setShader(() -> mcShader);
// reset the projection matrix so anything drawn after
// the LODs will use the correct projection matrix
gameRender.resetProjectionMatrix(mcProjectionMatrix);
// clear the depth buffer so anything drawn is drawn
// clear the depth buffer so everything drawn is drawn
// over the LODs
GL15.glClear(GL15.GL_DEPTH_BUFFER_BIT);
@@ -364,40 +385,40 @@ public class LodRenderer
profiler.pop();
}
/** This is where the actual drawing happens. */
private void drawStorageBuffer(VertexBuffer vbo, int bufferStorageId, Matrix4f modelViewMatrix)
private void drawArrays(int glBufferId, int vertexCount, int posAttrib, int colAttrib)
{
if (vbo == null)
if (glBufferId == 0)
return;
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferStorageId);
// 0L is the starting pointer
LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L);
// can be used to check for OpenGL errors
// int error = GL15.glGetError();
// ClientProxy.LOGGER.info(Integer.toHexString(error));
vbo.draw(modelViewMatrix, GL15.GL_QUADS);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LodUtil.LOD_VERTEX_FORMAT.clearBufferState();
// 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);
}
/** This is where the actual drawing happens. */
private void drawVertexBuffer(VertexBuffer vbo, Matrix4f modelViewMatrix)
{
if (vbo == null)
return;
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo.id);
// 0L is the starting pointer
LodUtil.LOD_VERTEX_FORMAT.setupBufferState(0L);
vbo.draw(modelViewMatrix, GL15.GL_QUADS);
GL15C.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
LodUtil.LOD_VERTEX_FORMAT.clearBufferState();
}
@@ -405,7 +426,6 @@ public class LodRenderer
// Setup Functions //
//=================//
@SuppressWarnings("deprecation")
private void setupFog(FogDistance fogDistance, FogQuality fogQuality)
{
if (fogQuality == FogQuality.OFF)
@@ -445,55 +465,52 @@ public class LodRenderer
{
// for more realistic fog when using FAR
if (LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get() == FogDistance.NEAR_AND_FAR)
RenderSystem.fogStart(farPlaneBlockDistance * 1.6f * 0.9f);
GL15.glFogf(GL15.GL_FOG_START, farPlaneBlockDistance * 1.6f * 0.9f);
else
RenderSystem.fogStart(Math.min(vanillaBlockRenderedDistance * 1.5f, farPlaneBlockDistance * 0.9f * 1.6f));
RenderSystem.fogEnd(farPlaneBlockDistance * 1.6f);
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
RenderSystem.fogStart(farPlaneBlockDistance * 0.75f);
RenderSystem.fogEnd(farPlaneBlockDistance * 1.0f);
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)
{
RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.41f);
RenderSystem.fogStart(vanillaBlockRenderedDistance * 1.6f);
GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.41f);
GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.6f);
}
else if (fogQuality == FogQuality.FAST)
{
RenderSystem.fogEnd(vanillaBlockRenderedDistance * 1.0f);
RenderSystem.fogStart(vanillaBlockRenderedDistance * 1.5f);
GL15.glFogf(GL15.GL_FOG_END, vanillaBlockRenderedDistance * 1.0f);
GL15.glFogf(GL15.GL_FOG_START, vanillaBlockRenderedDistance * 1.5f);
}
}
GL15.glEnable(GL15.GL_FOG);
RenderSystem.enableFog();
RenderSystem.setupNvFogDistance();
RenderSystem.fogMode(GlStateManager.FogMode.LINEAR);
GL15.glFogi(GL15.GL_FOG_MODE, GL15.GL_LINEAR);
if (GlProxy.getInstance().fancyFogAvailable)
GL15.glFogi(NVFogDistance.GL_FOG_DISTANCE_MODE_NV, glFogDistanceMode);
}
/**
/**
* Revert any changes that were made to the fog
* and sets up the fog for Minecraft.
*/
@SuppressWarnings("deprecation")
private void cleanupFog(NearFarFogSettings fogSettings,
float defaultFogStartDist, float defaultFogEndDist,
int defaultFogMode, int defaultFogDistance)
{
RenderSystem.fogStart(defaultFogStartDist);
RenderSystem.fogEnd(defaultFogEndDist);
RenderSystem.fogMode(defaultFogMode);
GL15.glFogf(GL15.GL_FOG_START, defaultFogStartDist);
GL15.glFogf(GL15.GL_FOG_END, defaultFogEndDist);
GL15.glFogi(GL15.GL_FOG_MODE, defaultFogMode);
// this setting is only valid if the GPU supports fancy fog
if (GlProxy.getInstance().fancyFogAvailable)
@@ -502,7 +519,7 @@ public class LodRenderer
// disable fog if Minecraft wasn't rendering fog
// or we want it disabled
if (!fogSettings.vanillaIsRenderingFog
|| LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.get())
|| LodConfig.CLIENT.graphics.fogQualityOption.disableVanillaFog.get())
{
// Make fog render a infinite distance away.
// This doesn't technically disable Minecraft's fog
@@ -512,9 +529,9 @@ public class LodRenderer
// we can't disable minecraft's fog outright because by default
// minecraft will re-enable the fog after our code
RenderSystem.fogStart(0.0F);
RenderSystem.fogEnd(Float.MAX_VALUE);
RenderSystem.fogDensity(0.0F);
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);
}
}
@@ -526,87 +543,72 @@ public class LodRenderer
* (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
* accuracy vs the model view matrix, which only uses floats)
*/
private Matrix4f offsetTheModelViewMatrix(MatrixStack mcMatrixStack, float partialTicks)
private Matrix4f offsetTheModelViewMatrix(MatrixStack mcModelViewMatrix, float partialTicks)
{
// duplicate the last matrix
mcMatrixStack.pushPose();
mcModelViewMatrix.pushPose();
// get all relevant camera info
ActiveRenderInfo renderInfo = mc.getGameRenderer().getMainCamera();
Vector3d projectedView = renderInfo.getPosition();
ActiveRenderInfo camera = mc.getGameRenderer().getMainCamera();
Vector3d projectedView = camera.getPosition();
// translate the camera relative to the regions' center
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
// accuracy vs the model view matrix, which only uses floats)
BlockPos bufferPos = vbosCenter.getWorldPosition();
BlockPosWrapper bufferPos = vbosCenter.getWorldPosition();
double xDiff = projectedView.x - bufferPos.getX();
double zDiff = projectedView.z - bufferPos.getZ();
mcMatrixStack.translate(-xDiff, -projectedView.y, -zDiff);
mcModelViewMatrix.translate(-xDiff, -projectedView.y, -zDiff);
// get the modified model view matrix
Matrix4f lodModelViewMatrix = mcMatrixStack.last().pose();
Matrix4f lodModelViewMatrix = mcModelViewMatrix.last().pose();
// remove the lod ModelViewMatrix
mcMatrixStack.popPose();
mcModelViewMatrix.popPose();
return lodModelViewMatrix;
}
/**
* create a new projection matrix and send it over to the GPU
* 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 void setupProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks)
private Matrix4f createProjectionMatrix(Matrix4f currentProjectionMatrix, float vanillaBlockRenderedDistance, float partialTicks)
{
Matrix4f lodPoj;
float nearClipPlane = LodConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get() ? vanillaBlockRenderedDistance / 5 : 1;
float farClipPlane = farPlaneBlockDistance * LodUtil.CHUNK_WIDTH >> 1;
// create the new projection matrix
Matrix4f lodProj =
Matrix4f.perspective(
getFov(partialTicks, true),
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
LodConfig.CLIENT.graphics.advancedGraphicsOption.useExtendedNearClipPlane.get() ? vanillaBlockRenderedDistance / 5 : 1,
farPlaneBlockDistance * LodUtil.CHUNK_WIDTH / 2);
if (vivecraftDetected)
{
//use modify clip plane method to modify the current projection matrix's clip planes.
lodPoj = ReflectionHandler.INSTANCE.Matrix4fModifyClipPlanes(
currentProjectionMatrix,
nearClipPlane,
farClipPlane);
}
else
{
// create the new projection matrix
lodPoj = Matrix4f.perspective(
getFov(partialTicks, true),
(float) this.mc.getWindow().getScreenWidth() / (float) this.mc.getWindow().getScreenHeight(),
nearClipPlane,
farClipPlane);
// get Minecraft's un-edited projection matrix
// (this is before it is zoomed, distorted, etc.)
Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true);
// true here means use "use fov setting" (probably)
// this logic strips away the defaultMcProj matrix, so we
// can get the distortionMatrix, which represents all
// transformations, zooming, distortions, etc. done
// to Minecraft's Projection matrix
Matrix4f defaultMcProjInv = defaultMcProj.copy();
defaultMcProjInv.invert();
Matrix4f distortionMatrix = defaultMcProjInv.copy();
distortionMatrix.multiply(currentProjectionMatrix);
// edit the lod projection to match Minecraft's
// (so the LODs line up with the real world)
lodPoj.multiply(distortionMatrix);
}
// get Minecraft's un-edited projection matrix
// (this is before it is zoomed, distorted, etc.)
Matrix4f defaultMcProj = mc.getGameRenderer().getProjectionMatrix(mc.getGameRenderer().getMainCamera(), partialTicks, true);
// true here means use "use fov setting" (probably)
// send the projection over to the GPU
gameRender.resetProjectionMatrix(lodPoj);
// this logic strips away the defaultMcProj matrix, so we
// can get the distortionMatrix, which represents all
// transformations, zooming, distortions, etc. done
// to Minecraft's Projection matrix
Matrix4f defaultMcProjInv = defaultMcProj.copy();
defaultMcProjInv.invert();
Matrix4f distortionMatrix = defaultMcProjInv.copy();
distortionMatrix.multiply(currentProjectionMatrix);
// edit the lod projection to match Minecraft's
// (so the LODs line up with the real world)
lodProj.multiply(distortionMatrix);
return lodProj;
}
@@ -695,7 +697,7 @@ public class LodRenderer
lodBufferBuilder.destroyBuffers();
}
// TODO move this into the MC wrapper
private double getFov(float partialTicks, boolean useFovSetting)
{
return mc.getGameRenderer().getFov(mc.getGameRenderer().getMainCamera(), partialTicks, useFovSetting);
@@ -804,8 +806,6 @@ public class LodRenderer
/** Determines if the LODs should have a fullRegen or partialRegen */
private void determineIfLodsShouldRegenerate(LodDimension lodDim, float partialTicks)
{
short chunkRenderDistance = (short) mc.getRenderDistance();
int vanillaRenderedChunksWidth = chunkRenderDistance * 2 + 2;
@@ -815,14 +815,14 @@ public class LodRenderer
// check if the view distance changed
if (ClientProxy.previousLodRenderDistance != LodConfig.CLIENT.graphics.qualityOption.lodChunkRenderDistance.get()
|| chunkRenderDistance != prevRenderDistance
|| prevFogDistance != LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get())
|| chunkRenderDistance != prevRenderDistance
|| prevFogDistance != LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get())
{
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
DetailDistanceUtil.updateSettings();
fullRegen = true;
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayer().xChunk, mc.getPlayer().zChunk);
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayerChunkPos().getZ(), mc.getPlayerChunkPos().getZ());
prevFogDistance = LodConfig.CLIENT.graphics.fogQualityOption.fogDistance.get();
prevRenderDistance = chunkRenderDistance;
}
@@ -841,12 +841,12 @@ public class LodRenderer
if (newTime - prevPlayerPosTime > LodConfig.CLIENT.advancedModOptions.buffers.rebuildTimes.get().playerMoveTimeout)
{
if (LevelPosUtil.getDetailLevel(previousPos) == 0
|| mc.getPlayer().xChunk != LevelPosUtil.getPosX(previousPos)
|| mc.getPlayer().zChunk != LevelPosUtil.getPosZ(previousPos))
|| 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.getPlayer().xChunk, mc.getPlayer().zChunk);
previousPos = LevelPosUtil.createLevelPos((byte) 4, mc.getPlayerChunkPos().getX(), mc.getPlayerChunkPos().getZ());
}
prevPlayerPosTime = newTime;
}
@@ -875,17 +875,23 @@ public class LodRenderer
// 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.getOptions().gamma != prevBrightness)
// 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.getOptions().gamma != prevBrightness)
{
fullRegen = true;
prevBrightness = mc.getOptions().gamma;
prevSkyBrightness = skyBrightness;
}
/*if (lightMap != lastLightMap)
{
fullRegen = true;
lastLightMap = lightMap;
}*/
//================//
// partial regens //
//================//
@@ -921,21 +927,21 @@ public class LodRenderer
//==============//
// determine which LODs should not be rendered close to the player
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayer().blockPosition());
HashSet<ChunkPos> chunkPosToSkip = LodUtil.getNearbyLodChunkPosToSkip(lodDim, mc.getPlayerBlockPos());
int xIndex;
int zIndex;
for (ChunkPos pos : chunkPosToSkip)
{
vanillaRenderedChunksEmptySkip = false;
xIndex = (pos.x - mc.getPlayer().xChunk) + (chunkRenderDistance + 1);
zIndex = (pos.z - mc.getPlayer().zChunk) + (chunkRenderDistance + 1);
xIndex = (pos.x - mc.getPlayerChunkPos().getX()) + (chunkRenderDistance + 1);
zIndex = (pos.z - 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)
&& xIndex < vanillaRenderedChunks.length
&& zIndex < vanillaRenderedChunks.length)
{
if (!vanillaRenderedChunks[xIndex][zIndex])
{
@@ -954,6 +960,8 @@ public class LodRenderer
vanillaRenderedChunksChanged = true;
vanillaRenderedChunksEmptySkip = true;
}
vanillaRenderedChunks = new boolean[vanillaRenderedChunksWidth][vanillaRenderedChunksWidth];
}
}
@@ -21,10 +21,11 @@ package com.seibel.lod.render;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.MinecraftWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.util.math.vector.Vector3f;
/**
* This holds miscellaneous helper code
@@ -42,13 +43,13 @@ public class RenderUtil
* 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(ChunkPos pos, ChunkPos center)
public static boolean isChunkPosInLoadedArea(ChunkPosWrapper pos, ChunkPosWrapper center)
{
return (pos.x >= center.x - mc.getRenderDistance()
&& pos.x <= center.x + mc.getRenderDistance())
return (pos.getX() >= center.getX() - mc.getRenderDistance()
&& pos.getX() <= center.getX() + mc.getRenderDistance())
&&
(pos.z >= center.z - mc.getRenderDistance()
&& pos.z <= center.z + mc.getRenderDistance());
(pos.getZ() >= center.getZ() - mc.getRenderDistance()
&& pos.getZ() <= center.getZ() + mc.getRenderDistance());
}
/**
@@ -85,22 +86,24 @@ public class RenderUtil
* Returns true if one of the region's 4 corners is in front
* of the camera.
*/
public static boolean isRegionInViewFrustum(BlockPos playerBlockPos, Vector3d cameraDir, BlockPos vboCenterPos)
public static boolean isRegionInViewFrustum(BlockPos playerBlockPos, Vector3f cameraDir, BlockPosWrapper vboCenterPos)
{
// convert the vbo position into a direction vector
// starting from the player's position
Vector3d vboVec = new Vector3d(vboCenterPos.getX(), 0, vboCenterPos.getZ());
Vector3d playerVec = new Vector3d(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ());
Vector3d vboCenterVec = vboVec.subtract(playerVec);
Vector3f vboVec = new Vector3f(vboCenterPos.getX(), 0, vboCenterPos.getZ());
Vector3f playerVec = new Vector3f(playerBlockPos.getX(), playerBlockPos.getY(), playerBlockPos.getZ());
vboVec.sub(playerVec);
Vector3f vboCenterVec = vboVec;
int halfRegionWidth = LodUtil.REGION_WIDTH / 2;
// calculate the 4 corners
Vector3d vboSeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);
Vector3d vboSwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z + halfRegionWidth);
Vector3d vboNwVec = new Vector3d(vboCenterVec.x - halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
Vector3d vboNeVec = new Vector3d(vboCenterVec.x + halfRegionWidth, vboCenterVec.y, vboCenterVec.z - halfRegionWidth);
Vector3f vboSeVec = new Vector3f(vboCenterVec.x() + halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() + halfRegionWidth);
Vector3f vboSwVec = new Vector3f(vboCenterVec.x() - halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() + halfRegionWidth);
Vector3f vboNwVec = new Vector3f(vboCenterVec.x() - halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() - halfRegionWidth);
Vector3f vboNeVec = new Vector3f(vboCenterVec.x() + halfRegionWidth, vboCenterVec.y(), vboCenterVec.z() - halfRegionWidth);
// if any corner is visible, this region should be rendered
return isNormalizedVectorInViewFrustum(vboSeVec, cameraDir) ||
@@ -113,7 +116,7 @@ public class RenderUtil
* Currently takes the dot product of the two vectors,
* but in the future could do more complicated frustum culling tests.
*/
private static boolean isNormalizedVectorInViewFrustum(Vector3d objectVector, Vector3d cameraDir)
private static boolean isNormalizedVectorInViewFrustum(Vector3f objectVector, Vector3f cameraDir)
{
// the -0.1 is to offer a slight buffer, so we are
// more likely to render LODs and thus, hopefully prevent
@@ -0,0 +1,116 @@
/*
* 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.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.proxy.ClientProxy;
/**
* 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)
{
ClientProxy.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));
}
}
@@ -0,0 +1,184 @@
/*
* 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.render.shader;
import java.nio.FloatBuffer;
import org.lwjgl.opengl.GL20;
import org.lwjgl.system.MemoryStack;
import net.minecraft.util.math.vector.Matrix4f;
/**
* 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, Matrix4f value)
{
try (MemoryStack stack = MemoryStack.stackPush())
{
FloatBuffer buffer = stack.mallocFloat(4 * 4);
value.store(buffer);
GL20.glUniformMatrix4fv(location, false, buffer);
}
}
}
@@ -29,6 +29,8 @@ import com.seibel.lod.enums.HorizontalResolution;
import com.seibel.lod.enums.VanillaOverdraw;
import com.seibel.lod.objects.LodDimension;
import com.seibel.lod.objects.RegionPos;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import com.seibel.lod.wrappers.MinecraftWrapper;
import net.minecraft.client.multiplayer.ServerData;
@@ -349,10 +351,10 @@ public class LodUtil
* Get a HashSet of all ChunkPos within the normal render distance
* that should not be rendered.
*/
public static HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPos playerPos)
public static HashSet<ChunkPos> getNearbyLodChunkPosToSkip(LodDimension lodDim, BlockPosWrapper blockPosWrapper)
{
int chunkRenderDist = mc.getRenderDistance();
ChunkPos centerChunk = new ChunkPos(playerPos);
ChunkPosWrapper centerChunk = new ChunkPosWrapper(blockPosWrapper);
int skipRadius;
VanillaOverdraw overdraw = LodConfig.CLIENT.graphics.advancedGraphicsOption.vanillaOverdraw.get();
@@ -426,12 +428,12 @@ public class LodUtil
// if the skipRadius is being used
if (skipRadius != 0)
{
for (int x = centerChunk.x - chunkRenderDist; x < centerChunk.x + chunkRenderDist; x++)
for (int x = centerChunk.getX() - chunkRenderDist; x < centerChunk.getX() + chunkRenderDist; x++)
{
for (int z = centerChunk.z - chunkRenderDist; z < centerChunk.z + chunkRenderDist; z++)
for (int z = centerChunk.getZ() - chunkRenderDist; z < centerChunk.getZ() + chunkRenderDist; z++)
{
if (x <= centerChunk.x - skipRadius || x >= centerChunk.x + skipRadius
|| z <= centerChunk.z - skipRadius || z >= centerChunk.z + skipRadius)
if (x <= centerChunk.getX() - skipRadius || x >= centerChunk.getX() + skipRadius
|| z <= centerChunk.getZ() - skipRadius || z >= centerChunk.getZ() + skipRadius)
posToSkip.remove(new ChunkPos(x, z));
}
@@ -19,8 +19,6 @@
package com.seibel.lod.util;
import static com.seibel.lod.util.LodUtil.DETAIL_OPTIONS;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@@ -80,7 +78,7 @@ public class ThreadMapUtil
|| (adjDataMap.get(Thread.currentThread().getName()).get(Direction.NORTH) == null)
|| (adjDataMap.get(Thread.currentThread().getName()).get(Direction.NORTH).length != verticalData))
{
adjDataMap.put(Thread.currentThread().getName(), new HashMap<Direction, long[]>());
adjDataMap.put(Thread.currentThread().getName(), new HashMap<>());
adjDataMap.get(Thread.currentThread().getName()).put(Direction.UP, new long[1]);
adjDataMap.get(Thread.currentThread().getName()).put(Direction.DOWN, new long[1]);
for (Direction direction : Box.ADJ_DIRECTIONS)
@@ -142,9 +140,9 @@ public class ThreadMapUtil
{
if (!saveContainer.containsKey(Thread.currentThread().getName()) || (saveContainer.get(Thread.currentThread().getName()) == null))
{
byte[][] array = new byte[DETAIL_OPTIONS][];
byte[][] array = new byte[LodUtil.DETAIL_OPTIONS][];
int size = 1;
for (int i = DETAIL_OPTIONS - 1; i >= 0; i--)
for (int i = LodUtil.DETAIL_OPTIONS - 1; i >= 0; i--)
{
array[i] = new byte[2 + 8 * size * size * DetailDistanceUtil.getMaxVerticalData(i)];
size = size << 1;
@@ -188,8 +186,8 @@ public class ThreadMapUtil
{
if (!verticalUpdate.containsKey(Thread.currentThread().getName()) || (verticalUpdate.get(Thread.currentThread().getName()) == null))
{
long[][] array = new long[DETAIL_OPTIONS][];
for (int i = 1; i < DETAIL_OPTIONS; i++)
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);
}
@@ -22,7 +22,7 @@ public class BlockColorWrapper
//set of block which require tint
public static final ConcurrentMap<Block, BlockColorWrapper> blockColorWrapperMap = new ConcurrentHashMap<>();
public static final ModelDataMap dataMap = new ModelDataMap.Builder().build();
public static final BlockPos blockPos = new BlockPos(0,0,0);
public static final BlockPos blockPos = new BlockPos(0, 0, 0);
public static final Random random = new Random(0);
//public static BlockColourWrapper WATER_COLOR = getBlockColorWrapper(Blocks.WATER);
public static final Direction[] directions = new Direction[] { Direction.UP, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.NORTH, Direction.DOWN };
@@ -37,33 +37,51 @@ public class BlockColorWrapper
/**Constructor only require for the block instance we are wrapping**/
public BlockColorWrapper(BlockState blockState, BlockPosWrapper blockPosWrapper)
public BlockColorWrapper(Block block)
{
this.block = blockState.getBlock();
this.block = block;
this.color = 0;
this.isColored = true;
this.toTint = false;
this.foliageTint = false;
this.grassTint = false;
this.waterTint = false;
setupColorAndTint(blockState,blockPosWrapper);
setupColorAndTint();
/*StringBuilder s = new StringBuilder();
s.append(block + "\n"
+ Integer.toHexString(
Minecraft.getInstance().getBlockColors().createDefault().getColor(
block.defaultBlockState(),
(World) MinecraftWrapper.INSTANCE.getWrappedServerLevel().getLevel(),
blockPosWrapper.getBlockPos())) + "\n"
);
for(Property x : Minecraft.getInstance().getBlockColors().getColoringProperties(block))
s.append(x.getName() + " " + x.getPossibleValues() + '\n');
System.out.println(s);*/
//System.out.println(block + " color " + Integer.toHexString(color) + " to tint " + toTint + " folliageTint " + folliageTint + " grassTint " + grassTint + " waterTint " + waterTint);
}
/**
* this return a wrapper of the block in input
* @param blockState of the block to wrap
* return base color of water (grey value)
*/
static public BlockColorWrapper getBlockColorWrapper(BlockState blockState, BlockPosWrapper blockPosWrapper)
static public BlockColorWrapper getWaterColor()
{
return getBlockColorWrapper(Blocks.WATER);
}
/**
* this return a wrapper of the block in input
* @param block object of the block to wrap
*/
static public BlockColorWrapper getBlockColorWrapper(Block block)
{
//first we check if the block has already been wrapped
if (blockColorWrapperMap.containsKey(blockState.getBlock()) && blockColorWrapperMap.get(blockState.getBlock()) != null)
return blockColorWrapperMap.get(blockState.getBlock());
if (blockColorWrapperMap.containsKey(block) && blockColorWrapperMap.get(block) != null)
return blockColorWrapperMap.get(block);
//if it hasn't been created yet, we create it and save it in the map
BlockColorWrapper blockWrapper = new BlockColorWrapper(blockState, blockPosWrapper);
blockColorWrapperMap.put(blockState.getBlock(), blockWrapper);
BlockColorWrapper blockWrapper = new BlockColorWrapper(block);
blockColorWrapperMap.put(block, blockWrapper);
//we return the newly created wrapper
return blockWrapper;
@@ -73,8 +91,10 @@ public class BlockColorWrapper
* Generate the color of the given block from its texture
* and store it for later use.
*/
private void setupColorAndTint(BlockState blockState, BlockPosWrapper blockPosWrapper)
private void setupColorAndTint()
{
BlockState blockState = block.defaultBlockState();
BlockPosWrapper blockPosWrapper = new BlockPosWrapper();
MinecraftWrapper mc = MinecraftWrapper.INSTANCE;
TextureAtlasSprite texture;
List<BakedQuad> quads = null;
@@ -114,7 +134,7 @@ public class BlockColorWrapper
else
{
isColored = true;
texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientWorld(), blockPosWrapper.getBlockPos());
texture = mc.getModelManager().getBlockModelShaper().getTexture(block.defaultBlockState(), mc.getClientLevel(), blockPosWrapper.getBlockPos());
}
int count = 0;
@@ -229,7 +249,7 @@ public class BlockColorWrapper
public int getColor()
{
return color;
return color;
}
//------------//
@@ -1,64 +1,67 @@
package com.seibel.lod.wrappers.Block;
import com.seibel.lod.util.ColorUtil;
import net.minecraft.block.*;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.client.model.data.ModelDataMap;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class BlockPosWrapper {
private final BlockPos.Mutable blockPos;
//This class wraps the minecraft BlockPos.Mutable (and BlockPos) class
public class BlockPosWrapper
{
private final BlockPos.Mutable blockPos;
public BlockPosWrapper()
{
this.blockPos = new BlockPos.Mutable(0,0,0);
}
public void set(int x, int y, int z)
{
blockPos.set(x, y, z);
}
public int getX()
{
return blockPos.getX();
}
public int getY()
{
return blockPos.getY();
}
public int getZ()
{
return blockPos.getZ();
}
public BlockPos.Mutable getBlockPos()
{
return blockPos;
}
@Override public boolean equals(Object o)
{
return blockPos.equals(o);
}
@Override public int hashCode()
{
return Objects.hash(blockPos);
}
public BlockPosWrapper()
{
this.blockPos = new BlockPos.Mutable(0,0,0);
}
public BlockPosWrapper(int x, int y, int z)
{
this.blockPos = new BlockPos.Mutable(x, y, z);
}
public void set(int x, int y, int z)
{
blockPos.set(x, y, z);
}
public int getX()
{
return blockPos.getX();
}
public int getY()
{
return blockPos.getY();
}
public int getZ()
{
return blockPos.getZ();
}
public int get(Direction.Axis axis)
{
return axis.choose(getX(), getY(), getZ());
}
public BlockPos.Mutable getBlockPos()
{
return blockPos;
}
@Override public boolean equals(Object o)
{
return blockPos.equals(o);
}
@Override public int hashCode()
{
return Objects.hash(blockPos);
}
public BlockPosWrapper offset(int x, int y, int z)
{
blockPos.set(blockPos.getX() + x, blockPos.getY() + y, blockPos.getZ() + z);
return this;
}
}
@@ -23,7 +23,7 @@ public class BlockShapeWrapper
public static BlockShapeWrapper WATER_SHAPE = new BlockShapeWrapper();
private final Block block;
private final boolean toAvoid;
private boolean toAvoid;
private boolean nonFull;
private boolean noCollision;
@@ -1,5 +1,7 @@
package com.seibel.lod.wrappers.Chunk;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
@@ -11,9 +13,30 @@ public class ChunkPosWrapper
{
private final ChunkPos chunkPos;
public ChunkPosWrapper(ChunkPos chunkPos)
public ChunkPosWrapper(ChunkPos newChunkPos)
{
this.chunkPos = newChunkPos;
}
public ChunkPosWrapper(BlockPos blockPos)
{
this.chunkPos = new ChunkPos(blockPos);
}
public ChunkPosWrapper(ChunkPosWrapper newChunkPos)
{
this.chunkPos = newChunkPos.chunkPos;
}
public ChunkPosWrapper(BlockPosWrapper blockPos)
{
this.chunkPos = chunkPos;
this.chunkPos = new ChunkPos(blockPos.getBlockPos());
}
public ChunkPosWrapper(int chunkX, int chunkZ)
{
this.chunkPos = new ChunkPos(chunkX, chunkZ);
}
public int getX()
@@ -61,4 +84,9 @@ public class ChunkPosWrapper
return Objects.hash(chunkPos);
}
public BlockPosWrapper getWorldPosition()
{
BlockPos blockPos = chunkPos.getWorldPosition();
return new BlockPosWrapper(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
}
@@ -26,9 +26,8 @@ public class ChunkWrapper
BlockState blockState = chunk.getBlockState(blockPos.getBlockPos());
//This type of block is always in water
//This type of block could be in water
return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
|| (blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get());
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
public int getHeightMapValue(int xRel, int zRel){
@@ -42,7 +41,7 @@ public class ChunkWrapper
public BlockColorWrapper getBlockColorWrapper(BlockPosWrapper blockPos)
{
return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(blockPos.getBlockPos()),blockPos);
return BlockColorWrapper.getBlockColorWrapper(chunk.getBlockState(blockPos.getBlockPos()).getBlock());
}
public BlockShapeWrapper getBlockShapeWrapper(BlockPosWrapper blockPos)
@@ -73,9 +72,8 @@ public class ChunkWrapper
BlockState blockState = chunk.getBlockState(blockPos.getBlockPos());
//This type of block is always in water
//This type of block could be in water
return ((blockState.getBlock() instanceof ILiquidContainer) && !(blockState.getBlock() instanceof IWaterLoggable))
|| (blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).isPresent() && blockState.getOptionalValue(BlockStateProperties.WATERLOGGED).get());
|| (blockState.hasProperty(BlockStateProperties.WATERLOGGED) && blockState.getValue(BlockStateProperties.WATERLOGGED));
}
public int getEmittedBrightness(BlockPosWrapper blockPos)
@@ -26,7 +26,9 @@ import com.seibel.lod.ModInfo;
import com.seibel.lod.proxy.ClientProxy;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.World.WorldWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import com.seibel.lod.wrappers.Chunk.ChunkPosWrapper;
import com.seibel.lod.wrappers.World.LevelWrapper;
import net.minecraft.client.GameSettings;
import net.minecraft.client.MainWindow;
import net.minecraft.client.Minecraft;
@@ -44,6 +46,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.profiler.IProfiler;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.DimensionType;
import net.minecraft.world.server.ServerWorld;
@@ -170,6 +173,17 @@ public class MinecraftWrapper
return mc.player;
}
public BlockPosWrapper getPlayerBlockPos()
{
BlockPos playerPos = getPlayer().blockPosition();
return new BlockPosWrapper(playerPos.getX(), playerPos.getY(), playerPos.getZ());
}
public ChunkPosWrapper getPlayerChunkPos()
{
return new ChunkPosWrapper(getPlayer().xChunk, getPlayer().zChunk);
}
public GameSettings getOptions()
{
return mc.options;
@@ -180,17 +194,17 @@ public class MinecraftWrapper
return mc.getModelManager();
}
public ClientWorld getClientWorld()
public ClientWorld getClientLevel()
{
return mc.level;
}
public WorldWrapper getWrappedClientWorld()
public LevelWrapper getWrappedClientLevel()
{
return WorldWrapper.getWorldWrapper(mc.level);
return LevelWrapper.getLevelWrapper(mc.level);
}
public WorldWrapper getWrappedServerWorld()
public LevelWrapper getWrappedServerLevel()
{
if (mc.level == null)
@@ -212,7 +226,7 @@ public class MinecraftWrapper
}
}
return WorldWrapper.getWorldWrapper(returnWorld);
return LevelWrapper.getLevelWrapper(returnWorld);
}
/** Measured in chunks */
@@ -1,33 +1,24 @@
package com.seibel.lod.wrappers.World;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.block.Blocks;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeColors;
import java.awt.*;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class BiomeColorWrapper
{
public static int getGrassColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper)
public static int getGrassColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper)
{
return BiomeColors.getAverageGrassColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos());
return BiomeColors.getAverageGrassColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos());
}
public static int getWaterColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper)
public static int getWaterColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper)
{
return BiomeColors.getAverageWaterColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos());
return BiomeColors.getAverageWaterColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos());
}
public static int getFoliageColor(WorldWrapper worldWrapper, BlockPosWrapper blockPosWrapper)
public static int getFoliageColor(LevelWrapper levelWrapper, BlockPosWrapper blockPosWrapper)
{
return BiomeColors.getAverageFoliageColor(worldWrapper.getWorld(), blockPosWrapper.getBlockPos());
return BiomeColors.getAverageFoliageColor(levelWrapper.getWorld(), blockPosWrapper.getBlockPos());
}
}
@@ -1,16 +1,10 @@
package com.seibel.lod.wrappers.World;
import com.seibel.lod.util.ColorUtil;
import com.seibel.lod.util.LodUtil;
import com.seibel.lod.wrappers.Block.BlockColorWrapper;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.block.Blocks;
import net.minecraft.block.material.MaterialColor;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeColors;
import java.awt.*;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -48,57 +42,72 @@ public class BiomeWrapper
public int getColorForBiome(int x, int z)
{
int colorInt;
int tintValue = 0;
switch (biome.getBiomeCategory())
{
case NETHER:
colorInt = Blocks.NETHERRACK.defaultBlockState().materialColor.col;
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.NETHERRACK).getColor();
break;
case THEEND:
colorInt = Blocks.END_STONE.defaultBlockState().materialColor.col;
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.END_STONE).getColor();
break;
case BEACH:
case DESERT:
colorInt = Blocks.SAND.defaultBlockState().materialColor.col;
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SAND).getColor();
break;
case EXTREME_HILLS:
colorInt = Blocks.STONE.defaultMaterialColor().col;
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.STONE).getColor();
break;
case MUSHROOM:
colorInt = MaterialColor.COLOR_LIGHT_GRAY.col;
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.MYCELIUM).getColor();
break;
case ICY:
colorInt = Blocks.SNOW.defaultMaterialColor().col;
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SNOW).getColor();
break;
case MESA:
colorInt = Blocks.RED_SAND.defaultMaterialColor().col;
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.RED_SAND).getColor();
break;
case OCEAN:
case RIVER:
colorInt = biome.getWaterColor();
colorInt = BlockColorWrapper.getWaterColor().getColor();
tintValue = biome.getWaterColor();
break;
case PLAINS:
case SAVANNA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.GRASS_BLOCK).getColor();
tintValue = biome.getGrassColor(x, z);
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case TAIGA:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.SPRUCE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case JUNGLE:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.JUNGLE_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
case NONE:
case FOREST:
case TAIGA:
case JUNGLE:
case PLAINS:
case SAVANNA:
case SWAMP:
default:
Color tmp = LodUtil.intToColor(biome.getGrassColor(x, z));
tmp = tmp.darker();
colorInt = LodUtil.colorToInt(tmp);
case SWAMP:
case FOREST:
colorInt = BlockColorWrapper.getBlockColorWrapper(Blocks.OAK_LEAVES).getColor();
tintValue = biome.getFoliageColor();
colorInt = ColorUtil.multiplyRGBcolors(colorInt,tintValue);
break;
}
return colorInt;
@@ -0,0 +1,80 @@
package com.seibel.lod.wrappers.World;
import com.seibel.lod.wrappers.Block.BlockPosWrapper;
import net.minecraft.world.IWorld;
import net.minecraft.world.LightType;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class LevelWrapper
{
private static final ConcurrentMap<IWorld, LevelWrapper> worldWrapperMap = new ConcurrentHashMap<>();
private final IWorld world;
public LevelWrapper(IWorld world)
{
this.world = world;
}
public static LevelWrapper getLevelWrapper(IWorld world)
{
//first we check if the biome has already been wrapped
if(worldWrapperMap.containsKey(world) && worldWrapperMap.get(world) != null)
return worldWrapperMap.get(world);
//if it hasn't been created yet, we create it and save it in the map
LevelWrapper levelWrapper = new LevelWrapper(world);
worldWrapperMap.put(world, levelWrapper);
//we return the newly created wrapper
return levelWrapper;
}
public static void clearMap()
{
worldWrapperMap.clear();
}
public DimensionTypeWrapper getDimensionType()
{
return DimensionTypeWrapper.getDimensionTypeWrapper(world.dimensionType());
}
public int getBlockLight(BlockPosWrapper blockPos)
{
return world.getBrightness(LightType.BLOCK, blockPos.getBlockPos());
}
public int getSkyLight(BlockPosWrapper blockPos)
{
return world.getBrightness(LightType.SKY, blockPos.getBlockPos());
}
public BiomeWrapper getBiome(BlockPosWrapper blockPos)
{
return BiomeWrapper.getBiomeWrapper(world.getBiome(blockPos.getBlockPos()));
}
public IWorld getWorld()
{
return world;
}
public boolean hasCeiling()
{
return world.dimensionType().hasCeiling();
}
public boolean hasSkyLight()
{
return world.dimensionType().hasSkyLight();
}
public boolean isEmpty()
{
return world == null;
}
}
+4 -1
View File
@@ -38,6 +38,9 @@ displayURL="https://www.curseforge.com/minecraft/mc-mods/lod-level-of-detail" #o
#// A file name (in the root of the mod JAR) containing a logo for display
logoFile="logo.png" #optional
#// A file name (in the root of the mod JAR) containing a icon for display by catalogue
catalogueImageIcon="icon.png"
#// A text field displayed in the mod UI
credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional
@@ -45,4 +48,4 @@ credits="TechnoVision, Vike, and Darkhax for their modding tutorials." #optional
authors="James Seibel, Leonardo Amato, and Cola" #optional
#// The description text for the mod (multi line!) (#mandatory)
description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.'''
description='''This mod generates and renders simplified terrain beyond the normal view distance, at a low performance cost.'''
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+26
View File
@@ -0,0 +1,26 @@
#version 150 core
in vec4 vertexColor;
//in vec2 textureCoord;
out vec4 fragColor;
//uniform sampler2D texImage;
/**
* Fragment Shader
*
* author: James Seibel
* version: 11-8-2021
*/
void main()
{
// TODO: add a white texture to support Optifine shaders
//vec4 textureColor = texture(texImage, textureCoord);
//fragColor = vertexColor * textureColor;
// very simple fragment shader, just return the vertix's color
fragColor = vertexColor;
}
+29
View File
@@ -0,0 +1,29 @@
#version 150 core
in vec3 vPosition;
in vec4 color;
out vec4 vertexColor;
//out vec2 textureCoord;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
/**
* Vertex Shader
*
* author: James Seibel
* version: 11-8-2021
*/
void main()
{
// TODO: add a simple white texture to support Optifine shaders
//textureCoord = textureCoord;
vertexColor = color;
// the vPosition needs to be converted to a vec4 so it can be multiplied
// by the 4x4 matrices
gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1);
}