From c1b7a1c19d43b4f2e4b0db226792bc61dbc8ad5b Mon Sep 17 00:00:00 2001 From: JozsefA Date: Tue, 11 May 2021 18:24:12 -0700 Subject: [PATCH] Extremely fancy shader templating - Nothing is properly hooked up yet. - A single shader file will be able to be compiled with different opengl targets. - I plan on using this to implement meshlet rendering as an alternate backend-backend that will be used when available. - It could also be used to enable compatibility with opengl 3.0 or potentially even 2.0. --- .../flywheel/backend/ShaderLoader.java | 24 ++++- .../InstancedArraysShaderTemplate.java | 102 ++++++++++++++++++ .../backend/loading/ParsedShader.java | 42 ++++++++ .../backend/loading/TaggedStruct.java | 36 +++++++ .../create/flywheel/shaders/block_new.frag | 19 ++++ .../create/flywheel/shaders/model_new.vert | 45 ++++++++ .../shaders/skeleton/instanced/instanced.frag | 12 +++ .../shaders/skeleton/instanced/instanced.vert | 12 ++- 8 files changed, 282 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/jozufozu/flywheel/backend/loading/InstancedArraysShaderTemplate.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/loading/ParsedShader.java create mode 100644 src/main/java/com/jozufozu/flywheel/backend/loading/TaggedStruct.java create mode 100644 src/main/resources/assets/create/flywheel/shaders/block_new.frag create mode 100644 src/main/resources/assets/create/flywheel/shaders/model_new.vert create mode 100644 src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.frag diff --git a/src/main/java/com/jozufozu/flywheel/backend/ShaderLoader.java b/src/main/java/com/jozufozu/flywheel/backend/ShaderLoader.java index bb1ed25c0..65497611b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/ShaderLoader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/ShaderLoader.java @@ -58,6 +58,16 @@ public class ShaderLoader { shaderSource.clear(); loadShaderSources(manager); +// ResourceLocation test = new ResourceLocation("create", "model_new.vert"); +// ResourceLocation vert = new ResourceLocation("create", "skeleton/instanced/instanced.vert"); +// +// InstancedArraysShaderTemplate template = new InstancedArraysShaderTemplate(getShaderSource(vert)); +// ParsedShader parsedShader = new ParsedShader(getShaderSource(test)); +// +// String apply = template.apply(parsedShader); +// +// printSource(test, apply); + for (ShaderContext context : Backend.contexts.values()) { context.load(this); } @@ -134,16 +144,20 @@ public class ShaderLoader { source = defines.process(source); if (debugDumpFile) { - Backend.log.debug("Finished processing '" + name + "':"); - int i = 1; - for (String s : source.split("\n")) { - Backend.log.debug(String.format("%1$4s: ", i++) + s); - } + printSource(name, source); } return new GlShader(type, name, source); } + private void printSource(ResourceLocation name, String source) { + Backend.log.debug("Finished processing '" + name + "':"); + int i = 1; + for (String s : source.split("\n")) { + Backend.log.debug(String.format("%1$4s: ", i++) + s); + } + } + private String processIncludes(String source, ResourceLocation baseName) { HashSet seen = new HashSet<>(); seen.add(baseName); diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/InstancedArraysShaderTemplate.java b/src/main/java/com/jozufozu/flywheel/backend/loading/InstancedArraysShaderTemplate.java new file mode 100644 index 000000000..ce6eb5db2 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/loading/InstancedArraysShaderTemplate.java @@ -0,0 +1,102 @@ +package com.jozufozu.flywheel.backend.loading; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class InstancedArraysShaderTemplate { + + private static final String delimiter = "#flwbeginbody"; + private static final Pattern headerFinder = Pattern.compile(delimiter); + + private static final Pattern prefixer = Pattern.compile("#FLWPrefixFields\\((\\S+),\\s*([^\\n]+)\\)"); + private static final Pattern assigner = Pattern.compile("#FLWAssignFields\\(([\\w\\d_]+),\\s*([\\w\\d_.]+),\\s*([\\w\\d_.]+)\\)"); + + public static final String[] required = {"FLWInstanceData", "FLWVertexData", "FLWFragment"}; + + final String header; + final String body; + + public InstancedArraysShaderTemplate(String templateSrc) { + Matcher matcher = headerFinder.matcher(templateSrc); + + if (!matcher.find()) { + throw new RuntimeException("Shader template must have a header and footer delimited by '" + delimiter + "'"); + } + + this.header = templateSrc.substring(0, matcher.start()); + this.body = templateSrc.substring(matcher.end()); + + } + + public String apply(ParsedShader shader) { + + return header + + shader.src + + processBody(shader); + } + + public String processBody(ParsedShader shader) { + String s = body; + + for (String name : required) { + TaggedStruct struct = shader.getTag(name); + + s = s.replace(name, struct.name); + } + + s = fillPrefixes(shader, s); + s = fillAssigns(shader, s); + + return s; + } + + private String fillPrefixes(ParsedShader shader, String s) { + Matcher prefixMatches = prefixer.matcher(s); + + StringBuffer out = new StringBuffer(); + while (prefixMatches.find()) { + String structName = prefixMatches.group(1); + String prefix = prefixMatches.group(2); + + TaggedStruct struct = shader.getStruct(structName); + + StringBuilder builder = new StringBuilder(); + for (String field : struct.fields.keySet()) { + builder.append(prefix); + builder.append(field); + builder.append(";\n"); + } + + prefixMatches.appendReplacement(out, builder.toString()); + } + prefixMatches.appendTail(out); + return out.toString(); + } + + private String fillAssigns(ParsedShader shader, String s) { + Matcher assignMatches = assigner.matcher(s); + + StringBuffer out = new StringBuffer(); + while (assignMatches.find()) { + String structName = assignMatches.group(1); + String lhs = assignMatches.group(2); + String rhs = assignMatches.group(3); + + TaggedStruct struct = shader.getStruct(structName); + + StringBuilder builder = new StringBuilder(); + for (String field : struct.fields.keySet()) { + builder.append(lhs); + builder.append(field); + builder.append(" = "); + builder.append(rhs); + builder.append(field); + builder.append(";\n"); + } + + assignMatches.appendReplacement(out, builder.toString()); + } + assignMatches.appendTail(out); + return out.toString(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/ParsedShader.java b/src/main/java/com/jozufozu/flywheel/backend/loading/ParsedShader.java new file mode 100644 index 000000000..aa76253a1 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/loading/ParsedShader.java @@ -0,0 +1,42 @@ +package com.jozufozu.flywheel.backend.loading; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ParsedShader { + private static final Pattern decorator = Pattern.compile("#\\[([\\w_]*)]"); + private static final Pattern taggedStruct = Pattern.compile("#\\[([\\w_]*)]\\s*struct\\s+([\\w\\d_]*)\\s*\\{(\\s*(?:.*;\\s*\\n)+\\s*)}\\s*;"); + + final String src; + + final Map tag2Struct = new HashMap<>(); + final Map name2Struct = new HashMap<>(); + + public ParsedShader(String src) { + + Matcher structs = taggedStruct.matcher(src); + + StringBuffer strippedSrc = new StringBuffer(); + while (structs.find()) { + TaggedStruct struct = new TaggedStruct(structs); + + structs.appendReplacement(strippedSrc, decorator.matcher(struct.source).replaceFirst("")); + + tag2Struct.put(struct.tag, struct); + name2Struct.put(struct.name, struct); + } + structs.appendTail(strippedSrc); + + this.src = strippedSrc.toString(); + } + + public TaggedStruct getTag(String tag) { + return tag2Struct.get(tag); + } + + public TaggedStruct getStruct(String name) { + return name2Struct.get(name); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/loading/TaggedStruct.java b/src/main/java/com/jozufozu/flywheel/backend/loading/TaggedStruct.java new file mode 100644 index 000000000..4249047c9 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/loading/TaggedStruct.java @@ -0,0 +1,36 @@ +package com.jozufozu.flywheel.backend.loading; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TaggedStruct { + + public static final Pattern fieldPattern = Pattern.compile("(\\S+)\\s*(\\S+);"); + + int srcStart, srcEnd; + String source; + String tag; + String name; + String body; + + Map fields = new HashMap<>(); + + public TaggedStruct(Matcher foundMatcher) { + this.source = foundMatcher.group(); + + srcStart = foundMatcher.start(); + srcEnd = foundMatcher.end(); + + tag = foundMatcher.group(1); + name = foundMatcher.group(2); + body = foundMatcher.group(3); + + Matcher fielder = fieldPattern.matcher(body); + + while (fielder.find()) { + fields.put(fielder.group(2), fielder.group(1)); + } + } +} diff --git a/src/main/resources/assets/create/flywheel/shaders/block_new.frag b/src/main/resources/assets/create/flywheel/shaders/block_new.frag new file mode 100644 index 000000000..11ed8ac31 --- /dev/null +++ b/src/main/resources/assets/create/flywheel/shaders/block_new.frag @@ -0,0 +1,19 @@ +#version 110 + +#flwbuiltins + +#[FLWFragment] +struct Raster { + vec2 texCoords; + vec4 color; + float diffuse; + vec2 light; +}; + +void FLWMain(Raster r) { + vec4 tex = FLWBlockTexture(r.texCoords); + + vec4 color = vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color; + + FLWFinalizeColor(color); +} diff --git a/src/main/resources/assets/create/flywheel/shaders/model_new.vert b/src/main/resources/assets/create/flywheel/shaders/model_new.vert new file mode 100644 index 000000000..29df494d8 --- /dev/null +++ b/src/main/resources/assets/create/flywheel/shaders/model_new.vert @@ -0,0 +1,45 @@ +#flwbuiltins +#flwinclude <"create:core/matutils.glsl"> +#flwinclude <"create:core/diffuse.glsl"> + +#[FLWVertexData] +struct Vertex { + vec3 pos; + vec3 normal; + vec2 texCoords; +}; + +#[FLWInstanceData] +struct Instance { + vec2 light; + vec4 color; + mat4 transform; + mat3 normalMat; +}; + +#[FLWFragment] +struct Raster { + vec2 texCoords; + vec4 color; + float diffuse; + vec2 light; +}; + +Raster FLWMain(Vertex v, Instance i) { + vec4 worldPos = i.transform * vec4(v.pos, 1.); + + vec3 norm = i.normalMat * v.normal; + + FLWFinalizeWorldPos(worldPos); + FLWFinalizeNormal(norm); + + norm = normalize(norm); + + Raster r; + r.diffuse = diffuse(norm); + r.texCoords = v.texCoords; + r.light = i.light; + r.color = i.color; + + return r; +} diff --git a/src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.frag b/src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.frag new file mode 100644 index 000000000..fde45d306 --- /dev/null +++ b/src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.frag @@ -0,0 +1,12 @@ +#version 110 + +#flwbeginbody + +#FLWPrefixFields(FLWFragment, varying __v2f_) + +void main() { + FLWFragment f; + #FLWAssignFields(FLWFragment, f., __v2f_) + + FLWMain(f); +} diff --git a/src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.vert b/src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.vert index abb8d8487..b364babd7 100644 --- a/src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.vert +++ b/src/main/resources/assets/create/flywheel/shaders/skeleton/instanced/instanced.vert @@ -1,17 +1,19 @@ #version 110 + +#flwbeginbody #FLWPrefixFields(FLWVertexData, attribute __a_) #FLWPrefixFields(FLWInstanceData, attribute __a_) -#FLWPrefixFields(FLWOut, varying __v2f_) +#FLWPrefixFields(FLWFragment, varying __v2f_) void main() { FLWVertexData v; - #FLWAssignToFields(FLWVertexData, v, a_) + #FLWAssignFields(FLWVertexData, v., __a_) FLWInstanceData i; - #FLWAssignToFields(FLWInstanceData, i, a_) + #FLWAssignFields(FLWInstanceData, i., __a_) - FLWOut o = FLWMain(v, i); + FLWFragment o = FLWMain(v, i); - #FLWAssignFromFields(FLWOut, o, v2f_) + #FLWAssignFields(FLWFragment, __v2f_, o.) }