mirror of
https://github.com/FabricMC/fabric.git
synced 2024-11-14 19:25:23 -05:00
Add registry for core shaders in custom namespaces (#2901)
* Add registry for core shaders in custom namespaces
* Apply suggestions from code review
Co-authored-by: modmuss50 <modmuss50@gmail.com>
* Use Identifier.NAMESPACE_SEPARATOR instead of ":"
* Remove fabric_ prefixes from mixins
* Move test rendering to lower-right corner for test screenshots
---------
Co-authored-by: modmuss50 <modmuss50@gmail.com>
(cherry picked from commit ad177755a6
)
This commit is contained in:
parent
09a3510c69
commit
8f87821716
13 changed files with 417 additions and 2 deletions
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.api.client.rendering.v1;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import net.minecraft.client.gl.ShaderProgram;
|
||||
import net.minecraft.client.render.VertexFormat;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
|
||||
/**
|
||||
* Called when core shaders ({@linkplain ShaderProgram shader programs} loaded from {@code assets/<namespace>/shaders/core})
|
||||
* are loaded to register custom modded shaders.
|
||||
*
|
||||
* <p>Fabric API also modifies the {@code #moj_import} feature in core shaders to accept
|
||||
* arbitrary namespaces for shaders loaded using the {@code <filename.glsl>} syntax.
|
||||
* For example, {@code #moj_import <my_mod:test.glsl>} would import the shader from
|
||||
* {@code assets/my_mod/shaders/include/test.glsl}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CoreShaderRegistrationCallback {
|
||||
Event<CoreShaderRegistrationCallback> EVENT = EventFactory.createArrayBacked(CoreShaderRegistrationCallback.class, callbacks -> context -> {
|
||||
for (CoreShaderRegistrationCallback callback : callbacks) {
|
||||
callback.registerShaders(context);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Registers core shaders using the registration context.
|
||||
*
|
||||
* @param context the registration context
|
||||
*/
|
||||
void registerShaders(RegistrationContext context) throws IOException;
|
||||
|
||||
/**
|
||||
* A context object used to create and register core shader programs.
|
||||
*
|
||||
* <p>This is not meant for implementation by users of the API.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
interface RegistrationContext {
|
||||
/**
|
||||
* Creates and registers a core shader program.
|
||||
*
|
||||
* <p>The program is loaded from {@code assets/<namespace>/shaders/core/<path>.json}.
|
||||
*
|
||||
* @param id the program ID
|
||||
* @param vertexFormat the vertex format used by the shader
|
||||
* @param loadCallback a callback that is called when the shader program has been successfully loaded
|
||||
*/
|
||||
void register(Identifier id, VertexFormat vertexFormat, Consumer<ShaderProgram> loadCallback) throws IOException;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.impl.client.rendering;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.minecraft.client.gl.ShaderProgram;
|
||||
import net.minecraft.client.render.VertexFormat;
|
||||
import net.minecraft.resource.ResourceFactory;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
public final class FabricShaderProgram extends ShaderProgram {
|
||||
public FabricShaderProgram(ResourceFactory factory, Identifier name, VertexFormat format) throws IOException {
|
||||
super(factory, name.toString(), format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the input string containing an identifier
|
||||
* with the namespace of the id in the front instead of in the middle.
|
||||
*
|
||||
* <p>Example: {@code shaders/core/my_mod:xyz} -> {@code my_mod:shaders/core/xyz}
|
||||
*
|
||||
* @param input the raw input string
|
||||
* @param containedId the ID contained within the input string
|
||||
* @return the corrected full ID string
|
||||
*/
|
||||
public static String rewriteAsId(String input, String containedId) {
|
||||
Identifier contained = new Identifier(containedId);
|
||||
return contained.getNamespace() + Identifier.NAMESPACE_SEPARATOR + input.replace(containedId, contained.getPath());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.rendering.shader;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
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.Slice;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import net.minecraft.client.gl.ShaderProgram;
|
||||
import net.minecraft.client.render.GameRenderer;
|
||||
import net.minecraft.resource.ResourceFactory;
|
||||
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.CoreShaderRegistrationCallback;
|
||||
import net.fabricmc.fabric.impl.client.rendering.FabricShaderProgram;
|
||||
|
||||
/**
|
||||
* Implements custom core shader registration (CoreShaderRegistrationCallback).
|
||||
*/
|
||||
@Mixin(GameRenderer.class)
|
||||
abstract class GameRendererMixin {
|
||||
@Inject(
|
||||
method = "loadPrograms",
|
||||
at = @At(value = "INVOKE", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", remap = false, shift = At.Shift.AFTER),
|
||||
slice = @Slice(from = @At(value = "NEW", target = "net/minecraft/client/gl/ShaderProgram", ordinal = 0)),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void registerShaders(ResourceFactory factory, CallbackInfo info, List<?> shaderStages, List<Pair<ShaderProgram, Consumer<ShaderProgram>>> programs) throws IOException {
|
||||
CoreShaderRegistrationCallback.RegistrationContext context = (id, vertexFormat, loadCallback) -> {
|
||||
ShaderProgram program = new FabricShaderProgram(factory, id, vertexFormat);
|
||||
programs.add(Pair.of(program, loadCallback));
|
||||
};
|
||||
CoreShaderRegistrationCallback.EVENT.invoker().registerShaders(context);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.rendering.shader;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.rendering.FabricShaderProgram;
|
||||
|
||||
/**
|
||||
* Lets modded shaders {@code #moj_import} shaders from any namespace with the
|
||||
* {@code <>} syntax.
|
||||
*/
|
||||
@Mixin(targets = "net.minecraft.client.gl.ShaderProgram$1")
|
||||
abstract class ShaderProgramImportProcessorMixin {
|
||||
@Unique
|
||||
private String capturedImport;
|
||||
|
||||
@Inject(method = "loadImport", at = @At("HEAD"))
|
||||
private void captureImport(boolean inline, String name, CallbackInfoReturnable<String> info) {
|
||||
capturedImport = name;
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "loadImport", at = @At("STORE"), ordinal = 0, argsOnly = true)
|
||||
private String modifyImportId(String id, boolean inline) {
|
||||
if (!inline && capturedImport.contains(String.valueOf(Identifier.NAMESPACE_SEPARATOR))) {
|
||||
return FabricShaderProgram.rewriteAsId(id, capturedImport);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Inject(method = "loadImport", at = @At("RETURN"))
|
||||
private void uncaptureImport(boolean inline, String name, CallbackInfoReturnable<String> info) {
|
||||
capturedImport = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.mixin.client.rendering.shader;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
|
||||
import net.minecraft.client.gl.ShaderProgram;
|
||||
import net.minecraft.client.gl.ShaderStage;
|
||||
import net.minecraft.resource.ResourceFactory;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.fabric.impl.client.rendering.FabricShaderProgram;
|
||||
|
||||
@Mixin(ShaderProgram.class)
|
||||
abstract class ShaderProgramMixin {
|
||||
@Shadow
|
||||
@Final
|
||||
private String name;
|
||||
|
||||
// Allow loading FabricShaderPrograms from arbitrary namespaces.
|
||||
@ModifyArg(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Identifier;<init>(Ljava/lang/String;)V"), allow = 1)
|
||||
private String modifyProgramId(String id) {
|
||||
if ((Object) this instanceof FabricShaderProgram) {
|
||||
return FabricShaderProgram.rewriteAsId(id, name);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
// Allow loading shader stages from arbitrary namespaces.
|
||||
@ModifyVariable(method = "loadShader", at = @At("STORE"), ordinal = 1)
|
||||
private static String modifyStageId(String id, ResourceFactory factory, ShaderStage.Type type, String name) {
|
||||
if (name.contains(String.valueOf(Identifier.NAMESPACE_SEPARATOR))) {
|
||||
return FabricShaderProgram.rewriteAsId(id, name);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
}
|
|
@ -16,7 +16,10 @@
|
|||
"BlockEntityRendererFactoriesMixin",
|
||||
"EntityRenderersMixin",
|
||||
"ScreenMixin",
|
||||
"DimensionEffectsAccessor"
|
||||
"DimensionEffectsAccessor",
|
||||
"shader.GameRendererMixin",
|
||||
"shader.ShaderProgramImportProcessorMixin",
|
||||
"shader.ShaderProgramMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package net.fabricmc.fabric.test.rendering.client;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gl.ShaderProgram;
|
||||
import net.minecraft.client.render.BufferBuilder;
|
||||
import net.minecraft.client.render.BufferRenderer;
|
||||
import net.minecraft.client.render.Tessellator;
|
||||
import net.minecraft.client.render.VertexFormat;
|
||||
import net.minecraft.client.render.VertexFormats;
|
||||
import net.minecraft.client.util.Window;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.CoreShaderRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
|
||||
|
||||
/**
|
||||
* Tests {@link HudRenderCallback} and {@link CoreShaderRegistrationCallback} by drawing a green rectangle
|
||||
* in the lower-right corner of the screen.
|
||||
*/
|
||||
public class HudAndShaderTest implements ClientModInitializer {
|
||||
private static ShaderProgram testShader;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
CoreShaderRegistrationCallback.EVENT.register(context -> {
|
||||
// Register a custom shader taking POSITION vertices.
|
||||
Identifier id = new Identifier("fabric-rendering-v1-testmod", "test");
|
||||
context.register(id, VertexFormats.POSITION, program -> testShader = program);
|
||||
});
|
||||
|
||||
HudRenderCallback.EVENT.register((matrices, tickDelta) -> {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
Window window = client.getWindow();
|
||||
int x = window.getScaledWidth() - 15;
|
||||
int y = window.getScaledHeight() - 15;
|
||||
RenderSystem.setShader(() -> testShader);
|
||||
RenderSystem.setShaderColor(0f, 1f, 0f, 1f);
|
||||
Matrix4f positionMatrix = matrices.peek().getPositionMatrix();
|
||||
BufferBuilder buffer = Tessellator.getInstance().getBuffer();
|
||||
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION);
|
||||
buffer.vertex(positionMatrix, x, y, 50).next();
|
||||
buffer.vertex(positionMatrix, x, y + 10, 50).next();
|
||||
buffer.vertex(positionMatrix, x + 10, y + 10, 50).next();
|
||||
buffer.vertex(positionMatrix, x + 10, y, 50).next();
|
||||
BufferRenderer.drawWithGlobalProgram(buffer.end());
|
||||
// Reset shader color
|
||||
RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#version 150
|
||||
|
||||
#moj_import <fabric-rendering-v1-testmod:test_include.glsl>
|
||||
|
||||
uniform vec4 ColorModulator;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = applyColor(vec4(1.0, 1.0, 1.0, 1.0), ColorModulator);
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"blend": {
|
||||
"func": "add",
|
||||
"srcrgb": "srcalpha",
|
||||
"dstrgb": "1-srcalpha"
|
||||
},
|
||||
"vertex": "fabric-rendering-v1-testmod:test",
|
||||
"fragment": "fabric-rendering-v1-testmod:test",
|
||||
"attributes": [
|
||||
],
|
||||
"samplers": [
|
||||
],
|
||||
"uniforms": [
|
||||
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
|
||||
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }
|
||||
]
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#version 150
|
||||
|
||||
#moj_import "test_local_import.glsl"
|
||||
|
||||
in vec3 Position;
|
||||
|
||||
uniform mat4 ProjMat;
|
||||
uniform mat4 ModelViewMat;
|
||||
|
||||
void main() {
|
||||
gl_Position = applyMatrix(ProjMat, applyMatrix(ModelViewMat, vec4(Position, 1.0)));
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#version 150
|
||||
|
||||
// Just a simple function to test importing custom files.
|
||||
vec4 applyMatrix(mat4 matrix, vec4 vector) {
|
||||
return matrix * vector;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#version 150
|
||||
|
||||
// Just a simple function to test importing custom files.
|
||||
vec4 applyColor(vec4 color, vec4 colorModulator) {
|
||||
return color * colorModulator;
|
||||
}
|
|
@ -17,7 +17,8 @@
|
|||
"net.fabricmc.fabric.test.rendering.client.ArmorRenderingTests",
|
||||
"net.fabricmc.fabric.test.rendering.client.FeatureRendererTest",
|
||||
"net.fabricmc.fabric.test.rendering.client.TooltipComponentTests",
|
||||
"net.fabricmc.fabric.test.rendering.client.DimensionalRenderingTest"
|
||||
"net.fabricmc.fabric.test.rendering.client.DimensionalRenderingTest",
|
||||
"net.fabricmc.fabric.test.rendering.client.HudAndShaderTest"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue