diff --git a/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java index 438504c0d..5473de7ad 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java +++ b/src/main/java/com/jozufozu/flywheel/backend/gl/GLSLVersion.java @@ -1,8 +1,12 @@ package com.jozufozu.flywheel.backend.gl; public enum GLSLVersion { + @Deprecated V110(110), + @Deprecated V120(120), + V150(150), + V330(330), ; public final int version; diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java index 24e743fc3..c033b7b8c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingProgramMetaData.java @@ -29,14 +29,14 @@ public class InstancingProgramMetaData { Optional vertexFunc = file.findFunction("vertex"); Optional fragmentFunc = file.findFunction("fragment"); - if (!fragmentFunc.isPresent()) { + if (fragmentFunc.isEmpty()) { ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); } - if (!vertexFunc.isPresent()) { + if (vertexFunc.isEmpty()) { ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); } - if (!fragmentFunc.isPresent() || !vertexFunc.isPresent()) { + if (fragmentFunc.isEmpty() || vertexFunc.isEmpty()) { throw new ShaderLoadingException(); } @@ -64,19 +64,19 @@ public class InstancingProgramMetaData { Optional maybeVertex = file.findStruct(vertexName); Optional maybeInstance = file.findStruct(instanceName); - if (!maybeVertex.isPresent()) { + if (maybeVertex.isEmpty()) { ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined"); } - if (!maybeInterpolant.isPresent()) { + if (maybeInterpolant.isEmpty()) { ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); } - if (!maybeInstance.isPresent()) { + if (maybeInstance.isEmpty()) { ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined"); } - if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent() || !maybeInstance.isPresent()) { + if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty() || maybeInstance.isEmpty()) { throw new ShaderLoadingException(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java index 33fc589f3..fdb4e4381 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/InstancingTemplate.java @@ -37,9 +37,9 @@ public class InstancingTemplate extends Template { public void vertexFooter(StringBuilder template, SourceFile file) { InstancingProgramMetaData data = getMetadata(file); - Template.prefixFields(template, data.vertex, "attribute", "a_v_"); - Template.prefixFields(template, data.instance, "attribute", "a_i_"); - Template.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.vertex, "in", "a_v_"); + Template.prefixFields(template, data.instance, "in", "a_i_"); + Template.prefixFields(template, data.interpolant, "out", "v2f_"); template.append("void main() {\n"); template.append(data.vertexName) @@ -63,7 +63,7 @@ public class InstancingTemplate extends Template { public void fragmentFooter(StringBuilder template, SourceFile file) { InstancingProgramMetaData data = getMetadata(file); - Template.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.interpolant, "in", "v2f_"); template.append("void main() {\n"); template.append(data.interpolantName) diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java index d353be2b0..67562a68b 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotProgramMetaData.java @@ -26,15 +26,15 @@ public class OneShotProgramMetaData { Optional maybeVertexMain = file.findFunction("vertex"); Optional maybeFragmentMain = file.findFunction("fragment"); - if (!maybeVertexMain.isPresent()) { + if (maybeVertexMain.isEmpty()) { ErrorReporter.generateFileError(file, "could not find \"vertex\" function"); } - if (!maybeFragmentMain.isPresent()) { + if (maybeFragmentMain.isEmpty()) { ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined"); } - if (!maybeVertexMain.isPresent() || !maybeFragmentMain.isPresent()) { + if (maybeVertexMain.isEmpty() || maybeFragmentMain.isEmpty()) { throw new RuntimeException(); } @@ -62,13 +62,13 @@ public class OneShotProgramMetaData { Optional maybeInterpolant = file.findStruct(interpolantName); Optional maybeVertex = file.findStruct(vertexName); - if (!maybeVertex.isPresent()) + if (maybeVertex.isEmpty()) ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined"); - if (!maybeInterpolant.isPresent()) + if (maybeInterpolant.isEmpty()) ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined"); - if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent()) { + if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty()) { throw new RuntimeException(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java index 82f001cb3..f457ebfc4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/OneShotTemplate.java @@ -32,8 +32,8 @@ public class OneShotTemplate extends Template { public void vertexFooter(StringBuilder template, SourceFile file) { OneShotProgramMetaData data = getMetadata(file); - Template.prefixFields(template, data.vertex, "attribute", "a_v_"); - Template.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.vertex, "in", "a_v_"); + Template.prefixFields(template, data.interpolant, "out", "v2f_"); template.append("void main() {\n"); template.append(data.vertexName) @@ -53,7 +53,7 @@ public class OneShotTemplate extends Template { public void fragmentFooter(StringBuilder template, SourceFile file) { OneShotProgramMetaData data = getMetadata(file); - Template.prefixFields(template, data.interpolant, "varying", "v2f_"); + Template.prefixFields(template, data.interpolant, "in", "v2f_"); template.append("void main() {\n"); template.append(data.interpolant.name) diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java index fd65afa86..8b5b621e8 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/Template.java @@ -25,10 +25,10 @@ public abstract class Template { private final Map metadata = new HashMap<>(); - private final Function parser; + private final Function reader; - protected Template(Function parser) { - this.parser = parser; + protected Template(Function reader) { + this.reader = reader; } /** @@ -46,11 +46,12 @@ public abstract class Template { public abstract Collection getShaderInputs(SourceFile file); public D getMetadata(SourceFile file) { - return metadata.computeIfAbsent(file, parser); + // lazily read files, cache results + return metadata.computeIfAbsent(file, reader); } public GLSLVersion getVersion() { - return GLSLVersion.V120; + return GLSLVersion.V150; } public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) { diff --git a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java index c71117e34..2cb7defbb 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java +++ b/src/main/java/com/jozufozu/flywheel/backend/pipeline/WorldShader.java @@ -57,7 +57,7 @@ public class WorldShader { .append(template.getVersion()) .append('\n') .append("#define ") - .append(type.define) + .append(type.define) // special case shader type declaration .append('\n') .append(defines != null ? defines : "") .append(header) diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java b/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java index b04bb87c5..ba4e5f343 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/FileResolution.java @@ -67,11 +67,10 @@ public class FileResolution { try { file = sources.findSource(fileLoc); } catch (RuntimeException error) { - ErrorBuilder builder = new ErrorBuilder(); - builder.error(String.format("could not find source for file %s", fileLoc)); + ErrorBuilder builder = ErrorBuilder.error(String.format("could not find source for file %s", fileLoc)); // print the location of all places where this file was referenced for (Span span : foundSpans) { - builder.in(span.getSourceFile()) + builder.pointAtFile(span.getSourceFile()) .pointAt(span, 2); } Backend.log.error(builder.build()); diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/Index.java b/src/main/java/com/jozufozu/flywheel/backend/source/Index.java index be47757c2..4ebc6540c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/Index.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/Index.java @@ -27,8 +27,8 @@ public class Index { Collection files = sources.values(); for (SourceFile file : files) { - file.getStructs().forEach(knownStructs::put); - file.getFunctions().forEach(knownFunctions::put); + file.structs.forEach(knownStructs::put); + file.functions.forEach(knownFunctions::put); } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java b/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java index dc2d7f818..ab055553d 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/SourceFile.java @@ -13,80 +13,65 @@ import com.google.common.collect.ImmutableMap; import com.jozufozu.flywheel.backend.source.parse.Import; import com.jozufozu.flywheel.backend.source.parse.ShaderFunction; import com.jozufozu.flywheel.backend.source.parse.ShaderStruct; -import com.jozufozu.flywheel.backend.source.span.CharPos; import com.jozufozu.flywheel.backend.source.span.ErrorSpan; import com.jozufozu.flywheel.backend.source.span.Span; import com.jozufozu.flywheel.backend.source.span.StringSpan; -import com.jozufozu.flywheel.util.StringUtil; -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.resources.ResourceLocation; +/** + * Immutable class representing a shader file. + * + *

+ * This class parses shader files and generates what is effectively a high level AST of the source. + *

+ */ public class SourceFile { private static final Pattern includePattern = Pattern.compile("#use \"(.*)\""); - private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)"); - // https://regexr.com/60n3d public static final Pattern functionDeclaration = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{"); public final ResourceLocation name; public final ShaderSources parent; - private final String source; - private final CharSequence elided; - private final ImmutableList lines; + 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; - private final IntList lineStarts; + public final SourceLines lines; - // Function name -> Function object - private final ImmutableMap functions; - private final ImmutableMap structs; + /** + * Function lookup by name. + */ + public final ImmutableMap functions; - // Includes ordered as defined in the source - private final ImmutableList imports; + /** + * Struct lookup by name. + */ + public final ImmutableMap structs; - // Sections of the source that must be trimmed for compilation. - private final List elisions = new ArrayList<>(); + /** + * Includes ordered as defined in the source. + */ + public final ImmutableList imports; public SourceFile(ShaderSources parent, ResourceLocation name, String source) { this.parent = parent; this.name = name; this.source = source; - this.lineStarts = createLineStarts(); - this.lines = createLineList(lineStarts); + this.lines = new SourceLines(source); - this.imports = parseImports(); + List elisions = new ArrayList<>(); + + this.imports = parseImports(elisions); this.functions = parseFunctions(); this.structs = parseStructs(); - this.elided = createElidedSource(); - } - - public String getSource() { - return source; - } - - public CharSequence getElidedSource() { - return elided; - } - - public ShaderSources getParent() { - return parent; - } - - public ImmutableMap getFunctions() { - return functions; - } - - public ImmutableMap getStructs() { - return structs; - } - - public ImmutableList getIncludes() { - return imports; + this.elided = elideSource(source, elisions).toString(); } /** @@ -96,11 +81,11 @@ public class SourceFile { * @return null if no definition matches the name. */ public Optional findStruct(CharSequence name) { - ShaderStruct struct = getStructs().get(name.toString()); + ShaderStruct struct = structs.get(name.toString()); if (struct != null) return Optional.of(struct); - for (Import include : getIncludes()) { + for (Import include : imports) { Optional externalStruct = include.getOptional() .flatMap(file -> file.findStruct(name)); @@ -117,11 +102,11 @@ public class SourceFile { * @return Optional#empty() if no definition matches the name. */ public Optional findFunction(CharSequence name) { - ShaderFunction local = getFunctions().get(name.toString()); + ShaderFunction local = functions.get(name.toString()); if (local != null) return Optional.of(local); - for (Import include : getIncludes()) { + for (Import include : imports) { Optional external = include.getOptional() .flatMap(file -> file.findFunction(name)); @@ -142,29 +127,12 @@ public class SourceFile { } public void generateFinalSource(StringBuilder source) { - for (Import include : getIncludes()) { + for (Import include : imports) { SourceFile file = include.getFile(); if (file != null) file.generateFinalSource(source); } - source.append(getElidedSource()); - } - - public CharPos getCharPos(int charPos) { - int lineNo = 0; - for (; lineNo < lineStarts.size(); lineNo++) { - int ls = lineStarts.getInt(lineNo); - - if (charPos < ls) { - break; - } - } - - lineNo -= 1; - - int lineStart = lineStarts.getInt(lineNo); - - return new CharPos(charPos, lineNo, charPos - lineStart); + source.append(elided); } public String printSource() { @@ -172,18 +140,13 @@ public class SourceFile { builder.append("Source for shader '") .append(name) - .append("':\n"); - int i = 1; - for (String s : lines) { - builder.append(String.format("%1$4s: ", i++)) - .append(s) - .append('\n'); - } + .append("':\n") + .append(lines.printLinesWithNumbers()); return builder.toString(); } - private CharSequence createElidedSource() { + private static CharSequence elideSource(String source, List elisions) { StringBuilder out = new StringBuilder(); int lastEnd = 0; @@ -199,34 +162,6 @@ public class SourceFile { return out; } - /** - * Scan the source for line breaks, recording the position of the first character of each line. - */ - private IntList createLineStarts() { - IntList l = new IntArrayList(); - l.add(0); // first line is always at position 0 - - Matcher matcher = newLine.matcher(source); - - while (matcher.find()) { - l.add(matcher.end()); - } - return l; - } - - private ImmutableList createLineList(IntList lines) { - ImmutableList.Builder builder = ImmutableList.builder(); - - for (int i = 1; i < lines.size(); i++) { - int start = lines.getInt(i - 1); - int end = lines.getInt(i); - - builder.add(StringUtil.trimEnd(source.substring(start, end))); - } - - return builder.build(); - } - /** * Scan the source for function definitions and "parse" them into objects that contain properties of the function. */ @@ -284,8 +219,9 @@ public class SourceFile { /** * Scan the source for #use "..." directives. * Records the contents of the directive into an {@link Import} object, and marks the directive for elision. + * @param elisions */ - private ImmutableList parseImports() { + private ImmutableList parseImports(List elisions) { Matcher uses = includePattern.matcher(source); List imports = new ArrayList<>(); @@ -321,11 +257,4 @@ public class SourceFile { return -1; } - public int getLineCount() { - return lines.size(); - } - - public CharSequence getLine(int lineNo) { - return lines.get(lineNo); - } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/SourceLines.java b/src/main/java/com/jozufozu/flywheel/backend/source/SourceLines.java new file mode 100644 index 000000000..1c04bdee7 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/SourceLines.java @@ -0,0 +1,97 @@ +package com.jozufozu.flywheel.backend.source; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.google.common.collect.ImmutableList; +import com.jozufozu.flywheel.backend.source.span.CharPos; +import com.jozufozu.flywheel.util.StringUtil; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; + +public class SourceLines { + + private static final Pattern newLine = Pattern.compile("(\\r\\n|\\r|\\n)"); + + /** + * 0-indexed line to char pos mapping. + */ + private final IntList lineStarts; + + /** + * 0-indexed line lookup + */ + private final ImmutableList lines; + + public SourceLines(String source) { + this.lineStarts = createLineLookup(source); + this.lines = getLines(source, lineStarts); + } + + /** + * Scan the source for line breaks, recording the position of the first character of each line. + * @param source + */ + private static IntList createLineLookup(String source) { + IntList l = new IntArrayList(); + l.add(0); // first line is always at position 0 + + Matcher matcher = newLine.matcher(source); + + while (matcher.find()) { + l.add(matcher.end()); + } + return l; + } + + private static ImmutableList getLines(String source, IntList lines) { + ImmutableList.Builder builder = ImmutableList.builder(); + + for (int i = 1; i < lines.size(); i++) { + int start = lines.getInt(i - 1); + int end = lines.getInt(i); + + builder.add(StringUtil.trimEnd(source.substring(start, end))); + } + + return builder.build(); + } + + public CharPos getCharPos(int charPos) { + int lineNo = 0; + for (; lineNo < lineStarts.size(); lineNo++) { + int ls = lineStarts.getInt(lineNo); + + if (charPos < ls) { + break; + } + } + + lineNo -= 1; + + int lineStart = lineStarts.getInt(lineNo); + + return new CharPos(charPos, lineNo, charPos - lineStart); + } + + public int getLineCount() { + return lines.size(); + } + + public String getLine(int lineNo) { + return lines.get(lineNo); + } + + public String printLinesWithNumbers() { + StringBuilder builder = new StringBuilder(); + + for (int i = 0, linesSize = lines.size(); i < linesSize; i++) { + builder.append(String.format("%1$4s: ", i + 1)) + .append(lines.get(i)) + .append('\n'); + } + + return builder.toString(); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java index b6669e077..8a908f6dc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorBuilder.java @@ -1,95 +1,79 @@ package com.jozufozu.flywheel.backend.source.error; +import java.util.ArrayList; +import java.util.List; + import javax.annotation.Nullable; import com.jozufozu.flywheel.backend.source.SourceFile; +import com.jozufozu.flywheel.backend.source.SourceLines; +import com.jozufozu.flywheel.backend.source.error.lines.ErrorLine; +import com.jozufozu.flywheel.backend.source.error.lines.FileLine; +import com.jozufozu.flywheel.backend.source.error.lines.HeaderLine; +import com.jozufozu.flywheel.backend.source.error.lines.SourceLine; +import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine; import com.jozufozu.flywheel.backend.source.span.Span; import com.jozufozu.flywheel.util.FlwUtil; public class ErrorBuilder { - private final StringBuilder internal = new StringBuilder(); + private final List lines = new ArrayList<>(); - public ErrorBuilder error(CharSequence msg) { - internal.append("error: ") - .append(msg); - return endLine(); + private final Level level; + + public ErrorBuilder(Level level, CharSequence msg) { + this.level = level; + + lines.add(new HeaderLine(level.toString(), msg)); } - public ErrorBuilder note(CharSequence msg) { - internal.append("note: ") - .append(msg); - return endLine(); + public static ErrorBuilder error(CharSequence msg) { + return new ErrorBuilder(Level.ERROR, msg); } - public ErrorBuilder hint(CharSequence msg) { - internal.append("hint: ") - .append(msg); - return endLine(); + public static ErrorBuilder warn(CharSequence msg) { + return new ErrorBuilder(Level.WARN, msg); } - public ErrorBuilder in(SourceFile file) { - internal.append("--> ") - .append(file.name); - return endLine(); - } - - public ErrorBuilder numberedLine(int no, CharSequence content) { - return line(String.valueOf(no), content); - } - - public ErrorBuilder numberedLine(int no, int width, CharSequence content) { - String fmt = "%1$" + width + 's'; - return line(String.format(fmt, no), content); - } - - public ErrorBuilder line(CharSequence leftColumn, CharSequence rightColumn) { - - internal.append(leftColumn) - .append(" | ") - .append(rightColumn); - - return endLine(); - } - - public ErrorBuilder endLine() { - internal.append('\n'); + public ErrorBuilder pointAtFile(SourceFile file) { + lines.add(new FileLine(file.name.toString())); return this; } public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) { if (span == null) return this; - hint("add " + span.getSourceFile().importStatement() + " " + msg) - .in(span.getSourceFile()) - .pointAt(span, 1); + String builder = "add " + span.getSourceFile() + .importStatement() + ' ' + msg; - return this; + lines.add(new HeaderLine("hint", builder)); + + return this.pointAtFile(span.getSourceFile()) + .pointAt(span, 1); } public ErrorBuilder pointAt(Span span, int ctxLines) { - SourceFile file = span.getSourceFile(); - if (span.lines() == 1) { + SourceLines lines = span.getSourceFile().lines; int spanLine = span.firstLine(); int firstLine = Math.max(0, spanLine - ctxLines); - int lastLine = Math.min(file.getLineCount(), spanLine + ctxLines); + int lastLine = Math.min(lines.getLineCount(), spanLine + ctxLines); - int digits = FlwUtil.numDigits(lastLine); - - int firstCol = span.getStart().getCol(); - int lastCol = span.getEnd().getCol(); + int firstCol = span.getStart() + .col(); + int lastCol = span.getEnd() + .col(); for (int i = firstLine; i <= lastLine; i++) { - CharSequence line = file.getLine(i); + CharSequence line = lines.getLine(i); - numberedLine(i + 1, digits, line); + this.lines.add(SourceLine.numbered(i + 1, line.toString())); if (i == spanLine) { - line(FlwUtil.repeatChar(' ', digits), generateUnderline(firstCol, lastCol)); + this.lines.add(new SpanHighlightLine(firstCol, lastCol)); } } } @@ -98,19 +82,23 @@ public class ErrorBuilder { } public CharSequence build() { - return internal; - } - public CharSequence generateUnderline(int firstCol, int lastCol) { - StringBuilder line = new StringBuilder(lastCol); - for (int i = 0; i < firstCol; i++) { - line.append(' '); + int maxLength = -1; + for (ErrorLine line : lines) { + int length = line.neededMargin(); + + if (length > maxLength) maxLength = length; } - for (int i = firstCol; i < lastCol; i++) { - line.append('^'); + StringBuilder builder = new StringBuilder(); + for (ErrorLine line : lines) { + int length = line.neededMargin(); + + builder.append(FlwUtil.repeatChar(' ', maxLength - length)) + .append(line.build()) + .append('\n'); } - return line; + return builder; } } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java index f62686bdf..97232a6c4 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/ErrorReporter.java @@ -13,10 +13,8 @@ public class ErrorReporter { public static void generateSpanError(Span span, String message) { SourceFile file = span.getSourceFile(); - ErrorBuilder builder = new ErrorBuilder(); - - CharSequence error = builder.error(message) - .in(file) + CharSequence error = ErrorBuilder.error(message) + .pointAtFile(file) .pointAt(span, 2) .build(); @@ -25,10 +23,8 @@ public class ErrorReporter { public static void generateFileError(SourceFile file, String message) { - ErrorBuilder builder = new ErrorBuilder(); - - CharSequence error = builder.error(message) - .in(file) + CharSequence error = ErrorBuilder.error(message) + .pointAtFile(file) .build(); Backend.log.error(error); @@ -43,10 +39,9 @@ public class ErrorReporter { .stream() .findFirst() .map(ShaderStruct::getName); - ErrorBuilder builder = new ErrorBuilder(); - ErrorBuilder error = builder.error(msg) - .in(file) + ErrorBuilder error = ErrorBuilder.error(msg) + .pointAtFile(file) .pointAt(vertexName, 2) .hintIncludeFor(span.orElse(null), hint); @@ -62,10 +57,9 @@ public class ErrorReporter { .stream() .findFirst() .map(ShaderFunction::getName); - ErrorBuilder builder = new ErrorBuilder(); - ErrorBuilder error = builder.error(msg) - .in(file) + ErrorBuilder error = ErrorBuilder.error(msg) + .pointAtFile(file) .hintIncludeFor(span.orElse(null), hint); Backend.log.error(error.build()); diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/Level.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/Level.java new file mode 100644 index 000000000..ecf151fdd --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/Level.java @@ -0,0 +1,18 @@ +package com.jozufozu.flywheel.backend.source.error; + +public enum Level { + WARN("warn"), + ERROR("error"), + ; + + private final String error; + + Level(String error) { + this.error = error; + } + + @Override + public String toString() { + return error; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/Divider.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/Divider.java new file mode 100644 index 000000000..a24c21c4b --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/Divider.java @@ -0,0 +1,18 @@ +package com.jozufozu.flywheel.backend.source.error.lines; + +public enum Divider { + BAR(" | "), + ARROW("-->"), + ; + + private final String s; + + Divider(String s) { + this.s = s; + } + + @Override + public String toString() { + return s; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/ErrorLine.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/ErrorLine.java new file mode 100644 index 000000000..fe3f6ec57 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/ErrorLine.java @@ -0,0 +1,19 @@ +package com.jozufozu.flywheel.backend.source.error.lines; + +public interface ErrorLine { + + default int neededMargin() { + return left().length(); + } + + default Divider divider() { + return Divider.BAR; + } + + default String build() { + return left() + divider() + right(); + } + + String left(); + String right(); +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/FileLine.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/FileLine.java new file mode 100644 index 000000000..b39bf3494 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/FileLine.java @@ -0,0 +1,19 @@ +package com.jozufozu.flywheel.backend.source.error.lines; + +public record FileLine(String fileName) implements ErrorLine { + + @Override + public String left() { + return "--"; + } + + @Override + public Divider divider() { + return Divider.ARROW; + } + + @Override + public String right() { + return fileName; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/HeaderLine.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/HeaderLine.java new file mode 100644 index 000000000..85eab0fe4 --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/HeaderLine.java @@ -0,0 +1,26 @@ +package com.jozufozu.flywheel.backend.source.error.lines; + +import com.jozufozu.flywheel.backend.source.error.Level; + +public record HeaderLine(String level, CharSequence message) implements ErrorLine { + + @Override + public int neededMargin() { + return 0; + } + + @Override + public String build() { + return level + ": " + message; + } + + @Override + public String left() { + return null; + } + + @Override + public String right() { + return null; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SourceLine.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SourceLine.java new file mode 100644 index 000000000..496e411fc --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SourceLine.java @@ -0,0 +1,18 @@ +package com.jozufozu.flywheel.backend.source.error.lines; + +public record SourceLine(String number, String line) implements ErrorLine { + + public static SourceLine numbered(int number, String line) { + return new SourceLine(Integer.toString(number), line); + } + + @Override + public String left() { + return number; + } + + @Override + public String right() { + return line; + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SpanHighlightLine.java b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SpanHighlightLine.java new file mode 100644 index 000000000..379ab82ea --- /dev/null +++ b/src/main/java/com/jozufozu/flywheel/backend/source/error/lines/SpanHighlightLine.java @@ -0,0 +1,23 @@ +package com.jozufozu.flywheel.backend.source.error.lines; + +public class SpanHighlightLine implements ErrorLine { + private final String line; + + public SpanHighlightLine(int firstCol, int lastCol) { + line = generateUnderline(firstCol, lastCol); + } + + @Override + public String left() { + return ""; + } + + @Override + public String right() { + return line; + } + + public static String generateUnderline(int firstCol, int lastCol) { + return " ".repeat(Math.max(0, firstCol)) + "^".repeat(Math.max(0, lastCol - firstCol)); + } +} diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java index 0fd47d76d..d402a65fc 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/CharPos.java @@ -3,27 +3,5 @@ package com.jozufozu.flywheel.backend.source.span; /** * A position in a file. */ -public class CharPos { - - private final int idx; - private final int line; - private final int col; - - public CharPos(int idx, int line, int col) { - this.idx = idx; - this.line = line; - this.col = col; - } - - public int getPos() { - return idx; - } - - public int getLine() { - return line; - } - - public int getCol() { - return col; - } +public record CharPos(int pos, int line, int col) { } diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java index fdac16a4d..1b812fde9 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/Span.java @@ -5,7 +5,11 @@ import java.util.regex.Matcher; import com.jozufozu.flywheel.backend.source.SourceFile; /** - * A span of code in a {@link SourceFile}. + * A segment of code in a {@link SourceFile}. + * + *

+ * Spans are used for pretty-printing errors. + *

*/ public abstract class Span implements CharSequence { @@ -14,7 +18,7 @@ public abstract class Span implements CharSequence { protected final CharPos end; public Span(SourceFile in, int start, int end) { - this(in, in.getCharPos(start), in.getCharPos(end)); + this(in, in.lines.getCharPos(start), in.lines.getCharPos(end)); } public Span(SourceFile in, CharPos start, CharPos end) { @@ -42,14 +46,14 @@ public abstract class Span implements CharSequence { * @return the string index at the (inclusive) beginning of this code segment. */ public int getStartPos() { - return start.getPos(); + return start.pos(); } /** * @return the string index at the (exclusive) end of this code segment. */ public int getEndPos() { - return end.getPos(); + return end.pos(); } /** @@ -63,11 +67,11 @@ public abstract class Span implements CharSequence { * @return how many lines this span spans. */ public int lines() { - return end.getLine() - start.getLine() + 1; + return end.line() - start.line() + 1; } public int firstLine() { - return start.getLine(); + return start.line(); } /** @@ -89,7 +93,7 @@ public abstract class Span implements CharSequence { @Override public char charAt(int index) { - return in.getSource().charAt(start.getPos() + index); + return in.source.charAt(start.pos() + index); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java b/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java index 788a62a08..4712c6b74 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java +++ b/src/main/java/com/jozufozu/flywheel/backend/source/span/StringSpan.java @@ -14,13 +14,13 @@ public class StringSpan extends Span { @Override public Span subSpan(int from, int to) { - return new StringSpan(in, start.getPos() + from, start.getPos() + to); + return new StringSpan(in, start.pos() + from, start.pos() + to); } @Override public String get() { - return in.getSource() - .substring(start.getPos(), end.getPos()); + return in.source + .substring(start.pos(), end.pos()); } @Override diff --git a/src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java b/src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java index dd81e465c..69e40a08d 100644 --- a/src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java +++ b/src/main/java/com/jozufozu/flywheel/core/shader/spec/SpecificValueCondition.java @@ -9,8 +9,8 @@ import net.minecraft.resources.ResourceLocation; public class SpecificValueCondition implements IGameStateCondition { public static final Codec CODEC = RecordCodecBuilder.create(condition -> condition.group(IGameStateProvider.CODEC.fieldOf("provider") - .forGetter(SpecificValueCondition::getStateProvider), Codec.STRING.fieldOf("value") - .forGetter(SpecificValueCondition::getValue)) + .forGetter(SpecificValueCondition::getStateProvider), Codec.STRING.fieldOf("value") + .forGetter(SpecificValueCondition::getValue)) .apply(condition, SpecificValueCondition::new)); private final String required; diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl index 9d7de9986..1535dc2ed 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl +++ b/src/main/resources/assets/flywheel/flywheel/shaders/context/crumbling.glsl @@ -26,6 +26,8 @@ void FLWFinalizeWorldPos(inout vec4 worldPos) { #elif defined(FRAGMENT_SHADER) +out vec4 fragColor; + vec4 FLWBlockTexture(vec2 texCoords) { vec4 cr = texture2D(uCrumbling, texCoords * uTextureScale); float diffuseAlpha = texture2D(uBlockAtlas, texCoords).a; @@ -42,7 +44,11 @@ void FLWFinalizeColor(vec4 color) { color.a = a; #endif - gl_FragColor = color; + if (color.a < 0.1) { + discard; + } + + fragColor = color; } vec4 FLWLight(vec2 lightCoords) { diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/context/fog.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/context/fog.glsl index 4f0a66d4b..b6d086422 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/context/fog.glsl +++ b/src/main/resources/assets/flywheel/flywheel/shaders/context/fog.glsl @@ -1,4 +1,8 @@ -varying float FragDistance; +#if defined(VERTEX_SHADER) +out float FragDistance; +#elif defined(FRAGMENT_SHADER) +in float FragDistance; +#endif uniform vec4 uFogColor; uniform vec2 uFogRange; diff --git a/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl b/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl index beb4319f2..9da66a38c 100644 --- a/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl +++ b/src/main/resources/assets/flywheel/flywheel/shaders/context/world.glsl @@ -34,6 +34,8 @@ void FLWFinalizeWorldPos(inout vec4 worldPos) { //#endif //#endif +out vec4 fragColor; + vec4 FLWBlockTexture(vec2 texCoords) { return texture2D(uBlockAtlas, texCoords); } @@ -53,7 +55,7 @@ void FLWFinalizeColor(vec4 color) { } #endif - gl_FragColor = color; + fragColor = color; } vec4 FLWLight(vec2 lightCoords) {