Fix projection matrix bug

- Copy the projection matrix when vanilla would be uploading it to GL.
 - This should account for just about everything.

Unrelated to the fix:
 - Shader programs now have more flexibility in specialization.
 - Accomplished with IMultiProgram.
 - Remove unnecessary SHADER_DEBUG_OUTPUT boolean.
This commit is contained in:
JozsefA 2021-04-09 14:48:44 -07:00
parent edb1b59f41
commit f2c6afdcb3
12 changed files with 127 additions and 88 deletions

View file

@ -186,7 +186,6 @@ public class ClientEvents {
ms.pop();
RenderWork.runAll();
FastRenderDispatcher.endFrame();
}
@SubscribeEvent

View file

@ -49,7 +49,7 @@ public class RenderHooksMixin {
Matrix4f viewProjection = stack.peek()
.getModel()
.copy();
viewProjection.multiplyBackward(FastRenderDispatcher.getProjectionMatrix());
viewProjection.multiplyBackward(Backend.projectionMatrix);
FastRenderDispatcher.renderLayer(type, viewProjection, camX, camY, camZ);

View file

@ -0,0 +1,20 @@
package com.simibubi.create.foundation.mixin;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.util.math.vector.Matrix4f;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.simibubi.create.foundation.render.backend.Backend;
@Mixin(GameRenderer.class)
public class StoreProjectionMatrixMixin {
@Inject(method = "loadProjectionMatrix", at = @At("TAIL"))
private void onProjectionMatrixLoad(Matrix4f projection, CallbackInfo ci) {
Backend.projectionMatrix = projection.copy();
}
}

View file

@ -11,7 +11,7 @@ import org.lwjgl.opengl.GLCapabilities;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.render.backend.gl.GlFog;
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramGroup;
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
import com.simibubi.create.foundation.render.backend.gl.versioned.GlCompat;
import com.simibubi.create.foundation.render.backend.instancing.IFlywheelWorld;
@ -20,13 +20,17 @@ import net.minecraft.client.Minecraft;
import net.minecraft.resources.IReloadableResourceManager;
import net.minecraft.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.world.World;
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
public class Backend {
public static final Boolean SHADER_DEBUG_OUTPUT = true;
public static final Logger log = LogManager.getLogger(Backend.class);
public static final ShaderLoader shaderLoader = new ShaderLoader();
public static Matrix4f projectionMatrix = new Matrix4f();
public static GLCapabilities capabilities;
public static GlCompat compat;
@ -34,7 +38,7 @@ public class Backend {
private static boolean enabled;
static final Map<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>();
static final Map<ProgramSpec<?>, ProgramGroup<?>> programs = new HashMap<>();
static final Map<ProgramSpec<?>, IMultiProgram<?>> programs = new HashMap<>();
public Backend() {
throw new IllegalStateException();
@ -54,7 +58,7 @@ public class Backend {
@SuppressWarnings("unchecked")
public static <P extends GlProgram, S extends ProgramSpec<P>> P getProgram(S spec) {
return (P) programs.get(spec).get(GlFog.getFogMode());
return (P) programs.get(spec).get();
}
public static boolean isFlywheelWorld(World world) {
@ -89,7 +93,7 @@ public class Backend {
IResourceManager manager = mc.getResourceManager();
if (manager instanceof IReloadableResourceManager) {
ISelectiveResourceReloadListener listener = ShaderLoader::onResourceManagerReload;
ISelectiveResourceReloadListener listener = shaderLoader::onResourceManagerReload;
((IReloadableResourceManager) manager).addReloadListener(listener);
}
}

View file

@ -26,12 +26,6 @@ public class FastRenderDispatcher {
public static WorldAttached<ConcurrentHashMap.KeySetView<TileEntity, Boolean>> queuedUpdates = new WorldAttached<>(ConcurrentHashMap::newKeySet);
private static Matrix4f projectionMatrixThisFrame = null;
public static void endFrame() {
projectionMatrixThisFrame = null;
}
public static void enqueueUpdate(TileEntity te) {
queuedUpdates.get(te.getWorld()).add(te);
}
@ -82,45 +76,4 @@ public class FastRenderDispatcher {
layer.endDrawing();
}
// copied from GameRenderer.renderWorld
public static Matrix4f getProjectionMatrix() {
if (projectionMatrixThisFrame != null) return projectionMatrixThisFrame;
float partialTicks = AnimationTickHolder.getPartialTicks();
Minecraft mc = Minecraft.getInstance();
GameRenderer gameRenderer = mc.gameRenderer;
ClientPlayerEntity player = mc.player;
MatrixStack matrixstack = new MatrixStack();
matrixstack.peek()
.getModel()
.multiply(gameRenderer.getBasicProjectionMatrix(gameRenderer.getActiveRenderInfo(), partialTicks, true));
gameRenderer.bobViewWhenHurt(matrixstack, partialTicks);
if (mc.gameSettings.viewBobbing) {
gameRenderer.bobView(matrixstack, partialTicks);
}
float portalTime = MathHelper.lerp(partialTicks, player.prevTimeInPortal, player.timeInPortal);
if (portalTime > 0.0F) {
int i = 20;
if (player.isPotionActive(Effects.NAUSEA)) {
i = 7;
}
float f1 = 5.0F / (portalTime * portalTime + 5.0F) - portalTime * 0.04F;
f1 = f1 * f1;
Vector3f vector3f = new Vector3f(0.0F, MathHelper.SQRT_2 / 2.0F, MathHelper.SQRT_2 / 2.0F);
matrixstack.multiply(vector3f.getDegreesQuaternion(((float)gameRenderer.rendererUpdateCount + partialTicks) * (float)i));
matrixstack.scale(1.0F / f1, 1.0F, 1.0F);
float f2 = -((float)gameRenderer.rendererUpdateCount + partialTicks) * (float)i;
matrixstack.multiply(vector3f.getDegreesQuaternion(f2));
}
Matrix4f matrix4f = matrixstack.peek().getModel();
gameRenderer.loadProjectionMatrix(matrix4f);
projectionMatrixThisFrame = matrix4f;
return projectionMatrixThisFrame;
}
}

View file

@ -28,9 +28,11 @@ import org.lwjgl.system.MemoryUtil;
import com.google.common.collect.Lists;
import com.mojang.blaze3d.systems.RenderSystem;
import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
import com.simibubi.create.foundation.render.backend.gl.shader.SingleProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.GlProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.GlShader;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramGroup;
import com.simibubi.create.foundation.render.backend.gl.shader.FogSensitiveProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.IMultiProgram;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramSpec;
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderConstants;
import com.simibubi.create.foundation.render.backend.gl.shader.ShaderType;
@ -45,9 +47,12 @@ public class ShaderLoader {
public static final String SHADER_DIR = "flywheel/shaders/";
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
static final Map<ResourceLocation, String> shaderSource = new HashMap<>();
// #flwinclude <"valid_namespace:valid/path_to_file.glsl">
private static final Pattern includePattern = Pattern.compile("#flwinclude <\"([\\w\\d_]+:[\\w\\d_./]+)\">");
static void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
final Map<ResourceLocation, String> shaderSource = new HashMap<>();
void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
if (predicate.test(VanillaResourceType.SHADERS)) {
OptifineHandler.refresh();
Backend.refresh();
@ -56,16 +61,16 @@ public class ShaderLoader {
shaderSource.clear();
loadShaderSources(manager);
Backend.programs.values().forEach(ProgramGroup::delete);
Backend.programs.values().forEach(IMultiProgram::delete);
Backend.programs.clear();
Backend.registry.values().forEach(ShaderLoader::loadProgram);
Backend.registry.values().forEach(this::loadProgramFromSpec);
Backend.log.info("Loaded all shader programs.");
}
}
}
private static void loadShaderSources(IResourceManager manager){
private void loadShaderSources(IResourceManager manager){
Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> {
for (String ext : EXTENSIONS) {
if (s.endsWith(ext)) return true;
@ -89,19 +94,26 @@ public class ShaderLoader {
}
}
static <P extends GlProgram, S extends ProgramSpec<P>> void loadProgram(S programSpec) {
Map<GlFogMode, P> programGroup = new EnumMap<>(GlFogMode.class);
private <P extends GlProgram, S extends ProgramSpec<P>> void loadProgramFromSpec(S programSpec) {
for (GlFogMode fogMode : GlFogMode.values()) {
programGroup.put(fogMode, loadProgram(programSpec, fogMode));
if (programSpec.fogSensitive) {
Map<GlFogMode, P> programGroup = new EnumMap<>(GlFogMode.class);
for (GlFogMode fogMode : GlFogMode.values()) {
programGroup.put(fogMode, loadProgram(programSpec, fogMode));
}
Backend.programs.put(programSpec, new FogSensitiveProgram<>(programGroup));
} else {
P program = loadProgram(programSpec, GlFogMode.NONE);
Backend.programs.put(programSpec, new SingleProgram<>(program));
}
Backend.programs.put(programSpec, new ProgramGroup<>(programGroup));
Backend.log.debug("Loaded program {}", programSpec.name);
}
private static <P extends GlProgram, S extends ProgramSpec<P>> P loadProgram(S programSpec, GlFogMode fogMode) {
private <P extends GlProgram, S extends ProgramSpec<P>> P loadProgram(S programSpec, GlFogMode fogMode) {
GlShader vert = null;
GlShader frag = null;
try {
@ -124,16 +136,14 @@ public class ShaderLoader {
}
}
private static final Pattern includePattern = Pattern.compile("#flwinclude <\"([\\w\\d_]+:[\\w\\d_./]+)\">");
private static String processIncludes(ResourceLocation baseName, String source) {
private String processIncludes(ResourceLocation baseName, String source) {
HashSet<ResourceLocation> seen = new HashSet<>();
seen.add(baseName);
return includeRecursive(source, seen).collect(Collectors.joining("\n"));
}
private static Stream<String> includeRecursive(String source, Set<ResourceLocation> seen) {
private Stream<String> includeRecursive(String source, Set<ResourceLocation> seen) {
return new BufferedReader(new StringReader(source)).lines().flatMap(line -> {
Matcher matcher = includePattern.matcher(line);
@ -156,7 +166,7 @@ public class ShaderLoader {
});
}
private static GlShader loadShader(ResourceLocation name, ShaderType type, ShaderConstants defines) {
private GlShader loadShader(ResourceLocation name, ShaderType type, ShaderConstants defines) {
String source = shaderSource.get(name);
source = processIncludes(name, source);
@ -168,7 +178,7 @@ public class ShaderLoader {
return new GlShader(type, name, source);
}
public static String readToString(InputStream is) {
public String readToString(InputStream is) {
RenderSystem.assertThread(RenderSystem::isOnRenderThread);
ByteBuffer bytebuffer = null;
@ -189,7 +199,7 @@ public class ShaderLoader {
return null;
}
public static ByteBuffer readToBuffer(InputStream is) throws IOException {
public ByteBuffer readToBuffer(InputStream is) throws IOException {
ByteBuffer bytebuffer;
if (is instanceof FileInputStream) {
FileInputStream fileinputstream = (FileInputStream)is;

View file

@ -2,21 +2,25 @@ package com.simibubi.create.foundation.render.backend.gl.shader;
import java.util.Map;
import com.simibubi.create.foundation.render.backend.gl.GlFog;
import com.simibubi.create.foundation.render.backend.gl.GlFogMode;
public class ProgramGroup<P extends GlProgram> {
public class FogSensitiveProgram<P extends GlProgram> implements IMultiProgram<P> {
private final Map<GlFogMode, P> programs;
public ProgramGroup(Map<GlFogMode, P> programs) {
public FogSensitiveProgram(Map<GlFogMode, P> programs) {
this.programs = programs;
}
public P get(GlFogMode fogMode) {
return programs.get(fogMode);
@Override
public P get() {
return programs.get(GlFog.getFogMode());
}
@Override
public void delete() {
programs.values().forEach(GlProgram::delete);
}
}

View file

@ -38,7 +38,7 @@ public abstract class GlProgram extends GlObject {
public int getUniformLocation(String uniform) {
int index = GL20.glGetUniformLocation(this.handle(), uniform);
if (index < 0 && Backend.SHADER_DEBUG_OUTPUT) {
if (index < 0) {
Backend.log.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
}
@ -106,7 +106,7 @@ public abstract class GlProgram extends GlObject {
String log = GL20.glGetProgramInfoLog(this.program);
if (!log.isEmpty() && Backend.SHADER_DEBUG_OUTPUT) {
if (!log.isEmpty()) {
Backend.log.debug("Program link log for " + this.name + ": " + log);
}

View file

@ -0,0 +1,21 @@
package com.simibubi.create.foundation.render.backend.gl.shader;
/**
* Encapsulates any number of shader programs for use in similar contexts.
* Allows the implementor to choose which shader program to use based on arbitrary state.
*
* @param <P>
*/
public interface IMultiProgram<P extends GlProgram> {
/**
* Get the shader program most suited for the current game state.
* @return The one true program.
*/
P get();
/**
* Delete all shader programs encapsulated by your implementation.
*/
void delete();
}

View file

@ -20,6 +20,8 @@ public class ProgramSpec<P extends GlProgram> {
public final ArrayList<IVertexAttrib> attributes;
public final boolean fogSensitive;
public static <P extends GlProgram> Builder<P> builder(String name, GlProgram.ProgramFactory<P> factory) {
return builder(new ResourceLocation(Create.ID, name), factory);
}
@ -28,16 +30,16 @@ public class ProgramSpec<P extends GlProgram> {
return new Builder<>(name, factory);
}
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag, GlProgram.ProgramFactory<P> factory, ShaderConstants defines, ArrayList<IVertexAttrib> attributes) {
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag, GlProgram.ProgramFactory<P> factory, ShaderConstants defines, ArrayList<IVertexAttrib> attributes, boolean fogSensitive) {
this.name = name;
this.vert = vert;
this.frag = frag;
this.defines = defines;
this.factory = factory;
this.attributes = attributes;
}
this.fogSensitive = fogSensitive;
}
public ResourceLocation getVert() {
return vert;
@ -51,6 +53,7 @@ public class ProgramSpec<P extends GlProgram> {
private ResourceLocation vert;
private ResourceLocation frag;
private ShaderConstants defines = ShaderConstants.EMPTY;
private boolean fogSensitive = true;
private final ResourceLocation name;
private final GlProgram.ProgramFactory<P> factory;
@ -77,13 +80,18 @@ public class ProgramSpec<P extends GlProgram> {
return this;
}
public <A extends Enum<A> & IVertexAttrib> Builder<P> addAttributes(Class<A> attributeEnum) {
public Builder<P> setFogSensitive(boolean fogSensitive) {
this.fogSensitive = fogSensitive;
return this;
}
public <A extends Enum<A> & IVertexAttrib> Builder<P> addAttributes(Class<A> attributeEnum) {
attributes.addAll(Arrays.asList(attributeEnum.getEnumConstants()));
return this;
}
public ProgramSpec<P> createProgramSpec() {
return new ProgramSpec<>(name, vert, frag, factory, defines, attributes);
return new ProgramSpec<>(name, vert, frag, factory, defines, attributes, fogSensitive);
}
}
}

View file

@ -0,0 +1,19 @@
package com.simibubi.create.foundation.render.backend.gl.shader;
public class SingleProgram<P extends GlProgram> implements IMultiProgram<P> {
final P program;
public SingleProgram(P program) {
this.program = program;
}
@Override
public P get() {
return program;
}
@Override
public void delete() {
program.delete();
}
}

View file

@ -13,10 +13,11 @@
"RenderHooksMixin",
"ShaderCloseMixin",
"TileRemoveMixin",
"EntityContraptionInteractionMixin"
"EntityContraptionInteractionMixin",
"StoreProjectionMatrixMixin"
],
"injectors": {
"defaultRequire": 1
},
"minVersion": "0.8"
}
}