Closes #85 (Sodium Overdraw incompatibility)
One minor issue: Sodium returns chunks differently than vanilla MC or Optifine. Specifically as of 1.16.5 (12-12-2021) it also returns one layer of chunks further than what is currently rendered.
This commit is contained in:
@@ -19,8 +19,10 @@
|
||||
|
||||
package com.seibel.lod.core.handlers;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
@@ -35,7 +37,7 @@ import com.seibel.lod.core.objects.math.Mat4f;
|
||||
* different MC versions.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-26-2021
|
||||
* @version 12-12-2021
|
||||
*/
|
||||
public interface IReflectionHandler
|
||||
{
|
||||
@@ -45,14 +47,9 @@ public interface IReflectionHandler
|
||||
/** @returns if Vivecraft is present. Attempts to find the "VRRenderer" class. */
|
||||
boolean vivecraftPresent();
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane);
|
||||
/** @returns if Sodium (or a sodium like) mod is present. Attempts to find the "SodiumWorldRenderer" class. */
|
||||
boolean sodiumPresent();
|
||||
|
||||
/** @returns a HashSet containing every chunk that is currently being rendered by Sodium. */
|
||||
HashSet<AbstractChunkPosWrapper> getSodiumRenderedChunks();
|
||||
}
|
||||
|
||||
@@ -20,39 +20,57 @@
|
||||
package com.seibel.lod.core.handlers;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.lod.core.ModInfo;
|
||||
import com.seibel.lod.core.enums.rendering.FogDrawMode;
|
||||
import com.seibel.lod.core.objects.math.Mat4f;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongIterator;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
|
||||
/**
|
||||
* A singleton used to get variables from methods
|
||||
* where they are private or potentially absent.
|
||||
* Specifically the fog setting in Optifine or the
|
||||
* presence/absence of other mods.
|
||||
* For example: the fog setting in Optifine or the
|
||||
* presence/absence of Vivecraft.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-26-2021
|
||||
* @version 12-12-2021
|
||||
*/
|
||||
public class ReflectionHandler implements IReflectionHandler
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger(ModInfo.NAME + "-" + ReflectionHandler.class.getSimpleName());
|
||||
|
||||
private static ReflectionHandler instance;
|
||||
private final IWrapperFactory wrapperFactory;
|
||||
|
||||
public static ReflectionHandler instance;
|
||||
|
||||
private Field ofFogField = null;
|
||||
private final Object mcOptionsObject;
|
||||
|
||||
private Boolean sodiumPresent = null;
|
||||
private Field sodiumLoadedChunkPositionsField = null;
|
||||
private Object sodiumWorldRendererInstance = null;
|
||||
|
||||
|
||||
|
||||
|
||||
private ReflectionHandler(Field[] optionFields, Object newMcOptionsObject)
|
||||
{
|
||||
wrapperFactory = SingletonHandler.get(IWrapperFactory.class);
|
||||
|
||||
mcOptionsObject = newMcOptionsObject;
|
||||
|
||||
setupFogField(optionFields);
|
||||
|
||||
if (sodiumPresent())
|
||||
setupSodiumInteraction();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,6 +93,8 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** finds the Optifine fog type field */
|
||||
private void setupFogField(Field[] optionFields)
|
||||
{
|
||||
@@ -155,41 +175,119 @@ public class ReflectionHandler implements IReflectionHandler
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the projection matrix's clip planes.
|
||||
* The projection matrix must be in column-major format.
|
||||
*
|
||||
* @param projectionMatrix The projection matrix to be modified.
|
||||
* @param newNearClipPlane the new near clip plane value.
|
||||
* @param newFarClipPlane the new far clip plane value.
|
||||
* @return The modified matrix.
|
||||
*/
|
||||
@Override
|
||||
public Mat4f ModifyProjectionClipPlanes(Mat4f projectionMatrix, float newNearClipPlane, float newFarClipPlane)
|
||||
|
||||
|
||||
|
||||
private void setupSodiumInteraction()
|
||||
{
|
||||
// find the matrix values.
|
||||
float nearMatrixValue = -((newFarClipPlane + newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
float farMatrixValue = -((2 * newFarClipPlane * newNearClipPlane) / (newFarClipPlane - newNearClipPlane));
|
||||
String errorMessagePrfix = ReflectionHandler.class.getSimpleName() + ": was unable to setup Sodium interaction. Error: ";
|
||||
|
||||
try
|
||||
{
|
||||
// TODO this was originally created before we had the Mat4f object,
|
||||
// so this doesn't need to be done with reflection anymore.
|
||||
// And should be moved to RenderUtil
|
||||
// try getting the SodiumWorldRender class
|
||||
Class<?> sodiumWorldRendererClass = Class.forName("me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer");
|
||||
|
||||
// get the fields of the projectionMatrix
|
||||
Field[] fields = projectionMatrix.getClass().getDeclaredFields();
|
||||
// bypass the security protections on the fields that encode near and far plane values.
|
||||
fields[10].setAccessible(true);
|
||||
fields[11].setAccessible(true);
|
||||
// Change the values of the near and far plane.
|
||||
fields[10].set(projectionMatrix, nearMatrixValue);
|
||||
fields[11].set(projectionMatrix, farMatrixValue);
|
||||
Field sodiumWorldRendererInstanceField = sodiumWorldRendererClass.getDeclaredField("instance");
|
||||
sodiumWorldRendererInstanceField.setAccessible(true); // the field is private by default
|
||||
|
||||
try
|
||||
{
|
||||
// try getting the singleton from the static field
|
||||
sodiumWorldRendererInstance = sodiumWorldRendererInstanceField.get(null);
|
||||
|
||||
try
|
||||
{
|
||||
// try getting the loadedChunkPosition field
|
||||
Field loadedPosField = sodiumWorldRendererInstance.getClass().getDeclaredField("loadedChunkPositions");
|
||||
loadedPosField.setAccessible(true);
|
||||
|
||||
sodiumLoadedChunkPositionsField = loadedPosField;
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
LOGGER.info(errorMessagePrfix + " no loadedChunkPositions field.", e);
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
LOGGER.info(errorMessagePrfix + " no sodiumWorldRenderer instance.", e);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
catch (NoSuchFieldException | SecurityException | ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
LOGGER.info(errorMessagePrfix + " no sodiumWorldRenderer class.", e);
|
||||
}
|
||||
return projectionMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sodiumPresent()
|
||||
{
|
||||
// we don't want to run a potentially expensive
|
||||
// reflection search operation every time this method is called
|
||||
if (sodiumPresent == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Class.forName("me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer");
|
||||
|
||||
sodiumPresent = true;
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
sodiumPresent = false;
|
||||
}
|
||||
}
|
||||
|
||||
return sodiumPresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: this returns chunks that aren't actually rendered.
|
||||
* Specifically as of 1.16.5 (12-12-2021) it also returns one layer
|
||||
* of chunks further than what is currently rendered.
|
||||
*/
|
||||
@Override
|
||||
public HashSet<AbstractChunkPosWrapper> getSodiumRenderedChunks()
|
||||
{
|
||||
if (!sodiumPresent())
|
||||
{
|
||||
throw new IllegalStateException("[getSodiumRenderedChunks] can only be called if Sodium is installed.");
|
||||
}
|
||||
|
||||
if (sodiumLoadedChunkPositionsField == null || sodiumWorldRendererInstance == null)
|
||||
{
|
||||
throw new IllegalStateException("[getSodiumRenderedChunks] was called either before the sodium setup was done, or the sodium setup failed.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
HashSet<AbstractChunkPosWrapper> loadedPos = new HashSet<>();
|
||||
|
||||
try
|
||||
{
|
||||
LongSet loadedChunkPositions = (LongSet) sodiumLoadedChunkPositionsField.get(sodiumWorldRendererInstance);
|
||||
|
||||
LongIterator iterator = loadedChunkPositions.iterator();
|
||||
while (iterator.hasNext())
|
||||
{
|
||||
loadedPos.add(wrapperFactory.createChunkPos(iterator.nextLong()));
|
||||
}
|
||||
}
|
||||
catch (IllegalArgumentException | IllegalAccessException e)
|
||||
{
|
||||
LOGGER.error("Unable to get sodium's rendered chunks" + e.getMessage(), e);
|
||||
}
|
||||
|
||||
|
||||
// // can be uncommented for debugging
|
||||
// StringBuilder builder = new StringBuilder(loadedPos.size() * 4);
|
||||
// for(AbstractChunkPosWrapper pos : loadedPos)
|
||||
// {
|
||||
// builder.append("(" + pos.getX() + "," + pos.getZ() + ") ");
|
||||
// }
|
||||
// ClientApi.LOGGER.info(builder.toString());
|
||||
|
||||
return loadedPos;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,13 @@ import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
import com.seibel.lod.forge.wrappers.chunk.ChunkPosWrapper;
|
||||
|
||||
/**
|
||||
* This handles creating abstract wrapper objects.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 12-11-2021
|
||||
* @version 12-12-2021
|
||||
*/
|
||||
public interface IWrapperFactory
|
||||
{
|
||||
@@ -39,6 +40,13 @@ public interface IWrapperFactory
|
||||
|
||||
|
||||
AbstractChunkPosWrapper createChunkPos();
|
||||
public default AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined)
|
||||
{
|
||||
int x = (int) (xAndZPositionCombined & Integer.MAX_VALUE);
|
||||
int z = (int) (xAndZPositionCombined >> Long.SIZE / 2) & Integer.MAX_VALUE;
|
||||
|
||||
return new ChunkPosWrapper(x, z);
|
||||
}
|
||||
AbstractChunkPosWrapper createChunkPos(int x, int z);
|
||||
AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos);
|
||||
AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
Reference in New Issue
Block a user