From 8c4cda60b05494b30c0edbf39e7b0057126b3c32 Mon Sep 17 00:00:00 2001 From: Ocelot Date: Sun, 24 Nov 2024 02:05:32 -0700 Subject: [PATCH] Added normal and lightmap buffers --- .../api/client/render/VeilRenderSystem.java | 3 +- .../dynamicbuffer/DynamicBufferType.java | 6 +- .../client/render/shader/ShaderManager.java | 11 + .../shader/processor/ShaderPreProcessor.java | 13 + .../veil/ext/ShaderInstanceExtension.java | 8 +- .../dynamicbuffer/DynamicBufferManger.java | 160 +++++++++- .../dynamicbuffer/DynamicBufferProcessor.java | 275 +++++++++++++----- .../client/render/shader/DummyResource.java | 31 ++ .../render/shader/ShaderProgramImpl.java | 17 +- .../render/shader/SimpleShaderProcessor.java | 37 ++- .../foundry/veil/impl/glsl/GlslParser.java | 18 +- .../veil/impl/glsl/node/GlslCompoundNode.java | 6 + .../veil/impl/glsl/node/GlslConstantNode.java | 7 + .../veil/impl/glsl/node/GlslEmptyNode.java | 6 + .../foundry/veil/impl/glsl/node/GlslNode.java | 3 + .../foundry/veil/impl/glsl/node/GlslTree.java | 4 + .../impl/glsl/node/branch/ForLoopNode.java | 7 + .../glsl/node/branch/GlslCaseLabelNode.java | 7 + .../impl/glsl/node/branch/GlslReturnNode.java | 7 + .../glsl/node/branch/GlslSelectionNode.java | 7 + .../impl/glsl/node/branch/GlslSwitchNode.java | 7 + .../veil/impl/glsl/node/branch/JumpNode.java | 6 + .../impl/glsl/node/branch/WhileLoopNode.java | 8 + .../glsl/node/expression/GlslAndNode.java | 6 + .../node/expression/GlslAssignmentNode.java | 8 + .../glsl/node/expression/GlslCompareNode.java | 8 + .../node/expression/GlslConditionalNode.java | 8 + .../node/expression/GlslExclusiveOrNode.java | 10 +- .../node/expression/GlslInclusiveOrNode.java | 10 +- .../node/expression/GlslLogicalAndNode.java | 10 +- .../node/expression/GlslLogicalOrNode.java | 10 +- .../node/expression/GlslLogicalXorNode.java | 10 +- .../node/expression/GlslOperationNode.java | 8 + .../node/expression/GlslPrecisionNode.java | 7 + .../glsl/node/expression/GlslUnaryNode.java | 7 + .../glsl/node/function/GlslFunctionNode.java | 8 +- .../node/function/GlslInvokeFunctionNode.java | 6 + .../GlslPrimitiveConstructorNode.java | 7 + .../glsl/node/variable/GlslArrayNode.java | 8 + .../glsl/node/variable/GlslDeclaration.java | 6 + .../glsl/node/variable/GlslFieldNode.java | 7 + .../impl/glsl/node/variable/GlslNewNode.java | 7 + .../glsl/node/variable/GlslStructNode.java | 7 + .../glsl/node/variable/GlslVariableNode.java | 7 + .../veil/mixin/accessor/ProgramAccessor.java | 2 +- .../dynamicbuffer/GameRendererMixin.java | 27 ++ .../dynamicbuffer/ShaderInstanceMixin.java | 144 +++++++++ .../client/shader/GameRendererMixin.java | 15 +- .../mixin/client/shader/ProgramMixin.java | 2 +- .../foundry/veil/platform/VeilPlatform.java | 2 +- .../shaders/program/quasar/particle.fsh | 2 + common/src/main/resources/veil.mixins.json | 2 + .../compat/sodium/ShaderLoaderMixin.java | 2 +- .../forge/platform/ForgeVeilPlatform.java | 2 +- 54 files changed, 876 insertions(+), 143 deletions(-) create mode 100644 common/src/main/java/foundry/veil/impl/client/render/shader/DummyResource.java create mode 100644 common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/GameRendererMixin.java create mode 100644 common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/ShaderInstanceMixin.java diff --git a/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java b/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java index 53bf51aa..c6a4f5ea 100644 --- a/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java +++ b/common/src/main/java/foundry/veil/api/client/render/VeilRenderSystem.java @@ -13,6 +13,7 @@ import foundry.veil.impl.client.imgui.VeilImGuiImpl; import foundry.veil.impl.client.render.pipeline.VeilUniformBlockState; import foundry.veil.impl.client.render.shader.ShaderProgramImpl; +import foundry.veil.impl.client.render.shader.SimpleShaderProcessor; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.resources.ResourceLocation; @@ -191,7 +192,6 @@ public final class VeilRenderSystem { private static ResourceLocation shaderLocation; private static VertexBuffer vbo; - private VeilRenderSystem() { } @@ -715,6 +715,7 @@ public static void endFrame() { glBindFramebuffer(GL_FRAMEBUFFER, 0); // Manual unbind to restore the default mc state UNIFORM_BLOCK_STATE.clear(); + SimpleShaderProcessor.clear(); } @ApiStatus.Internal diff --git a/common/src/main/java/foundry/veil/api/client/render/dynamicbuffer/DynamicBufferType.java b/common/src/main/java/foundry/veil/api/client/render/dynamicbuffer/DynamicBufferType.java index de08b1a9..db5bdb0d 100644 --- a/common/src/main/java/foundry/veil/api/client/render/dynamicbuffer/DynamicBufferType.java +++ b/common/src/main/java/foundry/veil/api/client/render/dynamicbuffer/DynamicBufferType.java @@ -5,9 +5,9 @@ public enum DynamicBufferType { ALBEDO("albedo", "Albedo", GlslTypeSpecifier.BuiltinType.VEC4, FramebufferAttachmentDefinition.Format.RGBA8), - NORMAL("normal", "Normal", GlslTypeSpecifier.BuiltinType.IVEC3, FramebufferAttachmentDefinition.Format.RGB8_SNORM), - LIGHT_COLOR("light_color", "LightColor", GlslTypeSpecifier.BuiltinType.VEC3, FramebufferAttachmentDefinition.Format.RGB8), - LIGHT_UV("light_uv", "LightUv", GlslTypeSpecifier.BuiltinType.UVEC2, FramebufferAttachmentDefinition.Format.RG8UI), + NORMAL("normal", "Normal", GlslTypeSpecifier.BuiltinType.VEC4, FramebufferAttachmentDefinition.Format.RGB8_SNORM), + LIGHT_UV("light_uv", "LightUV", GlslTypeSpecifier.BuiltinType.VEC4, FramebufferAttachmentDefinition.Format.RG8), + LIGHT_COLOR("light_color", "LightColor", GlslTypeSpecifier.BuiltinType.VEC4, FramebufferAttachmentDefinition.Format.RGB8), DEBUG("debug", "Debug", GlslTypeSpecifier.BuiltinType.VEC4, FramebufferAttachmentDefinition.Format.RGBA16F); private final String name; diff --git a/common/src/main/java/foundry/veil/api/client/render/shader/ShaderManager.java b/common/src/main/java/foundry/veil/api/client/render/shader/ShaderManager.java index c62d329d..0250107a 100644 --- a/common/src/main/java/foundry/veil/api/client/render/shader/ShaderManager.java +++ b/common/src/main/java/foundry/veil/api/client/render/shader/ShaderManager.java @@ -4,6 +4,7 @@ import com.google.gson.GsonBuilder; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; +import com.mojang.blaze3d.vertex.VertexFormat; import foundry.veil.Veil; import foundry.veil.api.client.render.VeilRenderSystem; import foundry.veil.api.client.render.dynamicbuffer.DynamicBufferType; @@ -579,6 +580,16 @@ public void addInclude(ResourceLocation name) { public boolean isSourceFile() { return this.sourceFile; } + + @Override + public @Nullable String shaderInstance() { + return null; + } + + @Override + public @Nullable VertexFormat vertexFormat() { + return null; + } } private record ReloadState(Map definitions, diff --git a/common/src/main/java/foundry/veil/api/client/render/shader/processor/ShaderPreProcessor.java b/common/src/main/java/foundry/veil/api/client/render/shader/processor/ShaderPreProcessor.java index 6baef66a..b43e869c 100644 --- a/common/src/main/java/foundry/veil/api/client/render/shader/processor/ShaderPreProcessor.java +++ b/common/src/main/java/foundry/veil/api/client/render/shader/processor/ShaderPreProcessor.java @@ -1,5 +1,6 @@ package foundry.veil.api.client.render.shader.processor; +import com.mojang.blaze3d.vertex.VertexFormat; import foundry.veil.api.client.render.shader.definition.ShaderPreDefinitions; import foundry.veil.api.client.render.shader.program.ProgramDefinition; import net.minecraft.resources.ResourceLocation; @@ -116,12 +117,24 @@ interface Context { @Nullable ProgramDefinition definition(); + /** + * @return The name of the shader instance this was compiled with or null if not a vanilla shader + */ + @Nullable + String shaderInstance(); + /** * @return The set of pre-definitions for shaders */ @Nullable ShaderPreDefinitions preDefinitions(); + /** + * @return The vertex format specified in the shader or null if a vanilla shader + */ + @Nullable + VertexFormat vertexFormat(); + /** * @return The OpenGL type of the compiling shader */ diff --git a/common/src/main/java/foundry/veil/ext/ShaderInstanceExtension.java b/common/src/main/java/foundry/veil/ext/ShaderInstanceExtension.java index ff8ee3b5..ff4e9fe5 100644 --- a/common/src/main/java/foundry/veil/ext/ShaderInstanceExtension.java +++ b/common/src/main/java/foundry/veil/ext/ShaderInstanceExtension.java @@ -1,6 +1,12 @@ package foundry.veil.ext; +import net.minecraft.resources.ResourceLocation; + +import java.util.Collection; + public interface ShaderInstanceExtension { - void setActiveBuffers(int activeBuffers); + void veil$recompile(boolean vertex, String source); + + Collection veil$getShaderSources(); } diff --git a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManger.java b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManger.java index 0c1e7bac..5e869db8 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManger.java +++ b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferManger.java @@ -1,22 +1,38 @@ package foundry.veil.impl.client.render.dynamicbuffer; +import com.google.common.collect.Sets; import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.preprocessor.GlslPreprocessor; +import com.mojang.blaze3d.systems.RenderSystem; +import com.mojang.blaze3d.vertex.VertexFormat; import foundry.veil.Veil; import foundry.veil.api.client.render.VeilRenderSystem; import foundry.veil.api.client.render.dynamicbuffer.DynamicBufferType; import foundry.veil.api.client.render.framebuffer.AdvancedFbo; +import foundry.veil.ext.ShaderInstanceExtension; +import foundry.veil.impl.client.render.shader.SimpleShaderProcessor; import foundry.veil.mixin.accessor.GameRendererAccessor; +import net.minecraft.FileUtil; +import net.minecraft.ReportedException; +import net.minecraft.Util; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.ResourceProvider; +import org.apache.commons.io.IOUtils; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nullable; import org.lwjgl.system.MemoryStack; import org.lwjgl.system.NativeResource; +import java.io.IOException; +import java.io.Reader; import java.nio.IntBuffer; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; import static org.lwjgl.opengl.GL11C.*; import static org.lwjgl.opengl.GL12C.*; @@ -45,6 +61,8 @@ public class DynamicBufferManger implements NativeResource { private final int[] clearBuffers; private final Map framebuffers; private final EnumMap dynamicBuffers; + private final AtomicBoolean vanillaCancelled; + private CompletableFuture vanillaLoadFuture; public DynamicBufferManger(int width, int height) { this.activeBuffers = 0; @@ -62,6 +80,129 @@ public DynamicBufferManger(int width, int height) { this.dynamicBuffers.put(value, buffer); } } + + this.vanillaCancelled = new AtomicBoolean(false); + this.vanillaLoadFuture = CompletableFuture.completedFuture(null); + } + + private void deleteFramebuffers() { + for (Map.Entry entry : this.framebuffers.entrySet()) { + entry.getValue().free(); + VeilRenderSystem.renderer().getFramebufferManager().removeFramebuffer(entry.getKey()); + } + this.framebuffers.clear(); + } + + /** + * Attempts to preload all vanilla minecraft shader files before creating the shaders on the CPU. + * + * @param shaders The shaders to reload + * @return A future for when vanilla shaders have reloaded + */ + public CompletableFuture reloadVanillaShaders(Collection shaders) { + this.vanillaCancelled.set(true); + return this.vanillaLoadFuture = CompletableFuture.runAsync(() -> { + this.vanillaCancelled.set(false); + ResourceManager resourceManager = Minecraft.getInstance().getResourceManager(); + + Veil.LOGGER.info("Compiling {} vanilla shaders", shaders.size()); + SimpleShaderProcessor.setup(resourceManager); + + Map shaderMap = new HashMap<>(shaders.size()); + for (ShaderInstance shader : shaders) { + shaderMap.put(shader.getName(), shader); + } + + while (!shaderMap.isEmpty()) { + if (this.vanillaCancelled.get()) { + return; + } + + Set lastFrameShaders = SimpleShaderProcessor.getLastFrameShaders(); + boolean compiled = false; + + for (String lastFrameShader : lastFrameShaders) { + ShaderInstance shader = shaderMap.remove(lastFrameShader); + if (shader != null) { + if (!shader.getName().startsWith("rendertype_")) { + continue; + } + + this.compileShader(resourceManager, shader); + compiled = true; + break; + } + } + + if (compiled) { + continue; + } + + Iterator iterator = shaderMap.values().iterator(); + while (iterator.hasNext()) { + ShaderInstance shader = iterator.next(); + iterator.remove(); + if (!shader.getName().startsWith("rendertype_")) { + continue; + } + + this.compileShader(resourceManager, shader); + break; + } + } + + SimpleShaderProcessor.free(); + if (!this.vanillaCancelled.get()) { + Veil.LOGGER.info("Compiled {} vanilla shaders", shaders.size()); + } + }, Util.backgroundExecutor()); + } + + private void compileShader(ResourceProvider resourceProvider, ShaderInstance shader) { + ShaderInstanceExtension extension = (ShaderInstanceExtension) shader; + Collection shaderSources = extension.veil$getShaderSources(); + VertexFormat vertexFormat = shader.getVertexFormat(); + for (ResourceLocation path : shaderSources) { + try (Reader reader = resourceProvider.openAsReader(path)) { + String source = IOUtils.toString(reader); + GlslPreprocessor preprocessor = new GlslPreprocessor() { + private final Set importedPaths = Sets.newHashSet(); + + @Override + public String applyImport(boolean useFullPath, String directory) { + directory = FileUtil.normalizeResourcePath((useFullPath ? path.getPath() : "shaders/include/") + directory); + if (!this.importedPaths.add(directory)) { + return null; + } else { + ResourceLocation resourcelocation = ResourceLocation.parse(directory); + + try { + String s2; + try (Reader reader = resourceProvider.openAsReader(resourcelocation)) { + s2 = IOUtils.toString(reader); + } + + return s2; + } catch (IOException e) { + Veil.LOGGER.error("Could not open GLSL import {}: {}", directory, e.getMessage()); + return "#error " + e.getMessage(); + } + } + } + }; + source = String.join("", preprocessor.process(source)); + + boolean vertex = path.getPath().endsWith(".vsh"); + String processed = SimpleShaderProcessor.modify(shader.getName(), path, vertexFormat, vertex ? GL_VERTEX_SHADER : GL_FRAGMENT_SHADER, source); + RenderSystem.recordRenderCall(() -> extension.veil$recompile(vertex, processed)); + } catch (Throwable t) { + Veil.LOGGER.error("Couldn't load vanilla shader from {}", path, t); + } + } + } + + public boolean isCompilingShaders() { + return !this.vanillaLoadFuture.isDone(); } public int getActiveBuffers() { @@ -78,14 +219,13 @@ public boolean setActiveBuffers(int activeBuffers) { } this.activeBuffers = activeBuffers; + this.reloadVanillaShaders(((GameRendererAccessor) Minecraft.getInstance().gameRenderer).getShaders().values()); this.deleteFramebuffers(); try { VeilRenderSystem.renderer().getShaderManager().setActiveBuffers(activeBuffers); - - for (ShaderInstance shader : ((GameRendererAccessor) Minecraft.getInstance().gameRenderer).getShaders().values()) { - - } + } catch (RuntimeException e) { + throw e; } catch (Exception e) { throw new RuntimeException(e); } @@ -98,14 +238,6 @@ public void setEnabled(boolean enabled) { this.enabled = enabled; } - private void deleteFramebuffers() { - for (Map.Entry entry : this.framebuffers.entrySet()) { - entry.getValue().free(); - VeilRenderSystem.renderer().getFramebufferManager().removeFramebuffer(entry.getKey()); - } - this.framebuffers.clear(); - } - @Override public void free() { this.deleteFramebuffers(); @@ -196,9 +328,9 @@ public void init(int width, int height) { GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0); - GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 1); + GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_MAX_LOD, 0); GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, 0.0F); GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); GlStateManager._texParameter(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); diff --git a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferProcessor.java b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferProcessor.java index b3fad833..87551609 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferProcessor.java +++ b/common/src/main/java/foundry/veil/impl/client/render/dynamicbuffer/DynamicBufferProcessor.java @@ -1,47 +1,191 @@ package foundry.veil.impl.client.render.dynamicbuffer; +import com.mojang.blaze3d.vertex.VertexFormat; +import com.mojang.blaze3d.vertex.VertexFormatElement; import foundry.veil.Veil; import foundry.veil.api.client.render.dynamicbuffer.DynamicBufferType; import foundry.veil.api.client.render.shader.processor.ShaderPreProcessor; import foundry.veil.impl.glsl.GlslInjectionPoint; import foundry.veil.impl.glsl.GlslParser; -import foundry.veil.impl.glsl.GlslSyntaxException; import foundry.veil.impl.glsl.grammar.GlslSpecifiedType; import foundry.veil.impl.glsl.grammar.GlslTypeQualifier; import foundry.veil.impl.glsl.grammar.GlslTypeSpecifier; +import foundry.veil.impl.glsl.grammar.GlslVersion; +import foundry.veil.impl.glsl.node.GlslConstantNode; import foundry.veil.impl.glsl.node.GlslNode; import foundry.veil.impl.glsl.node.GlslTree; import foundry.veil.impl.glsl.node.expression.GlslAssignmentNode; +import foundry.veil.impl.glsl.node.expression.GlslOperationNode; import foundry.veil.impl.glsl.node.function.GlslFunctionNode; +import foundry.veil.impl.glsl.node.function.GlslInvokeFunctionNode; import foundry.veil.impl.glsl.node.variable.GlslNewNode; import foundry.veil.impl.glsl.node.variable.GlslVariableNode; import foundry.veil.impl.glsl.visitor.GlslStringWriter; +import it.unimi.dsi.fastutil.objects.Object2IntArrayMap; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.lwjgl.opengl.GL20C.GL_FRAGMENT_SHADER; +import static org.lwjgl.opengl.GL20C.GL_VERTEX_SHADER; public class DynamicBufferProcessor implements ShaderPreProcessor { private static final String[] VECTOR_ELEMENTS = {".x", ".y", ".z", ".w"}; private final DynamicBufferType[] types; + private final Object2IntMap validBuffers; public DynamicBufferProcessor(DynamicBufferType[] types) { this.types = types; + this.validBuffers = new Object2IntArrayMap<>(); + } + + @Override + public void prepare() { + this.validBuffers.clear(); } @Override public String modify(Context ctx, String source) throws IOException { + VertexFormat vertexFormat = ctx.vertexFormat(); + if (ctx.definition() == null && (vertexFormat == null || ctx.name() == null)) { + return source; + } + try { GlslTree tree = GlslParser.parse(source); Map markers = tree.getMarkers(); GlslFunctionNode mainFunction = tree.mainFunction().orElseThrow(); + List mainFunctionBody = Objects.requireNonNull(mainFunction.getBody()); + + GlslVersion version = tree.getVersion(); + if (version.getVersion() < 330) { + version.setVersion(330); + } + version.setCore(true); + + // Check if there is any lightmap to pull out + GlslNode sampler = null; + GlslNode lightmapUV = null; + boolean blockLightmap = false; + boolean injectLightmap = !markers.containsKey("veil:" + DynamicBufferType.LIGHT_COLOR.getName()) || !markers.containsKey("veil:" + DynamicBufferType.LIGHT_UV.getName()); + boolean vertexShader = ctx.type() == GL_VERTEX_SHADER; + if (vertexShader && injectLightmap) { + Optional sampleLightmapOptional = mainFunction.stream().filter(node -> { + if (!(node instanceof GlslInvokeFunctionNode invokeFunctionNode) || invokeFunctionNode.getParameters().size() != 2) { + return false; + } + return invokeFunctionNode.getHeader() instanceof GlslVariableNode variableNode && ("minecraft_sample_lightmap".equals(variableNode.getName())); + }).findFirst(); + + if (sampleLightmapOptional.isPresent()) { + List parameters = ((GlslInvokeFunctionNode) sampleLightmapOptional.get()).getParameters(); + sampler = parameters.get(0); + lightmapUV = parameters.get(1); + blockLightmap = true; + } else if (vertexFormat != null && vertexFormat.contains(VertexFormatElement.UV2)) { + Optional texelFetchOptional = mainFunction.stream().filter(node -> { + if (!(node instanceof GlslInvokeFunctionNode invokeFunctionNode) || invokeFunctionNode.getParameters().size() != 3) { + return false; + } + List parameters = invokeFunctionNode.getParameters(); + return invokeFunctionNode.getHeader() instanceof GlslVariableNode functionName && + "texelFetch".equals(functionName.getName()) && + parameters.get(1) instanceof GlslOperationNode operation && + operation.getFirst() instanceof GlslVariableNode variableNode && + operation.getSecond() instanceof GlslConstantNode constantNode && + constantNode.intValue() == 16 && + operation.getOperand() == GlslOperationNode.Operand.DIVIDE && + vertexFormat.getElementName(VertexFormatElement.UV2).equals(variableNode.getName()); + }).findFirst(); + + if (texelFetchOptional.isPresent()) { + List parameters = ((GlslInvokeFunctionNode) texelFetchOptional.get()).getParameters(); + sampler = parameters.get(0); + lightmapUV = ((GlslOperationNode)parameters.get(1)).getFirst(); + } + } + } + + // must be a vanilla shader, so attempt to extract data from attributes boolean modified = false; + if (ctx.definition() == null) { + for (int i = 0; i < this.types.length; i++) { + DynamicBufferType type = this.types[i]; + String sourceName = type.getSourceName(); + + if (injectLightmap) { + if (type == DynamicBufferType.LIGHT_UV) { + if (markers.containsKey("veil:" + DynamicBufferType.LIGHT_UV.getName())) { + continue; + } + + if (vertexShader) { + if (lightmapUV != null) { + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("out vec2 Pass" + type.getSourceName())); + if (blockLightmap) { + mainFunctionBody.add(GlslParser.parseExpression("vec2 veilTexCoord2 = clamp(" + lightmapUV.getSourceString() + " / 256.0, vec2(0.5 / 16.0), vec2(15.5 / 16.0))")); + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode("Pass" + type.getSourceName()), new GlslVariableNode("veilTexCoord2"), GlslAssignmentNode.Operand.EQUAL)); + } else { + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode("Pass" + type.getSourceName()), GlslParser.parseExpression("vec2(" + lightmapUV.getSourceString() + " / 256.0)"), GlslAssignmentNode.Operand.EQUAL)); + } + modified = true; + this.validBuffers.computeInt(ctx.shaderInstance(), (unused, mask) -> (mask != null ? mask : 0) | type.getMask()); + } + } else if ((this.validBuffers.getInt(ctx.shaderInstance()) & type.getMask()) != 0) { + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("in vec2 Pass" + type.getSourceName())); + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("layout(location = " + (1 + i) + ") out " + type.getType().getSourceString() + " " + sourceName)); + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode(type.getSourceName()), GlslParser.parseExpression("vec4(Pass" + type.getSourceName() + ", 0.0, 1.0)"), GlslAssignmentNode.Operand.EQUAL)); + modified = true; + } + } else if (type == DynamicBufferType.LIGHT_COLOR) { + if (markers.containsKey("veil:" + DynamicBufferType.LIGHT_COLOR.getName())) { + continue; + } + + if (vertexShader) { + if (lightmapUV != null && sampler != null) { + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("out vec3 Pass" + type.getSourceName())); + if (blockLightmap) { + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode("Pass" + type.getSourceName()), GlslParser.parseExpression("texture(" + sampler.getSourceString() + ", veilTexCoord2).rgb"), GlslAssignmentNode.Operand.EQUAL)); + } else { + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode("Pass" + type.getSourceName()), GlslParser.parseExpression("texelFetch(" + sampler.getSourceString() + ", " + lightmapUV.getSourceString() + " / 16, 0).rgb"), GlslAssignmentNode.Operand.EQUAL)); + } + modified = true; + this.validBuffers.computeInt(ctx.shaderInstance(), (unused, mask) -> (mask != null ? mask : 0) | type.getMask()); + } + } else if ((this.validBuffers.getInt(ctx.shaderInstance()) & type.getMask()) != 0) { + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("in vec3 Pass" + type.getSourceName())); + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("layout(location = " + (1 + i) + ") out " + type.getType().getSourceString() + " " + sourceName)); + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode(type.getSourceName()), GlslParser.parseExpression("vec4(Pass" + type.getSourceName() + ", 1.0)"), GlslAssignmentNode.Operand.EQUAL)); + modified = true; + } + } + } + + // Inject Normal passthrough into vertex and fragment shaders + if (type == DynamicBufferType.NORMAL && vertexFormat.contains(VertexFormatElement.NORMAL) && !markers.containsKey("veil:" + DynamicBufferType.NORMAL.getName())) { + if (vertexShader) { + Optional fieldOptional = tree.field(vertexFormat.getElementName(VertexFormatElement.NORMAL)); + if (fieldOptional.isPresent()) { + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("out vec3 Pass" + type.getSourceName())); + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode("Pass" + type.getSourceName()), new GlslVariableNode(fieldOptional.get().getName()), GlslAssignmentNode.Operand.EQUAL)); + modified = true; + this.validBuffers.computeInt(ctx.shaderInstance(), (unused, mask) -> (mask != null ? mask : 0) | type.getMask()); + } + } else if ((this.validBuffers.getInt(ctx.shaderInstance()) & type.getMask()) != 0) { + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("in vec3 Pass" + type.getSourceName())); + tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("layout(location = " + (1 + i) + ") out " + type.getType().getSourceString() + " " + sourceName)); + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode(type.getSourceName()), GlslParser.parseExpression("vec4(Pass" + type.getSourceName() + ", 1.0)"), GlslAssignmentNode.Operand.EQUAL)); + modified = true; + } + } + } + } + for (int i = 0; i < this.types.length; i++) { DynamicBufferType bufferType = this.types[i]; GlslNode node = markers.get("veil:" + bufferType.getName()); @@ -51,14 +195,13 @@ public String modify(Context ctx, String source) throws IOException { GlslSpecifiedType specifiedType = node.getType(); if (specifiedType == null || !(specifiedType.getSpecifier() instanceof GlslTypeSpecifier.BuiltinType nodeType) || (!nodeType.isPrimitive() && !nodeType.isVector()) || (!nodeType.isFloat() && !nodeType.isInteger() && !nodeType.isUnsignedInteger())) { Veil.LOGGER.warn("Invalid node marked '#veil:{}' in shader: {}", bufferType.getName(), ctx.name()); - return source; + continue; } String fieldName = bufferType.getSourceName(); GlslTypeSpecifier.BuiltinType outType = bufferType.getType(); tree.add(GlslInjectionPoint.BEFORE_MAIN, GlslParser.parseExpression("layout(location = " + (1 + i) + ") out " + outType.getSourceString() + " " + fieldName)); if (node instanceof GlslNewNode newNode) { - GlslNode expression; String cast = switch (outType) { case FLOAT, VEC2, VEC3, VEC4 -> !nodeType.isFloat() ? "float" : null; case INT, IVEC2, IVEC3, IVEC4 -> !nodeType.isInteger() ? "int" : null; @@ -66,6 +209,7 @@ public String modify(Context ctx, String source) throws IOException { default -> null; }; + GlslNode expression; String name = newNode.getName(); if (nodeType == outType) { expression = new GlslVariableNode(name); @@ -73,104 +217,89 @@ public String modify(Context ctx, String source) throws IOException { // Not enough components, so pad StringBuilder builder = new StringBuilder(outType.getSourceString()).append("("); String padding = outType.isFloat() ? "0.0" : outType.isUnsignedInteger() ? "0u" : "0"; - switch (outType.getComponents()) { - case 2 -> { - builder.append(cast != null ? cast + "(" + name + ".x), " : (name + ".x, ")); - builder.append(padding); - } - case 3 -> { - for (int j = 0; j < nodeType.getComponents(); j++) { - builder.append(cast != null ? cast + "(" + name + VECTOR_ELEMENTS[j] + "), " : (name + VECTOR_ELEMENTS[j] + ", ")); - } - for (int j = nodeType.getComponents(); j < 3; j++) { - builder.append(padding).append(", "); - } - builder.delete(builder.length() - 2, builder.length()); - builder.append(')'); - } - case 4 -> { - for (int j = 0; j < nodeType.getComponents(); j++) { - builder.append(cast != null ? cast + "(" + name + VECTOR_ELEMENTS[j] + "), " : (name + VECTOR_ELEMENTS[j] + ", ")); - } - for (int j = nodeType.getComponents(); j < 3; j++) { - builder.append(padding).append(", "); - } - builder.append(outType.isFloat() ? "1.0" : outType.isUnsignedInteger() ? "1u" : "1"); - builder.append(')'); + if (nodeType.getComponents() == 1) { + builder.append(cast != null ? cast + "(" + name + "), " : (name + ", ")); + } else { + for (int j = 0; j < nodeType.getComponents(); j++) { + builder.append(cast != null ? cast + "(" + name + VECTOR_ELEMENTS[j] + "), " : (name + VECTOR_ELEMENTS[j] + ", ")); } } - expression = GlslParser.parseExpression(builder.toString()); - } else if (nodeType.getComponents() > outType.getComponents()) { - // Too many components, so truncate - StringBuilder builder = new StringBuilder(outType.getSourceString()).append("("); - for (int j = 0; j < outType.getComponents(); j++) { - builder.append(cast != null ? cast + "(" + name + VECTOR_ELEMENTS[j] + "), " : (name + VECTOR_ELEMENTS[j] + ", ")); + for (int j = nodeType.getComponents(); j < 3; j++) { + builder.append(padding).append(", "); } - builder.delete(builder.length() - 2, builder.length()); + builder.append(outType.isFloat() ? "1.0" : outType.isUnsignedInteger() ? "1u" : "1"); builder.append(')'); expression = GlslParser.parseExpression(builder.toString()); } else { expression = GlslParser.parseExpression((cast != null ? outType.getSourceString() : "") + '(' + name + ')'); } - mainFunction.getBody().add(new GlslAssignmentNode(new GlslVariableNode(fieldName), expression, GlslAssignmentNode.Operand.EQUAL)); + mainFunctionBody.add(new GlslAssignmentNode(new GlslVariableNode(fieldName), expression, GlslAssignmentNode.Operand.EQUAL)); } } } if (modified) { - List outputs = new ArrayList<>(); - tree.fields().forEach(node -> { - GlslSpecifiedType type = node.getType(); - boolean valid = false; - for (GlslTypeQualifier qualifier : type.getQualifiers()) { - if (qualifier == GlslTypeQualifier.StorageType.OUT) { - valid = true; - break; + if (ctx.type() == GL_FRAGMENT_SHADER) { + List outputs = new ArrayList<>(); + tree.fields().forEach(node -> { + GlslSpecifiedType type = node.getType(); + boolean valid = false; + for (GlslTypeQualifier qualifier : type.getQualifiers()) { + if (qualifier == GlslTypeQualifier.StorageType.OUT) { + valid = true; + break; + } } - } - if (!valid) { - return; - } + if (!valid) { + return; + } + + for (GlslTypeQualifier qualifier : type.getQualifiers()) { + if (qualifier instanceof GlslTypeQualifier.Layout( + List layoutIds + )) { + for (GlslTypeQualifier.LayoutId layoutId : layoutIds) { + if (!"location".equals(layoutId.identifier())) { + continue; + } - for (GlslTypeQualifier qualifier : type.getQualifiers()) { - if (qualifier instanceof GlslTypeQualifier.Layout(List layoutIds)) { - for (GlslTypeQualifier.LayoutId layoutId : layoutIds) { - if ("location".equals(layoutId.identifier())) { GlslNode expression = layoutId.expression(); - if (expression != null) { - try { - int location = Integer.parseInt(expression.getSourceString()); - if (location == 0) { - outputs.clear(); - return; - } - valid = false; - break; - } catch (NumberFormatException ignored) { + if (expression == null) { + continue; + } + + try { + int location = Integer.parseInt(expression.getSourceString()); + if (location == 0) { + outputs.clear(); + return; } + valid = false; + break; + } catch (NumberFormatException ignored) { } } } } - } - if (valid) { - outputs.add(node); - } - }); + if (valid) { + outputs.add(node); + } + }); - for (GlslNewNode output : outputs) { - output.getType().addLayoutId("location", GlslNode.intConstant(0)); + for (GlslNewNode output : outputs) { + output.getType().addLayoutId("location", GlslNode.intConstant(0)); + } } GlslStringWriter writer = new GlslStringWriter(); tree.visit(writer); return writer.toString(); } - } catch (GlslSyntaxException e) { - Veil.LOGGER.error("Failed to transform shader: {}", ctx.name(), e); + } catch (Throwable t) { + Veil.LOGGER.error("Failed to transform shader: {}", ctx.name(), t); } return source; } diff --git a/common/src/main/java/foundry/veil/impl/client/render/shader/DummyResource.java b/common/src/main/java/foundry/veil/impl/client/render/shader/DummyResource.java new file mode 100644 index 00000000..1f932581 --- /dev/null +++ b/common/src/main/java/foundry/veil/impl/client/render/shader/DummyResource.java @@ -0,0 +1,31 @@ +package foundry.veil.impl.client.render.shader; + +import net.minecraft.server.packs.PackResources; +import net.minecraft.server.packs.repository.KnownPack; +import net.minecraft.server.packs.resources.IoSupplier; +import net.minecraft.server.packs.resources.Resource; + +import java.io.InputStream; +import java.util.Optional; + +public class DummyResource extends Resource { + + public DummyResource(IoSupplier streamSupplier) { + super(null, streamSupplier); + } + + @Override + public PackResources source() { + throw new UnsupportedOperationException("No pack source"); + } + + @Override + public String sourcePackId() { + return "dummy"; + } + + @Override + public Optional knownPackInfo() { + return Optional.empty(); + } +} diff --git a/common/src/main/java/foundry/veil/impl/client/render/shader/ShaderProgramImpl.java b/common/src/main/java/foundry/veil/impl/client/render/shader/ShaderProgramImpl.java index d1635035..35f02c86 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/shader/ShaderProgramImpl.java +++ b/common/src/main/java/foundry/veil/impl/client/render/shader/ShaderProgramImpl.java @@ -449,22 +449,7 @@ public static class Wrapper extends ShaderInstance { "fragment": "dummy" } """.getBytes(StandardCharsets.UTF_8); - private static final Resource RESOURCE = new Resource(null, () -> new ByteArrayInputStream(DUMMY_SHADER)) { - @Override - public PackResources source() { - throw new UnsupportedOperationException("No pack source"); - } - - @Override - public String sourcePackId() { - return "dummy"; - } - - @Override - public Optional knownPackInfo() { - return Optional.empty(); - } - }; + private static final Resource RESOURCE = new DummyResource(() -> new ByteArrayInputStream(DUMMY_SHADER)); private static final VertexFormat DUMMY_FORMAT = VertexFormat.builder().build(); public static boolean constructing = false; diff --git a/common/src/main/java/foundry/veil/impl/client/render/shader/SimpleShaderProcessor.java b/common/src/main/java/foundry/veil/impl/client/render/shader/SimpleShaderProcessor.java index 6c8055c5..5b7de3b9 100644 --- a/common/src/main/java/foundry/veil/impl/client/render/shader/SimpleShaderProcessor.java +++ b/common/src/main/java/foundry/veil/impl/client/render/shader/SimpleShaderProcessor.java @@ -1,10 +1,14 @@ package foundry.veil.impl.client.render.shader; +import com.mojang.blaze3d.vertex.VertexFormat; +import foundry.veil.api.client.render.VeilRenderSystem; +import foundry.veil.api.client.render.dynamicbuffer.DynamicBufferType; import foundry.veil.api.client.render.shader.definition.ShaderPreDefinitions; import foundry.veil.api.client.render.shader.processor.ShaderCustomProcessor; import foundry.veil.api.client.render.shader.processor.ShaderModifyProcessor; import foundry.veil.api.client.render.shader.processor.ShaderPreProcessor; import foundry.veil.api.client.render.shader.program.ProgramDefinition; +import foundry.veil.impl.client.render.dynamicbuffer.DynamicBufferProcessor; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceProvider; import org.jetbrains.annotations.ApiStatus; @@ -12,6 +16,7 @@ import java.io.IOException; import java.util.Collections; +import java.util.HashSet; import java.util.Set; /** @@ -20,28 +25,46 @@ @ApiStatus.Internal public class SimpleShaderProcessor { - private static ShaderPreProcessor processor; + private static final ThreadLocal PROCESSOR = new ThreadLocal<>(); + private static final Set LAST_FRAME_SHADERS = new HashSet<>(); public static void setup(ResourceProvider resourceProvider) { - processor = ShaderPreProcessor.allOf(new ShaderModifyProcessor(), new ShaderCustomProcessor(resourceProvider)); + int activeBuffers = VeilRenderSystem.renderer().getDynamicBufferManger().getActiveBuffers(); + SimpleShaderProcessor.PROCESSOR.set(ShaderPreProcessor.allOf(new ShaderModifyProcessor(), new ShaderCustomProcessor(resourceProvider), new DynamicBufferProcessor(DynamicBufferType.decode(activeBuffers)))); } public static void free() { - processor = null; + SimpleShaderProcessor.PROCESSOR.remove(); } - public static String modify(@Nullable ResourceLocation name, int type, String source) throws IOException { + public static String modify(@Nullable String shaderInstance, @Nullable ResourceLocation name, @Nullable VertexFormat vertexFormat, int type, String source) throws IOException { + ShaderPreProcessor processor = SimpleShaderProcessor.PROCESSOR.get(); if (processor == null) { throw new NullPointerException("Processor not initialized"); } - return processor.modify(new Context(name, type), source); + return processor.modify(new Context(shaderInstance, name, type, vertexFormat), source); } - private record Context(ResourceLocation name, int type) implements ShaderPreProcessor.Context { + public static void markRendered(String shaderInstace) { + if (VeilRenderSystem.renderer().getDynamicBufferManger().isCompilingShaders()) { + LAST_FRAME_SHADERS.add(shaderInstace); + } + } + + public static void clear() { + LAST_FRAME_SHADERS.clear(); + } + + public static Set getLastFrameShaders() { + return LAST_FRAME_SHADERS; + } + + private record Context(String shaderInstance, ResourceLocation name, int type, + VertexFormat vertexFormat) implements ShaderPreProcessor.Context { @Override public String modify(@Nullable ResourceLocation name, String source) throws IOException { - return processor.modify(new Context(name, this.type), source); + return SimpleShaderProcessor.PROCESSOR.get().modify(new Context(this.shaderInstance, name, this.type, this.vertexFormat), source); } @Override diff --git a/common/src/main/java/foundry/veil/impl/glsl/GlslParser.java b/common/src/main/java/foundry/veil/impl/glsl/GlslParser.java index 644e777a..64e9424a 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/GlslParser.java +++ b/common/src/main/java/foundry/veil/impl/glsl/GlslParser.java @@ -137,13 +137,25 @@ public static List parseExpressionList(GlslLexer.Token[] tokens) throw return new GlslIntConstantNode(GlslIntFormat.OCTAL, true, Integer.parseUnsignedInt(reader.peek(-1).value(), 8)); } if (reader.tryConsume(GlslLexer.TokenType.UINTEGER_DECIMAL_CONSTANT)) { - return new GlslIntConstantNode(GlslIntFormat.DECIMAL, false, Integer.parseUnsignedInt(reader.peek(-1).value(), 10)); + String value = reader.peek(-1).value(); + if (value.endsWith("u")) { + value = value.substring(0, value.length() - 1); + } + return new GlslIntConstantNode(GlslIntFormat.DECIMAL, false, Integer.parseUnsignedInt(value, 10)); } if (reader.tryConsume(GlslLexer.TokenType.UINTEGER_HEXADECIMAL_CONSTANT)) { - return new GlslIntConstantNode(GlslIntFormat.HEXADECIMAL, false, Integer.parseUnsignedInt(reader.peek(-1).value(), 16)); + String value = reader.peek(-1).value(); + if (value.endsWith("u")) { + value = value.substring(0, value.length() - 1); + } + return new GlslIntConstantNode(GlslIntFormat.HEXADECIMAL, false, Integer.parseUnsignedInt(value, 16)); } if (reader.tryConsume(GlslLexer.TokenType.UINTEGER_OCTAL_CONSTANT)) { - return new GlslIntConstantNode(GlslIntFormat.OCTAL, false, Integer.parseUnsignedInt(reader.peek(-1).value(), 8)); + String value = reader.peek(-1).value(); + if (value.endsWith("u")) { + value = value.substring(0, value.length() - 1); + } + return new GlslIntConstantNode(GlslIntFormat.OCTAL, false, Integer.parseUnsignedInt(value, 8)); } if (reader.tryConsume(GlslLexer.TokenType.FLOATING_CONSTANT)) { return new GlslFloatConstantNode(Float.parseFloat(reader.peek(-1).value())); diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/GlslCompoundNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/GlslCompoundNode.java index b28ef0cd..fcf0a316 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/GlslCompoundNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/GlslCompoundNode.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; public class GlslCompoundNode implements GlslNode { @@ -18,6 +19,11 @@ public List toList() { return new ArrayList<>(this.children); } + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.children.stream().flatMap(GlslNode::stream)); + } + public List getChildren() { return this.children; } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/GlslConstantNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/GlslConstantNode.java index 23e4a35d..a24b842f 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/GlslConstantNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/GlslConstantNode.java @@ -1,5 +1,7 @@ package foundry.veil.impl.glsl.node; +import java.util.stream.Stream; + public interface GlslConstantNode extends GlslNode { Number numberValue(); @@ -23,4 +25,9 @@ default long unsignedIntValue() { boolean booleanValue(); boolean isNumber(); + + @Override + default Stream stream() { + return Stream.of(this); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/GlslEmptyNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/GlslEmptyNode.java index db2c8879..9441ffc2 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/GlslEmptyNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/GlslEmptyNode.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; public enum GlslEmptyNode implements GlslNode { INSTANCE; @@ -16,4 +17,9 @@ public List toList() { return new ArrayList<>(); } + @Override + public Stream stream() { + return Stream.empty(); + } + } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/GlslNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/GlslNode.java index 5e7dad2d..82691789 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/GlslNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/GlslNode.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +import java.util.stream.Stream; public interface GlslNode { @@ -27,6 +28,8 @@ default List toList() { return new ArrayList<>(Collections.singleton(this)); } + Stream stream(); + static GlslIntConstantNode intConstant(int value) { return new GlslIntConstantNode(GlslIntFormat.DECIMAL, true, value); } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/GlslTree.java b/common/src/main/java/foundry/veil/impl/glsl/node/GlslTree.java index 4a795766..ac588944 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/GlslTree.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/GlslTree.java @@ -80,6 +80,10 @@ public Stream functions() { return this.body.stream().filter(node -> node instanceof GlslFunctionNode).map(node -> (GlslFunctionNode) node); } + public Optional field(String name) { + return this.body.stream().filter(node -> node instanceof GlslNewNode newNode && name.equals(newNode.getName())).findFirst().map(newNode -> (GlslNewNode) newNode); + } + public Stream fields() { return this.body.stream().filter(node -> node instanceof GlslNewNode).map(node -> (GlslNewNode) node); } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/branch/ForLoopNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/branch/ForLoopNode.java index a3cc9348..360641e8 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/branch/ForLoopNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/branch/ForLoopNode.java @@ -1,11 +1,13 @@ package foundry.veil.impl.glsl.node.branch; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; /** * Represents for loops. @@ -88,4 +90,9 @@ public String getSourceString() { builder.append('}'); return builder.toString(); } + + @Override + public Stream stream() { + return Streams.concat(this.init.stream(), this.condition.stream(), this.increment.stream()); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslCaseLabelNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslCaseLabelNode.java index 2cecced2..47fd1852 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslCaseLabelNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslCaseLabelNode.java @@ -3,6 +3,8 @@ import foundry.veil.impl.glsl.node.GlslNode; import org.jetbrains.annotations.Nullable; +import java.util.stream.Stream; + public class GlslCaseLabelNode implements GlslNode { private GlslNode condition; @@ -32,4 +34,9 @@ public String toString() { public String getSourceString() { return "case: " + this.condition.getSourceString(); } + + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.condition.stream()); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslReturnNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslReturnNode.java index 243f2ab9..179104d6 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslReturnNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslReturnNode.java @@ -3,6 +3,8 @@ import foundry.veil.impl.glsl.node.GlslNode; import org.jetbrains.annotations.Nullable; +import java.util.stream.Stream; + public class GlslReturnNode implements GlslNode { private GlslNode value; @@ -28,4 +30,9 @@ public String toString() { public String getSourceString() { return this.value != null ? "return " + this.value.getSourceString() : "return"; } + + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.value.stream()); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSelectionNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSelectionNode.java index 0354f250..8b592e7b 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSelectionNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSelectionNode.java @@ -1,9 +1,11 @@ package foundry.veil.impl.glsl.node.branch; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; /** * if/else @@ -68,6 +70,11 @@ public String getSourceString() { return builder.toString(); } + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.first.stream().flatMap(GlslNode::stream), this.second.stream().flatMap(GlslNode::stream)); + } + @Override public String toString() { return "GlslSelectionNode{" + diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSwitchNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSwitchNode.java index c217799f..b68981a6 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSwitchNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/branch/GlslSwitchNode.java @@ -1,11 +1,13 @@ package foundry.veil.impl.glsl.node.branch; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; /** * Switch statement. @@ -85,4 +87,9 @@ public String getSourceString() { builder.append('}'); return builder.toString(); } + + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.condition.stream(), this.branches.stream().flatMap(GlslNode::stream)); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/branch/JumpNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/branch/JumpNode.java index b9100d23..31be84bc 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/branch/JumpNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/branch/JumpNode.java @@ -3,6 +3,7 @@ import foundry.veil.impl.glsl.node.GlslNode; import java.util.Locale; +import java.util.stream.Stream; public enum JumpNode implements GlslNode { CONTINUE, BREAK, DISCARD; @@ -11,4 +12,9 @@ public enum JumpNode implements GlslNode { public String getSourceString() { return this.name().toLowerCase(Locale.ROOT); } + + @Override + public Stream stream() { + return Stream.of(this); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/branch/WhileLoopNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/branch/WhileLoopNode.java index 73fd4907..180c2545 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/branch/WhileLoopNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/branch/WhileLoopNode.java @@ -1,7 +1,10 @@ package foundry.veil.impl.glsl.node.branch; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + /** * Represents both while and do/while loops. * @@ -51,6 +54,11 @@ public String getSourceString() { return "while (" + this.condition.getSourceString() + ") {\n" + this.body.getSourceString().replaceAll("\n", "\n\t") + "\n}"; } + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.condition.stream(), this.body.stream()); + } + public enum Type { WHILE, DO } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAndNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAndNode.java index 5df7f6a5..90fe7597 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAndNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAndNode.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Ocelot @@ -39,6 +40,11 @@ public String getSourceString() { return this.expressions.stream().map(GlslNode::getSourceString).collect(Collectors.joining(" & ")); } + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expressions.stream().flatMap(GlslNode::stream)); + } + @Override public String toString() { return "GlslAndNode[expressions=" + this.expressions + ']'; diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAssignmentNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAssignmentNode.java index 0e5b2b1b..4d260002 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAssignmentNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslAssignmentNode.java @@ -1,9 +1,12 @@ package foundry.veil.impl.glsl.node.expression; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.grammar.GlslSpecifiedType; import foundry.veil.impl.glsl.node.GlslNode; import org.jetbrains.annotations.Nullable; +import java.util.stream.Stream; + /** * @author Ocelot */ @@ -70,6 +73,11 @@ public String getSourceString() { return this.first.getSourceString() + ' ' + this.operand.getDelimiter() + ' ' + this.second.getSourceString(); } + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.first.stream(), this.second.stream()); + } + @Override public String toString() { return "GlslAssignmentNode{" + diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslCompareNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslCompareNode.java index 986613d5..c209855c 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslCompareNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslCompareNode.java @@ -1,7 +1,10 @@ package foundry.veil.impl.glsl.node.expression; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + /** * @author Ocelot */ @@ -22,6 +25,11 @@ public String getSourceString() { return this.first.getSourceString() + ' ' + this.operand.getDelimiter() + ' ' + this.second.getSourceString(); } + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.first.stream(), this.second.stream()); + } + /** * @return The first operand */ diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslConditionalNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslConditionalNode.java index fb4b05dc..ea24fe0a 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslConditionalNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslConditionalNode.java @@ -1,7 +1,10 @@ package foundry.veil.impl.glsl.node.expression; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + /** * @author Ocelot */ @@ -49,6 +52,11 @@ public String getSourceString() { return this.condition.getSourceString() + " ? " + this.first.getSourceString() + " : " + this.second.getSourceString(); } + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.first.stream(), this.second.stream()); + } + @Override public String toString() { return "GlslConditionalNode{" + diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslExclusiveOrNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslExclusiveOrNode.java index ba1a22ac..c1e2c525 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslExclusiveOrNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslExclusiveOrNode.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Ocelot @@ -18,13 +19,18 @@ public GlslExclusiveOrNode(Collection expressions) { this.expressions = new ArrayList<>(expressions); } + public List getExpressions() { + return this.expressions; + } + @Override public String getSourceString() { return this.expressions.stream().map(GlslNode::getSourceString).collect(Collectors.joining(" ^ ")); } - public List expressions() { - return this.expressions; + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expressions.stream().flatMap(GlslNode::stream)); } @Override diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslInclusiveOrNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslInclusiveOrNode.java index 00f58cde..6c82f73d 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslInclusiveOrNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslInclusiveOrNode.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Ocelot @@ -18,13 +19,18 @@ public GlslInclusiveOrNode(Collection expressions) { this.expressions = new ArrayList<>(expressions); } + public List getExpressions() { + return this.expressions; + } + @Override public String getSourceString() { return this.expressions.stream().map(GlslNode::getSourceString).collect(Collectors.joining(" | ")); } - public List expressions() { - return this.expressions; + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expressions.stream().flatMap(GlslNode::stream)); } @Override diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalAndNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalAndNode.java index 45fcad53..7dde1785 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalAndNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalAndNode.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Ocelot @@ -18,13 +19,18 @@ public GlslLogicalAndNode(Collection expressions) { this.expressions = new ArrayList<>(expressions); } + public List getExpressions() { + return this.expressions; + } + @Override public String getSourceString() { return this.expressions.stream().map(GlslNode::getSourceString).collect(Collectors.joining(" && ")); } - public List expressions() { - return this.expressions; + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expressions.stream().flatMap(GlslNode::stream)); } @Override diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalOrNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalOrNode.java index 64cd7083..e8c475a9 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalOrNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalOrNode.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Ocelot @@ -18,13 +19,18 @@ public GlslLogicalOrNode(Collection expressions) { this.expressions = new ArrayList<>(expressions); } + public List getExpressions() { + return this.expressions; + } + @Override public String getSourceString() { return this.expressions.stream().map(GlslNode::getSourceString).collect(Collectors.joining(" || ")); } - public List expressions() { - return this.expressions; + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expressions.stream().flatMap(GlslNode::stream)); } @Override diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalXorNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalXorNode.java index d5690545..1b4dea7e 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalXorNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslLogicalXorNode.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Ocelot @@ -18,13 +19,18 @@ public GlslLogicalXorNode(Collection expressions) { this.expressions = new ArrayList<>(expressions); } + public List getExpressions() { + return this.expressions; + } + @Override public String getSourceString() { return this.expressions.stream().map(GlslNode::getSourceString).collect(Collectors.joining(" ^^ ")); } - public List expressions() { - return this.expressions; + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expressions.stream().flatMap(GlslNode::stream)); } @Override diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslOperationNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslOperationNode.java index 292ad59e..ab976355 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslOperationNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslOperationNode.java @@ -1,7 +1,10 @@ package foundry.veil.impl.glsl.node.expression; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + /** * @author Ocelot */ @@ -41,6 +44,11 @@ public String getSourceString() { return builder.toString(); } + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.first.stream(), this.second.stream()); + } + /** * @return The first operand */ diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslPrecisionNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslPrecisionNode.java index 707ce14d..033c83fa 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslPrecisionNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslPrecisionNode.java @@ -4,6 +4,8 @@ import foundry.veil.impl.glsl.grammar.GlslTypeSpecifier; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + public class GlslPrecisionNode implements GlslNode { private GlslTypeQualifier.Precision precision; @@ -36,4 +38,9 @@ public GlslPrecisionNode setTypeSpecifier(GlslTypeSpecifier typeSpecifier) { public String getSourceString() { return "precision " + this.precision.getSourceString() + " " + this.typeSpecifier.getSourceString(); } + + @Override + public Stream stream() { + return Stream.of(this); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslUnaryNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslUnaryNode.java index 2284c2ce..c4493cd1 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslUnaryNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/expression/GlslUnaryNode.java @@ -4,6 +4,8 @@ import foundry.veil.impl.glsl.node.GlslNode; import org.jetbrains.annotations.Nullable; +import java.util.stream.Stream; + public class GlslUnaryNode implements GlslNode { private GlslNode expression; @@ -19,6 +21,11 @@ public GlslUnaryNode(GlslNode expression, Operand operand) { return this.expression.getType(); } + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expression.stream()); + } + public GlslNode getExpression() { return this.expression; } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslFunctionNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslFunctionNode.java index 49851ae9..ee39489d 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslFunctionNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslFunctionNode.java @@ -11,6 +11,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; /** * Defines a function in a GLSL file with an optional body. @@ -33,7 +34,7 @@ public void visit(GlslFunctionVisitor visitor) { visitor.visitReturn(returnNode); return; } - if(node instanceof GlslAssignmentNode assignmentNode) { + if (node instanceof GlslAssignmentNode assignmentNode) { visitor.visitAssignment(assignmentNode); return; } @@ -109,6 +110,11 @@ public String getSourceString() { return builder.toString(); } + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.body.stream().flatMap(GlslNode::stream)); + } + @Override public String toString() { return "GlslFunction{header=" + this.header + ", body=" + this.body + '}'; diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslInvokeFunctionNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslInvokeFunctionNode.java index 4630229a..0d646c4d 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslInvokeFunctionNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslInvokeFunctionNode.java @@ -6,6 +6,7 @@ import java.util.Collection; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; public class GlslInvokeFunctionNode implements GlslNode { @@ -39,4 +40,9 @@ public String getSourceString() { String parameters = this.parameters.stream().map(GlslNode::getSourceString).collect(Collectors.joining(", ")); return this.header.getSourceString() + "(" + parameters + ")"; } + + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.parameters.stream().flatMap(GlslNode::stream)); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslPrimitiveConstructorNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslPrimitiveConstructorNode.java index 5c8b0ec3..d0b48251 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslPrimitiveConstructorNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/function/GlslPrimitiveConstructorNode.java @@ -3,6 +3,8 @@ import foundry.veil.impl.glsl.grammar.GlslTypeSpecifier; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + public class GlslPrimitiveConstructorNode implements GlslNode { private GlslTypeSpecifier primitiveType; @@ -28,4 +30,9 @@ public String toString() { public String getSourceString() { return this.primitiveType.getSourceString() + this.primitiveType.getPostSourceString(); } + + @Override + public Stream stream() { + return Stream.of(this); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslArrayNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslArrayNode.java index 6555021f..4252a097 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslArrayNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslArrayNode.java @@ -1,7 +1,10 @@ package foundry.veil.impl.glsl.node.variable; +import com.google.common.collect.Streams; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + public class GlslArrayNode implements GlslNode { private GlslNode expression; @@ -35,6 +38,11 @@ public String getSourceString() { return this.expression.getSourceString() + '[' + this.index.getSourceString() + ']'; } + @Override + public Stream stream() { + return Streams.concat(Stream.of(this), this.expression.stream(), this.index.stream()); + } + @Override public String toString() { return "GlslArrayNode{" + diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslDeclaration.java b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslDeclaration.java index 2e00028f..88b2ac11 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslDeclaration.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslDeclaration.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.stream.Stream; public class GlslDeclaration implements GlslNode { @@ -39,4 +40,9 @@ public String getSourceString() { } return builder.toString().trim(); } + + @Override + public Stream stream() { + return Stream.of(this); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslFieldNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslFieldNode.java index 2f929446..535c1c86 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslFieldNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslFieldNode.java @@ -2,6 +2,8 @@ import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + public class GlslFieldNode implements GlslNode { private GlslNode expression; @@ -38,6 +40,11 @@ public String getSourceString() { return '(' + this.expression.getSourceString() + ")." + this.fieldSelection; } + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.expression.stream()); + } + @Override public String toString() { return "GlslFieldNode{" + diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslNewNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslNewNode.java index b579bf3b..7246ea47 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslNewNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslNewNode.java @@ -5,6 +5,8 @@ import foundry.veil.impl.glsl.node.GlslNode; import org.jetbrains.annotations.Nullable; +import java.util.stream.Stream; + public class GlslNewNode implements GlslNode { private GlslSpecifiedType type; @@ -22,6 +24,11 @@ public GlslSpecifiedType getType() { return this.type; } + @Override + public Stream stream() { + return Stream.concat(Stream.of(this), this.initializer.stream()); + } + public @Nullable String getName() { return this.name; } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslStructNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslStructNode.java index 97260044..fcaa1ebc 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslStructNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslStructNode.java @@ -3,6 +3,8 @@ import foundry.veil.impl.glsl.grammar.GlslSpecifiedType; import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + public class GlslStructNode implements GlslNode { private GlslSpecifiedType specifiedType; @@ -18,4 +20,9 @@ public String getSourceString() { } return this.specifiedType.getSourceString(); } + + @Override + public Stream stream() { + return Stream.of(this); + } } diff --git a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslVariableNode.java b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslVariableNode.java index b1872883..627d29fc 100644 --- a/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslVariableNode.java +++ b/common/src/main/java/foundry/veil/impl/glsl/node/variable/GlslVariableNode.java @@ -2,6 +2,8 @@ import foundry.veil.impl.glsl.node.GlslNode; +import java.util.stream.Stream; + public class GlslVariableNode implements GlslNode { private String name; @@ -24,6 +26,11 @@ public String getSourceString() { return this.name; } + @Override + public Stream stream() { + return Stream.of(this); + } + @Override public String toString() { return "GlslVariableNode{" + diff --git a/common/src/main/java/foundry/veil/mixin/accessor/ProgramAccessor.java b/common/src/main/java/foundry/veil/mixin/accessor/ProgramAccessor.java index 9e6dda3a..b547a940 100644 --- a/common/src/main/java/foundry/veil/mixin/accessor/ProgramAccessor.java +++ b/common/src/main/java/foundry/veil/mixin/accessor/ProgramAccessor.java @@ -8,5 +8,5 @@ public interface ProgramAccessor { @Accessor - void setId(int id); + int getId(); } diff --git a/common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/GameRendererMixin.java b/common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/GameRendererMixin.java new file mode 100644 index 00000000..a096ce63 --- /dev/null +++ b/common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/GameRendererMixin.java @@ -0,0 +1,27 @@ +package foundry.veil.mixin.client.dynamicbuffer; + +import foundry.veil.api.client.render.VeilRenderSystem; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.server.packs.resources.ResourceProvider; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Map; + +@Mixin(GameRenderer.class) +public class GameRendererMixin { + + @Shadow + @Final + private Map shaders; + + @Inject(method = "reloadShaders", at = @At("RETURN")) + public void reloadShaders(ResourceProvider resourceProvider, CallbackInfo ci) { + VeilRenderSystem.renderer().getDynamicBufferManger().reloadVanillaShaders(this.shaders.values()); + } +} diff --git a/common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/ShaderInstanceMixin.java b/common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/ShaderInstanceMixin.java new file mode 100644 index 00000000..5cea161a --- /dev/null +++ b/common/src/main/java/foundry/veil/mixin/client/dynamicbuffer/ShaderInstanceMixin.java @@ -0,0 +1,144 @@ +package foundry.veil.mixin.client.dynamicbuffer; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.shaders.Program; +import com.mojang.blaze3d.shaders.ProgramManager; +import com.mojang.blaze3d.shaders.Shader; +import com.mojang.blaze3d.shaders.Uniform; +import com.mojang.blaze3d.vertex.VertexFormat; +import foundry.veil.Veil; +import foundry.veil.ext.ShaderInstanceExtension; +import foundry.veil.impl.client.render.shader.SimpleShaderProcessor; +import foundry.veil.mixin.accessor.ProgramAccessor; +import net.minecraft.client.renderer.ShaderInstance; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceProvider; +import org.apache.commons.lang3.StringUtils; +import org.spongepowered.asm.mixin.*; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static org.lwjgl.opengl.GL20C.*; + +@Mixin(ShaderInstance.class) +public abstract class ShaderInstanceMixin implements Shader, ShaderInstanceExtension { + + @Shadow + private static Program getOrCreate(ResourceProvider resourceProvider, Program.Type programType, String name) throws IOException { + return null; + } + + @Mutable + @Shadow + @Final + private Program vertexProgram; + + @Mutable + @Shadow + @Final + private Program fragmentProgram; + + @Shadow + @Final + private int programId; + + @Shadow + @Final + private VertexFormat vertexFormat; + + @Shadow + protected abstract void updateLocations(); + + @Shadow + @Final + public Map uniformMap; + + @Shadow + @Final + private List uniformLocations; + + @Shadow + @Final + private List samplerLocations; + + @Shadow + @Final + private String name; + @Unique + private String veil$vertexSource; + @Unique + private String veil$fragmentSource; + + @Inject(method = "apply", at = @At("HEAD")) + public void apply(CallbackInfo ci) { + SimpleShaderProcessor.markRendered(this.name); + if (this.veil$vertexSource != null && this.veil$fragmentSource != null) { + try { + ProgramAccessor vertexAccessor = (ProgramAccessor) this.vertexProgram; + ProgramAccessor fragmentAccessor = (ProgramAccessor) this.fragmentProgram; + + glDetachShader(this.programId, vertexAccessor.getId()); + glDetachShader(this.programId, fragmentAccessor.getId()); + + GlStateManager.glShaderSource(vertexAccessor.getId(), List.of(this.veil$vertexSource)); + GlStateManager.glCompileShader(vertexAccessor.getId()); + if (GlStateManager.glGetShaderi(vertexAccessor.getId(), GL_COMPILE_STATUS) == 0) { + String error = StringUtils.trim(glGetShaderInfoLog(vertexAccessor.getId())); + throw new IOException("Couldn't compile vertex program (" + this.vertexProgram.getName() + ", " + this.name + ") : " + error); + } + + GlStateManager.glShaderSource(fragmentAccessor.getId(), List.of(this.veil$fragmentSource)); + GlStateManager.glCompileShader(fragmentAccessor.getId()); + if (GlStateManager.glGetShaderi(fragmentAccessor.getId(), GL_COMPILE_STATUS) == 0) { + String error = StringUtils.trim(glGetShaderInfoLog(fragmentAccessor.getId())); + throw new IOException("Couldn't compile fragment program (" + this.fragmentProgram.getName() + ", " + this.name + ") : " + error); + } + + int i = 0; + for (String name : this.vertexFormat.getElementAttributeNames()) { + Uniform.glBindAttribLocation(this.programId, i, name); + i++; + } + + this.uniformLocations.clear(); + this.samplerLocations.clear(); + this.uniformMap.clear(); + + // Force re-link + ProgramManager.linkShader(this); + this.updateLocations(); + + this.markDirty(); + } catch (Throwable t) { + Veil.LOGGER.error("Failed to recompile vanilla shader: {}", this.name, t); + } + this.veil$vertexSource = null; + this.veil$fragmentSource = null; + } + } + + @Override + public Collection veil$getShaderSources() { + // TODO probably extra code for iris/sodium needed + ResourceLocation vertexProgramName = ResourceLocation.parse(this.vertexProgram.getName()); + ResourceLocation fragmentProgramName = ResourceLocation.parse(this.fragmentProgram.getName()); + ResourceLocation vertexPath = ResourceLocation.fromNamespaceAndPath(vertexProgramName.getNamespace(), "shaders/core/" + vertexProgramName.getPath() + Program.Type.VERTEX.getExtension()); + ResourceLocation fragmentPath = ResourceLocation.fromNamespaceAndPath(fragmentProgramName.getNamespace(), "shaders/core/" + fragmentProgramName.getPath() + Program.Type.FRAGMENT.getExtension()); + return List.of(vertexPath, fragmentPath); + } + + @Override + public void veil$recompile(boolean vertex, String source) { + if (vertex) { + this.veil$vertexSource = source; + } else { + this.veil$fragmentSource = source; + } + } +} diff --git a/common/src/main/java/foundry/veil/mixin/client/shader/GameRendererMixin.java b/common/src/main/java/foundry/veil/mixin/client/shader/GameRendererMixin.java index 1d4b0548..8a80575c 100644 --- a/common/src/main/java/foundry/veil/mixin/client/shader/GameRendererMixin.java +++ b/common/src/main/java/foundry/veil/mixin/client/shader/GameRendererMixin.java @@ -39,16 +39,13 @@ public class GameRendererMixin { VeilRenderer renderer = VeilRenderSystem.renderer(); Collection modifiers = renderer.getShaderModificationManager().getModifiers(id); - if (modifiers.size() == 1) { - ShaderModification modification = modifiers.iterator().next(); - if (modification instanceof ReplaceShaderModification replaceModification) { - ShaderProgram shader = renderer.getShaderManager().getShader(replaceModification.veilShader()); - if (shader == null) { - LOGGER.error("Failed to replace vanilla shader '{}' with veil shader: {}", loc, replaceModification.veilShader()); - } else { - return shader.toShaderInstance(); - } + if (modifiers.size() == 1 && modifiers.iterator().next() instanceof ReplaceShaderModification replaceModification) { + ShaderProgram shader = renderer.getShaderManager().getShader(replaceModification.veilShader()); + if (shader != null) { + return shader.toShaderInstance(); } + + LOGGER.error("Failed to replace vanilla shader '{}' with veil shader: {}", loc, replaceModification.veilShader()); } return new ShaderInstance(resourceProvider, name, vertexFormat); diff --git a/common/src/main/java/foundry/veil/mixin/client/shader/ProgramMixin.java b/common/src/main/java/foundry/veil/mixin/client/shader/ProgramMixin.java index 8ad0ea9a..4cfa206c 100644 --- a/common/src/main/java/foundry/veil/mixin/client/shader/ProgramMixin.java +++ b/common/src/main/java/foundry/veil/mixin/client/shader/ProgramMixin.java @@ -48,7 +48,7 @@ public class ProgramMixin { source.append(sourceLine); } - return List.of(SimpleShaderProcessor.modify(veil$captureId, veil$captureType, source.toString())); + return List.of(SimpleShaderProcessor.modify(null, veil$captureId, null, veil$captureType, source.toString())); } catch (Exception e) { Veil.LOGGER.error("Failed to modify vanilla source for shader: {}", veil$captureId, e); } diff --git a/common/src/main/java/foundry/veil/platform/VeilPlatform.java b/common/src/main/java/foundry/veil/platform/VeilPlatform.java index 877361f1..766c8ffa 100644 --- a/common/src/main/java/foundry/veil/platform/VeilPlatform.java +++ b/common/src/main/java/foundry/veil/platform/VeilPlatform.java @@ -34,7 +34,7 @@ public interface VeilPlatform { boolean isDevelopmentEnvironment(); enum PlatformType { - FORGE("Forge"), + NEOFORGE("Forge"), FABRIC("Fabric"); private final String platformName; diff --git a/common/src/main/resources/assets/veil/pinwheel/shaders/program/quasar/particle.fsh b/common/src/main/resources/assets/veil/pinwheel/shaders/program/quasar/particle.fsh index 83f79f6d..691cb8c9 100644 --- a/common/src/main/resources/assets/veil/pinwheel/shaders/program/quasar/particle.fsh +++ b/common/src/main/resources/assets/veil/pinwheel/shaders/program/quasar/particle.fsh @@ -14,9 +14,11 @@ in vec4 vertexColor; out vec4 fragColor; void main() { + // #veil:albedo vec4 color = texture(Sampler0, texCoord0) * vertexColor * ColorModulator; if (color.a < 0.01) { discard; } fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor); } + diff --git a/common/src/main/resources/veil.mixins.json b/common/src/main/resources/veil.mixins.json index 51c00113..93c04804 100644 --- a/common/src/main/resources/veil.mixins.json +++ b/common/src/main/resources/veil.mixins.json @@ -25,7 +25,9 @@ "client.deferred.DebugScreenOverlayMixin", "client.deferred.LevelRendererMixin", "client.deferred.ShaderStateMixin", + "client.dynamicbuffer.GameRendererMixin", "client.dynamicbuffer.LevelRendererMixin", + "client.dynamicbuffer.ShaderInstanceMixin", "client.imgui.ImGuiImplGlfwMixin", "client.imgui.KeyboardHandlerMixin", "client.imgui.MouseHandlerMixin", diff --git a/fabric/src/main/java/foundry/veil/fabric/mixin/compat/sodium/ShaderLoaderMixin.java b/fabric/src/main/java/foundry/veil/fabric/mixin/compat/sodium/ShaderLoaderMixin.java index e0669705..76b32002 100644 --- a/fabric/src/main/java/foundry/veil/fabric/mixin/compat/sodium/ShaderLoaderMixin.java +++ b/fabric/src/main/java/foundry/veil/fabric/mixin/compat/sodium/ShaderLoaderMixin.java @@ -31,7 +31,7 @@ public class ShaderLoaderMixin { private static String modifySource(String src) { try { SimpleShaderProcessor.setup(Minecraft.getInstance().getResourceManager()); - return SimpleShaderProcessor.modify(ResourceLocation.fromNamespaceAndPath(veil$shaderName.getNamespace(), "shaders/" + veil$shaderName.getPath()), veil$shaderType, src); + return SimpleShaderProcessor.modify(null, ResourceLocation.fromNamespaceAndPath(veil$shaderName.getNamespace(), "shaders/" + veil$shaderName.getPath()), null, veil$shaderType, src); } catch (Exception e) { Veil.LOGGER.error("Failed to apply Veil shader modifiers to shader: {}", veil$shaderName, e); return src; diff --git a/neoforge/src/main/java/foundry/veil/forge/platform/ForgeVeilPlatform.java b/neoforge/src/main/java/foundry/veil/forge/platform/ForgeVeilPlatform.java index 53a2eb1b..dd6a9681 100644 --- a/neoforge/src/main/java/foundry/veil/forge/platform/ForgeVeilPlatform.java +++ b/neoforge/src/main/java/foundry/veil/forge/platform/ForgeVeilPlatform.java @@ -9,7 +9,7 @@ public class ForgeVeilPlatform implements VeilPlatform { @Override public PlatformType getPlatformType() { - return PlatformType.FORGE; + return PlatformType.NEOFORGE; } @Override