Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.vmstudio.visor.compatibility;

import org.jetbrains.annotations.Nullable;
import org.vmstudio.visor.api.client.render.VRRenderPass;
import org.vmstudio.visor.api.client.settings.VRClientSettings;
import org.vmstudio.visor.core.client.VisorState;
import org.vmstudio.visor.core.client.render.VRRenderState;

public class LodPassHelper {
@Nullable
public static VRRenderPass currentWorldPass() {
if (VisorState.get().isNotActive()
|| VRRenderState.getPhase().isNotVRWorld()) {
return null;
}

VRRenderPass renderPass = VRRenderState.getRenderPass();
return renderPass != null && renderPass.isWorld() ? renderPass : null;
}

public static boolean isVrWorldPass() {
return currentWorldPass() != null;
}

public static boolean isVrEyeWorldPass() {
VRRenderPass renderPass = currentWorldPass();
return renderPass != null && renderPass.isEye();
}

public static boolean shouldSkipMirrorPass() {
VRRenderPass renderPass = currentWorldPass();
if (renderPass == null || renderPass.isEye()) {
return false;
}

return !VRClientSettings.isDhMirrorPasses();
}
}
Original file line number Diff line number Diff line change
@@ -1,43 +1,23 @@
package org.vmstudio.visor.compatibility.dh;

import org.vmstudio.visor.api.ModLoader;
import org.vmstudio.visor.api.client.render.VRRenderPass;
import org.vmstudio.visor.core.client.VisorState;
import org.vmstudio.visor.core.client.render.VRRenderState;
import org.vmstudio.visor.api.client.settings.VRClientSettings;
import org.vmstudio.visor.compatibility.LodPassHelper;

public final class DhCompatHelper {
public static final String MOD_ID = "distanthorizons";

private DhCompatHelper() {
throw new UnsupportedOperationException("Utility class");
}

public static boolean isLoaded() {
return ModLoader.get().isModLoaded(MOD_ID);
}

public static boolean shouldSkipDhRender() {
if (VisorState.get().isNotActive()
|| VRRenderState.getPhase().isNotVRWorld()) {
return false;
}

VRRenderPass renderPass = VRRenderState.getRenderPass();
if (renderPass == null || !renderPass.isWorld() || renderPass.isEye()) {
return false;
}
return LodPassHelper.shouldSkipMirrorPass();
}

return !VRClientSettings.isDhMirrorPasses();
public static boolean isVrWorldPass() {
return LodPassHelper.isVrWorldPass();
}

public static boolean isVrEyeWorldPass() {
if (VisorState.get().isNotActive()
|| VRRenderState.getPhase().isNotVRWorld()) {
return false;
}

VRRenderPass renderPass = VRRenderState.getRenderPass();
return renderPass != null && renderPass.isEye();
return LodPassHelper.isVrEyeWorldPass();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.vmstudio.visor.compatibility.dh.mixin;

import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.vmstudio.visor.compatibility.MixinGate;
import org.vmstudio.visor.compatibility.dh.DhCompatHelper;

@Pseudo
@MixinGate(
classes = "com.seibel.distanthorizons.core.render.CameraZoom",
methods = "update",
fields = {"magnification", "coneTanHalfAngle", "lookDirectionX", "lookDirectionZ"}
)
@Mixin(targets = "com.seibel.distanthorizons.core.render.CameraZoom", remap = false)
public class DhCameraZoomVRMixin {
@Shadow(remap = false)
public double magnification;

@Shadow(remap = false)
public double coneTanHalfAngle;

@Shadow(remap = false)
public double lookDirectionX;

@Shadow(remap = false)
public double lookDirectionZ;

@Inject(method = "update", at = @At("HEAD"), cancellable = true, require = 0, expect = 0, remap = false)
private void visor$noZoomInVr(CallbackInfo ci) {
if (!DhCompatHelper.isVrWorldPass()) {
return;
}

this.magnification = 1.0;
this.coneTanHalfAngle = 0.0;
this.lookDirectionX = 0.0;
this.lookDirectionZ = 0.0;
ci.cancel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,15 @@ public final class ShaderPatcher {
.capture("view").glsl("),").same("view").glsl(".z*")
.same("mat").glsl("[2].w)"));

// vec4 assembled row by row with fma() for the depth term, the [2].xy column is simply left out (Mellow)
private static final ShaderPatch FORWARD_FMA_ROWS = new ShaderPatch()
.hint("fma")
.rewrite("return ${mat} * vec4(${view}, 1.0);",
p -> p.glsl("return vec4(vec2(").capture("mat").glsl("[0].x,").same("mat")
.glsl("[1].y)*").capture("view").glsl(".xy,fma(").same("mat")
.glsl("[2].z,").same("view").glsl(".z,").same("mat").glsl("[3].z),")
.same("mat").glsl("[2].w*").same("view").glsl(".z);"));

// ---- screen -> view ----

// unproject via inverse diagonal plus [3] column, divided in a second statement (rre36 packs);
Expand Down Expand Up @@ -145,6 +154,15 @@ public final class ShaderPatcher {
.same("out").glsl("/=(").same("mat").glsl("[2].w*(")
.capture("depth").glsl("*2.0-1.0)+").same("mat").glsl("[3].w);"));

// same row by row assembly for the inverse, this one drops the [3].xy column (Mellow)
private static final ShaderPatch INVERSE_FMA_ROWS = new ShaderPatch()
.hint("fma")
.rewrite("return ${mat} * vec4(${ndc}, 1.0);",
p -> p.glsl("return vec4(vec2(").capture("mat").glsl("[0].x,").same("mat")
.glsl("[1].y)*").capture("ndc").glsl(".xy,").same("mat")
.glsl("[3].z,fma(").same("mat").glsl("[2].w,").same("ndc")
.glsl(".z,").same("mat").glsl("[3].w));"));

// ---- view -> screen uv ----

// two-statement uv from the diagonal, xy only
Expand Down Expand Up @@ -210,6 +228,17 @@ public final class ShaderPatcher {
.same("mat").glsl("[3].xy;").capture("uv").glsl("=(")
.same("clip").glsl("/-").same("view").glsl(".z)*0.5+0.5;"));

// previous-frame reprojection returned straight away, what a TAA resolve samples its history with (Mellow)
private static final ShaderPatch SCREEN_PREVIOUS_FRAME_RETURN = new ShaderPatch()
.hint("[0]")
.rewrite("""
vec4 ${clip} = ${mat} * vec4(${view}, 1.0);
return (${clip}.xy / ${clip}.w) * 0.5 + 0.5;""",
p -> p.glsl("vec2").capture("clip").glsl("=vec2(").capture("mat").glsl("[0].x,")
.same("mat").glsl("[1].y)*").capture("view").glsl(".xy+")
.same("mat").glsl("[3].xy;return(").same("clip").glsl("/-")
.same("view").glsl(".z)*0.5+0.5;"));

private static final List<ShaderPatch> PATCHES = List.of(
// macro definitions first, then grouped by direction
PROJMAD_MACRO,
Expand All @@ -219,17 +248,20 @@ public final class ShaderPatcher {
FORWARD_DIAG4_Z,
FORWARD_MAT3_SCALE,
FORWARD_PROJMAD,
FORWARD_FMA_ROWS,
// screen -> view
INVERSE_DIAG_DIVIDE,
INVERSE_NDC_DIAG,
INVERSE_PROJMAD,
INVERSE_TEXCOORD_DIAG,
INVERSE_FMA_ROWS,
// view -> screen uv
SCREEN_XY_DIAG,
SCREEN_XY_DIAG2,
SCREEN_XYZ_DIAG,
SCREEN_LINE_XY_DIAG,
SCREEN_PREVIOUS_FRAME);
SCREEN_PREVIOUS_FRAME,
SCREEN_PREVIOUS_FRAME_RETURN);

private ShaderPatcher() {
throw new UnsupportedOperationException("Utility class");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.vmstudio.visor.compatibility.voxy;

import net.minecraft.client.Minecraft;
import org.jetbrains.annotations.Nullable;
import org.vmstudio.visor.api.ModLoader;
import org.vmstudio.visor.api.client.render.VRRenderPass;
import org.vmstudio.visor.compatibility.LodPassHelper;
import org.vmstudio.visor.compatibility.ShaderCompatHelper;
import org.vmstudio.visor.core.client.VisorState;
import org.vmstudio.visor.extensions.client.render.GameRendererExtension;

public final class VoxyCompatHelper {
public static final String MOD_ID = "voxy";
public static boolean isLoaded() {
return ModLoader.get().isModLoaded(MOD_ID);
}

@Nullable
public static VRRenderPass viewportPass() {
return LodPassHelper.currentWorldPass();
}

public static boolean shouldSkipVoxyRender() {
if (ShaderCompatHelper.isShaderActive()) {
return false;
}
return LodPassHelper.shouldSkipMirrorPass();
}

public static float vanillaNearPlane(float original) {
if (VisorState.get().isNotActive()) {
return original;
}
return ((GameRendererExtension) Minecraft.getInstance().gameRenderer).visor$getNearClipPlane();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package org.vmstudio.visor.compatibility.voxy;

import net.irisshaders.iris.Iris;
import org.lwjgl.opengl.GL30C;
import org.lwjgl.opengl.GL45C;
import org.vmstudio.visor.api.common.utils.LoggerUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public final class VoxyIrisPipelineBinder {
private static boolean initialized;
private static boolean ready;

private static Class<?> dataHolderInterface;
private static Method getPipelineData;

private static Field pipelineData;
private static Field pipelineFb;
private static Field pipelineFbTranslucent;
private static Field dataThePipeline;
private static Field dataOpaqueDrawTargets;
private static Field dataTranslucentDrawTargets;
private static Field depthFramebufferFramebuffer;
private static Field glFramebufferId;

public static void rebindVoxyToIrisPipeline(Object voxyPipeline) {
if (initialized && !ready) {
return;
}
try {
rebind(voxyPipeline, Iris.getPipelineManager().getPipelineNullable());
} catch (Throwable t) {
ready = false;
LoggerUtils.getLogger().error(
"Visor: failed to rebind Voxy to the current Iris pipeline; "
+ "Voxy LODs will be wrong with shaders in VR", t);
}
}

private static void rebind(Object voxyPipeline, Object irisPipeline) throws Exception {
if (!init(voxyPipeline.getClass())) {
return;
}
if (!dataHolderInterface.isInstance(irisPipeline)) {
return;
}
Object current = getPipelineData.invoke(irisPipeline);
Object bound = pipelineData.get(voxyPipeline);
if (current == null || current == bound) {
return;
}

dataThePipeline.set(bound, null);
dataThePipeline.set(current, voxyPipeline);
pipelineData.set(voxyPipeline, current);

attachDrawTargets(framebufferId(pipelineFb.get(voxyPipeline)),
(int[]) dataOpaqueDrawTargets.get(current), ((int[]) dataOpaqueDrawTargets.get(bound)).length);
attachDrawTargets(framebufferId(pipelineFbTranslucent.get(voxyPipeline)),
(int[]) dataTranslucentDrawTargets.get(current), ((int[]) dataTranslucentDrawTargets.get(bound)).length);
}

private static int framebufferId(Object depthFramebuffer) throws Exception {
return glFramebufferId.getInt(depthFramebufferFramebuffer.get(depthFramebuffer));
}

private static void attachDrawTargets(int framebuffer, int[] targets, int previousCount) {
int[] drawBuffers = new int[targets.length];
for (int i = 0; i < targets.length; i++) {
drawBuffers[i] = GL30C.GL_COLOR_ATTACHMENT0 + i;
GL45C.glNamedFramebufferTexture(framebuffer, GL30C.GL_COLOR_ATTACHMENT0 + i, targets[i], 0);
}
for (int i = targets.length; i < previousCount; i++) {
GL45C.glNamedFramebufferTexture(framebuffer, GL30C.GL_COLOR_ATTACHMENT0 + i, 0, 0);
}
GL45C.glNamedFramebufferDrawBuffers(framebuffer, drawBuffers);
}

private static boolean init(Class<?> pipelineClass) throws Exception {
if (initialized) {
return ready;
}
initialized = true;

dataHolderInterface = Class.forName("me.cortex.voxy.client.iris.IGetIrisVoxyPipelineData",
false, pipelineClass.getClassLoader());
getPipelineData = dataHolderInterface.getMethod("voxy$getPipelineData");
Class<?> dataClass = getPipelineData.getReturnType();

pipelineData = pipelineClass.getDeclaredField("data");
pipelineData.setAccessible(true);
pipelineFb = pipelineClass.getField("fb");
pipelineFb.setAccessible(true);
pipelineFbTranslucent = pipelineClass.getField("fbTranslucent");
pipelineFbTranslucent.setAccessible(true);
dataThePipeline = dataClass.getField("thePipeline");
dataThePipeline.setAccessible(true);
dataOpaqueDrawTargets = dataClass.getField("opaqueDrawTargets");
dataOpaqueDrawTargets.setAccessible(true);
dataTranslucentDrawTargets = dataClass.getField("translucentDrawTargets");
dataTranslucentDrawTargets.setAccessible(true);
depthFramebufferFramebuffer = pipelineFb.getType().getField("framebuffer");
depthFramebufferFramebuffer.setAccessible(true);
glFramebufferId = depthFramebufferFramebuffer.getType().getField("id");
glFramebufferId.setAccessible(true);

ready = true;
return true;
}



private static Map<Class<?>, Method> NAME_GETTERS = new ConcurrentHashMap<>();
public static Comparator<Object> UNIFORM_ORDER = Comparator.comparing(VoxyIrisPipelineBinder::uniformName);

private static boolean loggedNameFailure;

private static String uniformName(Object uniform) {
if (uniform == null) {
return "";
}
try {
Method getter = NAME_GETTERS.computeIfAbsent(uniform.getClass(), VoxyIrisPipelineBinder::findNameGetter);
return String.valueOf(getter.invoke(uniform));
} catch (Throwable t) {
if (!loggedNameFailure) {
loggedNameFailure = true;
LoggerUtils.getLogger().error(
"Visor: couldnt read the names of Voxy's shader uniforms; "
+ "Voxy LODs may get garbage uniforms with shaders in VR", t);
}
return "";
}
}

private static Method findNameGetter(Class<?> uniformClass) {
try {
Method method = uniformClass.getDeclaredMethod("name");
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new IllegalStateException("no name() in " + uniformClass.getName(), e);
}
}
}
Loading
Loading