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.
This commit is contained in:
JozsefA 2021-05-11 18:24:12 -07:00
parent 2c4d650639
commit c1b7a1c19d
8 changed files with 282 additions and 10 deletions

View file

@ -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,6 +144,13 @@ public class ShaderLoader {
source = defines.process(source);
if (debugDumpFile) {
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")) {
@ -141,9 +158,6 @@ public class ShaderLoader {
}
}
return new GlShader(type, name, source);
}
private String processIncludes(String source, ResourceLocation baseName) {
HashSet<ResourceLocation> seen = new HashSet<>();
seen.add(baseName);

View file

@ -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();
}
}

View file

@ -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<String, TaggedStruct> tag2Struct = new HashMap<>();
final Map<String, TaggedStruct> 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);
}
}

View file

@ -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<String, String> 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));
}
}
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -0,0 +1,12 @@
#version 110
#flwbeginbody
#FLWPrefixFields(FLWFragment, varying __v2f_)
void main() {
FLWFragment f;
#FLWAssignFields(FLWFragment, f., __v2f_)
FLWMain(f);
}

View file

@ -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.)
}