diff --git a/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java b/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java
index 66a9352c1..1c6c2c7d5 100644
--- a/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java
+++ b/src/main/java/com/jozufozu/flywheel/core/compile/VertexCompiler.java
@@ -1,5 +1,7 @@
package com.jozufozu.flywheel.core.compile;
+import java.util.ArrayList;
+
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
@@ -8,9 +10,12 @@ import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileIndex;
import com.jozufozu.flywheel.core.source.FileResolution;
+import com.jozufozu.flywheel.core.source.ShaderField;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
import com.jozufozu.flywheel.core.source.parse.StructField;
+import com.jozufozu.flywheel.core.source.span.Span;
+import com.jozufozu.flywheel.util.Pair;
/**
* Handles compilation and deletion of vertex shaders.
@@ -43,8 +48,21 @@ public class VertexCompiler extends Memoizer {
// INSTANCE
+ int attributeBaseIndex = key.vertexType.getLayout()
+ .getAttributeCount();
+
var instanceShader = key.instanceShader;
- instanceShader.generateFinalSource(index, finalSource);
+ var replacements = new ArrayList>();
+ for (ShaderField field : instanceShader.fields.values()) {
+ if (field.decoration != ShaderField.Decoration.IN) {
+ continue;
+ }
+
+ int location = Integer.parseInt(field.location.get());
+ int newLocation = location + attributeBaseIndex;
+ replacements.add(Pair.of(field.location, Integer.toString(newLocation)));
+ }
+ instanceShader.generateFinalSource(index, finalSource, replacements);
// MATERIAL
@@ -58,11 +76,17 @@ public class VertexCompiler extends Memoizer {
// MAIN
- var instanceStruct = instanceShader.findFunction("flw_instanceVertex")
- .flatMap(f -> f.getParameterType(0)
- .findStruct())
- .orElseThrow();
- finalSource.append(generateFooter(key.vertexType, instanceStruct));
+ finalSource.append("""
+ void main() {
+ flw_layoutVertex();
+
+ flw_instanceVertex();
+
+ flw_materialVertex();
+
+ flw_contextVertex();
+ }
+ """);
try {
return new GlShader(finalSource.toString(), ShaderType.VERTEX, ImmutableList.of(layoutShader.name, instanceShader.name, materialShader.name, contextShaderSource.name), shaderConstants);
@@ -71,65 +95,6 @@ public class VertexCompiler extends Memoizer {
}
}
- protected String generateFooter(VertexType vertexType, ShaderStruct instance) {
- ImmutableList fields = instance.getFields();
-
- int attributeBinding = vertexType.getLayout()
- .getAttributeCount();
-
- StringBuilder footer = new StringBuilder();
-
- for (StructField field : fields) {
- footer.append("layout(location = ")
- .append(attributeBinding)
- .append(") in")
- .append(' ')
- .append(field.type)
- .append(' ')
- .append("_flw_a_i_")
- .append(field.name)
- .append(";\n");
- attributeBinding += CompileUtil.getAttributeCount(field.type);
- }
- footer.append('\n');
-
- footer.append(String.format("""
- void main() {
- flw_layoutVertex();
-
- %s instance;
- %s
- flw_instanceVertex(instance);
-
- flw_materialVertex();
-
- flw_contextVertex();
- }
- """,
- instance.name,
- assignFields(instance, "instance.", "_flw_a_i_")
- ));
-
- return footer.toString();
- }
-
- protected static StringBuilder assignFields(ShaderStruct struct, String prefix1, String prefix2) {
- ImmutableList fields = struct.getFields();
-
- StringBuilder builder = new StringBuilder();
-
- for (StructField field : fields) {
- builder.append(prefix1)
- .append(field.name)
- .append(" = ")
- .append(prefix2)
- .append(field.name)
- .append(";\n");
- }
-
- return builder;
- }
-
@Override
protected void _destroy(GlShader value) {
value.delete();
diff --git a/src/main/java/com/jozufozu/flywheel/core/source/ShaderField.java b/src/main/java/com/jozufozu/flywheel/core/source/ShaderField.java
new file mode 100644
index 000000000..9c61684ac
--- /dev/null
+++ b/src/main/java/com/jozufozu/flywheel/core/source/ShaderField.java
@@ -0,0 +1,43 @@
+package com.jozufozu.flywheel.core.source;
+
+import java.util.regex.Pattern;
+
+import org.jetbrains.annotations.Nullable;
+
+import com.jozufozu.flywheel.core.source.parse.AbstractShaderElement;
+import com.jozufozu.flywheel.core.source.span.Span;
+
+public class ShaderField extends AbstractShaderElement {
+ public static final Pattern PATTERN = Pattern.compile("layout\\s+\\(location\\s+=\\s+(\\d+)\\)\\s+(in|out)\\s+([\\w\\d]+)\\s+" + "([\\w\\d]+)");
+
+ public final Span location;
+ public final @Nullable Decoration decoration;
+ public final Span type;
+ public final Span name;
+
+ public ShaderField(Span self, Span location, Span inOut, Span type, Span name) {
+ super(self);
+
+ this.location = location;
+ this.decoration = Decoration.fromSpan(inOut);
+ this.type = type;
+ this.name = name;
+ }
+
+ public enum Decoration {
+ IN,
+ OUT,
+ FLAT,
+ ;
+
+ @Nullable
+ public static Decoration fromSpan(Span span) {
+ return switch (span.toString()) {
+ case "in" -> IN;
+ case "out" -> OUT;
+ case "flat" -> FLAT;
+ default -> null;
+ };
+ }
+ }
+}
diff --git a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java
index 7332ae7cf..72b05f946 100644
--- a/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java
+++ b/src/main/java/com/jozufozu/flywheel/core/source/SourceFile.java
@@ -1,6 +1,8 @@
package com.jozufozu.flywheel.core.source;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -18,6 +20,7 @@ import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
import com.jozufozu.flywheel.core.source.span.ErrorSpan;
import com.jozufozu.flywheel.core.source.span.Span;
import com.jozufozu.flywheel.core.source.span.StringSpan;
+import com.jozufozu.flywheel.util.Pair;
import net.minecraft.resources.ResourceLocation;
@@ -34,10 +37,6 @@ public class SourceFile {
public final ShaderSources parent;
public final String source;
- /**
- * Sections of the source that must be trimmed for compilation. Currently, it only contains the spans of imports.
- */
- public final String elided;
public final SourceLines lines;
@@ -55,6 +54,8 @@ public class SourceFile {
* Includes ordered as defined in the source.
*/
public final ImmutableList imports;
+ public final ImmutableMap fields;
+ private final List elisions;
public SourceFile(ErrorReporter errorReporter, ShaderSources parent, ResourceLocation name, String source) {
this.parent = parent;
@@ -63,13 +64,12 @@ public class SourceFile {
this.lines = new SourceLines(source);
- List elisions = new ArrayList<>();
+ this.elisions = new ArrayList<>();
- this.imports = parseImports(errorReporter, elisions);
+ this.imports = parseImports(errorReporter);
this.functions = parseFunctions();
this.structs = parseStructs();
-
- this.elided = elideSource(source, elisions).toString();
+ this.fields = parseFields();
}
public Span getLineSpan(int line) {
@@ -136,11 +136,15 @@ public class SourceFile {
}
public void generateFinalSource(FileIndex env, StringBuilder source) {
+ generateFinalSource(env, source, Collections.emptyList());
+ }
+
+ public void generateFinalSource(FileIndex env, StringBuilder source, List> replacements) {
for (Import include : imports) {
SourceFile file = include.getFile();
if (file != null && !env.exists(file)) {
- file.generateFinalSource(env, source);
+ file.generateFinalSource(env, source, replacements);
}
}
@@ -151,7 +155,13 @@ public class SourceFile {
.append(" // ")
.append(name)
.append('\n');
- source.append(elided);
+
+ var replacementsAndElisions = new ArrayList<>(replacements);
+ for (Span elision : elisions) {
+ replacementsAndElisions.add(Pair.of(elision, ""));
+ }
+
+ source.append(this.replace(replacementsAndElisions));
source.append('\n');
}
@@ -159,18 +169,27 @@ public class SourceFile {
return "Source for shader '" + name + "':\n" + lines.printLinesWithNumbers();
}
- private static CharSequence elideSource(String source, List elisions) {
+ private CharSequence replace(List> replacements) {
StringBuilder out = new StringBuilder();
int lastEnd = 0;
- for (Span elision : elisions) {
- out.append(source, lastEnd, elision.getStartPos());
+ replacements.sort(Comparator.comparing(Pair::first));
- lastEnd = elision.getEndPos();
+ for (var replacement : replacements) {
+ var loc = replacement.first();
+
+ if (loc.getSourceFile() != this) {
+ continue;
+ }
+
+ out.append(this.source, lastEnd, loc.getStartPos());
+ out.append(replacement.second());
+
+ lastEnd = loc.getEndPos();
}
- out.append(source, lastEnd, source.length());
+ out.append(this.source, lastEnd, this.source.length());
return out;
}
@@ -179,7 +198,7 @@ public class SourceFile {
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
*/
private ImmutableMap parseFunctions() {
- Matcher matcher = ShaderFunction.functionDeclaration.matcher(source);
+ Matcher matcher = ShaderFunction.PATTERN.matcher(source);
Map functions = new HashMap<>();
@@ -229,12 +248,31 @@ public class SourceFile {
return structs.build();
}
+ /**
+ * Scan the source for function definitions and "parse" them into objects that contain properties of the function.
+ */
+ private ImmutableMap parseFields() {
+ Matcher matcher = ShaderField.PATTERN.matcher(source);
+
+ ImmutableMap.Builder fields = ImmutableMap.builder();
+ while (matcher.find()) {
+ Span self = Span.fromMatcher(this, matcher);
+ Span location = Span.fromMatcher(this, matcher, 1);
+ Span decoration = Span.fromMatcher(this, matcher, 2);
+ Span type = Span.fromMatcher(this, matcher, 3);
+ Span name = Span.fromMatcher(this, matcher, 4);
+
+ fields.put(location.get(), new ShaderField(self, location, decoration, type, name));
+ }
+
+ return fields.build();
+ }
+
/**
* Scan the source for {@code #use "..."} directives.
* Records the contents of the directive into an {@link Import} object, and marks the directive for elision.
- * @param elisions
*/
- private ImmutableList parseImports(ErrorReporter errorReporter, List elisions) {
+ private ImmutableList parseImports(ErrorReporter errorReporter) {
Matcher uses = Import.PATTERN.matcher(source);
Set importedFiles = new HashSet<>();
@@ -253,7 +291,7 @@ public class SourceFile {
}
}
- elisions.add(use); // we have to trim that later
+ this.elisions.add(use); // we have to trim that later
}
return ImmutableList.copyOf(imports);
diff --git a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java
index 134d0214f..38dda848c 100644
--- a/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java
+++ b/src/main/java/com/jozufozu/flywheel/core/source/parse/ShaderFunction.java
@@ -10,7 +10,7 @@ import com.jozufozu.flywheel.core.source.span.Span;
public class ShaderFunction extends AbstractShaderElement {
// https://regexr.com/60n3d
- public static final Pattern functionDeclaration = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
+ public static final Pattern PATTERN = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
public static final Pattern argument = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)");
public static final Pattern assignment = Pattern.compile("(\\w+)\\s*=");
diff --git a/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java b/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java
index 81b8e848a..7491f0cf4 100644
--- a/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java
+++ b/src/main/java/com/jozufozu/flywheel/core/source/span/Span.java
@@ -3,6 +3,8 @@ package com.jozufozu.flywheel.core.source.span;
import java.util.Optional;
import java.util.regex.Matcher;
+import org.jetbrains.annotations.NotNull;
+
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
@@ -14,7 +16,7 @@ import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
* Spans are used for pretty-printing errors.
*
*/
-public abstract class Span implements CharSequence {
+public abstract class Span implements CharSequence, Comparable {
protected final SourceFile in;
protected final CharPos start;
@@ -138,4 +140,9 @@ public abstract class Span implements CharSequence {
}
return in.findFunction(this);
}
+
+ @Override
+ public int compareTo(@NotNull Span o) {
+ return Integer.compareUnsigned(getStartPos(), o.getStartPos());
+ }
}
diff --git a/src/main/java/com/jozufozu/flywheel/core/structs/InstanceShaders.java b/src/main/java/com/jozufozu/flywheel/core/structs/InstanceShaders.java
index 0b253cfeb..f86faaee2 100644
--- a/src/main/java/com/jozufozu/flywheel/core/structs/InstanceShaders.java
+++ b/src/main/java/com/jozufozu/flywheel/core/structs/InstanceShaders.java
@@ -12,7 +12,7 @@ import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class InstanceShaders {
- public static final BiConsumer CHECK = SourceChecks.checkFunctionParameterTypeExists("flw_instanceVertex", 1, 0);
+ public static final BiConsumer CHECK = SourceChecks.checkFunctionArity("flw_instanceVertex", 0);
public static final FileResolution TRANSFORMED = create(ResourceUtil.subPath(Names.TRANSFORMED, ".vert"));
public static final FileResolution ORIENTED = create(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
diff --git a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert
index c9e5b5cff..dc49c75ca 100644
--- a/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert
+++ b/src/main/resources/assets/flywheel/flywheel/instance/oriented.vert
@@ -2,17 +2,15 @@
#use "flywheel:util/light.glsl"
#use "flywheel:util/quaternion.glsl"
-struct Oriented {
- vec2 light;
- vec4 color;
- vec3 pos;
- vec3 pivot;
- vec4 rotation;
-};
+layout (location = 0) in vec2 oriented_light;
+layout (location = 1) in vec4 oriented_color;
+layout (location = 2) in vec3 oriented_pos;
+layout (location = 3) in vec3 oriented_pivot;
+layout (location = 4) in vec4 oriented_rotation;
-void flw_instanceVertex(Oriented oriented) {
- flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented.pivot, oriented.rotation) + oriented.pivot + oriented.pos, 1.0);
- flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, oriented.rotation);
- flw_vertexColor = oriented.color;
- flw_vertexLight = shiftLight(oriented.light);
+void flw_instanceVertex() {
+ flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented_pivot, oriented_rotation) + oriented_pivot + oriented_pos, 1.0);
+ flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, oriented_rotation);
+ flw_vertexColor = oriented_color;
+ flw_vertexLight = shiftLight(oriented_light);
}
diff --git a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert
index cea24a54a..d5fe6fc62 100644
--- a/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert
+++ b/src/main/resources/assets/flywheel/flywheel/instance/transformed.vert
@@ -1,16 +1,14 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/light.glsl"
-struct Instance {
- vec2 light;
- vec4 color;
- mat4 transform;
- mat3 normalMat;
-};
+layout (location = 0) in vec2 transformed_light;
+layout (location = 1) in vec4 transformed_color;
+layout (location = 2) in mat4 transformed_pose;
+layout (location = 6) in mat3 transformed_normal;
-void flw_instanceVertex(Instance instance) {
- flw_vertexPos = instance.transform * flw_vertexPos;
- flw_vertexNormal = instance.normalMat * flw_vertexNormal;
- flw_vertexColor = instance.color;
- flw_vertexLight = shiftLight(instance.light);
+void flw_instanceVertex() {
+ flw_vertexPos = transformed_pose * flw_vertexPos;
+ flw_vertexNormal = transformed_normal * flw_vertexNormal;
+ flw_vertexColor = transformed_color;
+ flw_vertexLight = shiftLight(transformed_light);
}