mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-27 12:14:40 +01:00
Reload
- Redo shader loading - Now loads an immutable SourceFile containing some metadata - Replace legacy compilation pipeline with improved new one using new api - Builtins are defined in one file, now "header" - New ErrorReporter/ErrorBuilder methods - Fancier shader loading errors
This commit is contained in:
parent
9ea3344b6f
commit
392cfc9156
53 changed files with 766 additions and 1211 deletions
|
@ -6,7 +6,7 @@ import java.util.List;
|
|||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorBuilder;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
@ -40,7 +40,17 @@ public class FileResolution {
|
|||
try {
|
||||
file = sources.source(fileLoc);
|
||||
} catch (RuntimeException error) {
|
||||
ErrorReporter.generateSpanError(foundSpans.get(0), "could not find source");
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
builder.error(String.format("could not find source for file %s", fileLoc));
|
||||
for (Span span : foundSpans) {
|
||||
builder.in(span.getSourceFile())
|
||||
.pointAt(span, 2);
|
||||
}
|
||||
Backend.log.error(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
34
src/main/java/com/jozufozu/flywheel/backend/Index.java
Normal file
34
src/main/java/com/jozufozu/flywheel/backend/Index.java
Normal file
|
@ -0,0 +1,34 @@
|
|||
package com.jozufozu.flywheel.backend;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.MultimapBuilder;
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Indexes many shader source definitions to allow for error fix suggestions.
|
||||
*/
|
||||
public class Index {
|
||||
|
||||
private final Multimap<String, ShaderStruct> knownNames = MultimapBuilder.hashKeys().hashSetValues().build();
|
||||
|
||||
public Index(Map<ResourceLocation, SourceFile> sources) {
|
||||
Collection<SourceFile> files = sources.values();
|
||||
|
||||
for (SourceFile file : files) {
|
||||
file.getStructs().forEach(knownNames::put);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<ShaderStruct> getStructDefinitionsMatching(CharSequence name) {
|
||||
return knownNames.get(name.toString());
|
||||
}
|
||||
}
|
|
@ -1,23 +1,11 @@
|
|||
package com.jozufozu.flywheel.backend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.loading.Program;
|
||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ package com.jozufozu.flywheel.backend;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
|
@ -15,16 +13,11 @@ import com.google.common.collect.Lists;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
||||
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.pipeline.WorldShaderPipeline;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
|
||||
import com.jozufozu.flywheel.core.shader.extension.IProgramExtension;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
||||
import com.jozufozu.flywheel.util.StreamUtil;
|
||||
|
@ -49,7 +42,6 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
|
||||
private final Map<ResourceLocation, String> shaderSource = new HashMap<>();
|
||||
private final Map<ResourceLocation, SourceFile> shaderSources = new HashMap<>();
|
||||
|
||||
private final Map<ResourceLocation, FileResolution> resolutions = new HashMap<>();
|
||||
|
@ -57,6 +49,8 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
private boolean shouldCrash;
|
||||
private final Backend backend;
|
||||
|
||||
public Index index;
|
||||
|
||||
public ShaderSources(Backend backend) {
|
||||
this.backend = backend;
|
||||
IResourceManager manager = backend.minecraft.getResourceManager();
|
||||
|
@ -79,43 +73,24 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
return resolutions.computeIfAbsent(fileLoc, FileResolution::new);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Shader source(ResourceLocation name, ShaderType type) {
|
||||
return new Shader(this, type, name, getShaderSource(name));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void notifyError() {
|
||||
shouldCrash = true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public String getShaderSource(ResourceLocation loc) {
|
||||
String source = shaderSource.get(loc);
|
||||
|
||||
if (source == null) {
|
||||
throw new ShaderLoadingException(String.format("shader '%s' does not exist", loc));
|
||||
}
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
|
||||
if (predicate.test(VanillaResourceType.SHADERS)) {
|
||||
backend.refresh();
|
||||
|
||||
if (backend.gl20()) {
|
||||
shaderSource.clear();
|
||||
|
||||
shouldCrash = false;
|
||||
|
||||
backend.clearContexts();
|
||||
ModLoader.get()
|
||||
.postEvent(new GatherContextEvent(backend));
|
||||
|
||||
resolutions.clear();
|
||||
resolutions.values().forEach(FileResolution::invalidate);
|
||||
|
||||
loadProgramSpecs(manager);
|
||||
loadShaderSources(manager);
|
||||
|
@ -124,12 +99,6 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
resolution.resolve(this);
|
||||
}
|
||||
|
||||
WorldShaderPipeline<WorldProgram> pl = new WorldShaderPipeline<>(this, WorldProgram::new);
|
||||
|
||||
// ResourceLocation name = new ResourceLocation(Flywheel.ID, "model.glsl");
|
||||
// SourceFile source = source(name);
|
||||
// pl.compile(name, source, Collections.emptyList());
|
||||
|
||||
for (IShaderContext<?> context : backend.allContexts()) {
|
||||
context.load();
|
||||
}
|
||||
|
@ -140,9 +109,6 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
|
||||
Backend.log.info("Loaded all shader programs.");
|
||||
|
||||
// no need to hog all that memory
|
||||
shaderSource.clear();
|
||||
|
||||
ClientWorld world = Minecraft.getInstance().level;
|
||||
if (Backend.isFlywheelWorld(world)) {
|
||||
// TODO: looks like it might be good to have another event here
|
||||
|
@ -169,12 +135,13 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
|||
|
||||
ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR);
|
||||
|
||||
shaderSource.put(name, source);
|
||||
shaderSources.put(name, new SourceFile(this, name, source));
|
||||
} catch (IOException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
index = new Index(shaderSources);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import static org.lwjgl.opengl.GL20.glUseProgram;
|
|||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||
import com.jozufozu.flywheel.backend.loading.Program;
|
||||
import com.jozufozu.flywheel.util.RenderUtil;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
|
|
@ -5,7 +5,6 @@ import org.lwjgl.opengl.GL20;
|
|||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
|
|
|
@ -100,7 +100,15 @@ public class InstancedRenderRegistry {
|
|||
return skipRender.getBoolean(o);
|
||||
}
|
||||
|
||||
public class TileConfig<T extends TileEntity> {
|
||||
public interface Config<CONFIG extends Config<CONFIG, FACTORY>, FACTORY> {
|
||||
|
||||
CONFIG factory(FACTORY rendererFactory);
|
||||
|
||||
CONFIG setSkipRender(boolean skipRender);
|
||||
}
|
||||
|
||||
public class TileConfig<T extends TileEntity> implements Config<TileConfig<T>, ITileInstanceFactory<? super T>> {
|
||||
|
||||
|
||||
private final TileEntityType<T> type;
|
||||
|
||||
|
@ -112,14 +120,14 @@ public class InstancedRenderRegistry {
|
|||
tiles.put(type, rendererFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TileConfig<T> setSkipRender(boolean skipRender) {
|
||||
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public class EntityConfig<T extends Entity> {
|
||||
}
|
||||
public class EntityConfig<T extends Entity> implements Config<EntityConfig<T>, IEntityInstanceFactory<? super T>> {
|
||||
|
||||
|
||||
private final EntityType<T> type;
|
||||
|
||||
|
@ -131,12 +139,12 @@ public class InstancedRenderRegistry {
|
|||
entities.put(type, rendererFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EntityConfig<T> setSkipRender(boolean skipRender) {
|
||||
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface IProcessingStage {
|
||||
|
||||
void process(Shader shader);
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class InstancedArraysTemplate extends ProgramTemplate {
|
||||
|
||||
public static final String vertexData = "VertexData";
|
||||
public static final String instanceData = "InstanceData";
|
||||
public static final String fragment = "Fragment";
|
||||
|
||||
public static final String vertexPrefix = "a_v_";
|
||||
public static final String instancePrefix = "a_i_";
|
||||
|
||||
public static final String[] requiredVert = new String[]{instanceData, vertexData, fragment};
|
||||
|
||||
public static final String[] requiredFrag = {fragment};
|
||||
|
||||
public static final ResourceLocation vert = new ResourceLocation(Flywheel.ID, "template/instanced/instanced.vert");
|
||||
public static final ResourceLocation frag = new ResourceLocation(Flywheel.ID, "template/instanced/instanced.frag");
|
||||
|
||||
public InstancedArraysTemplate(ShaderSources loader) {
|
||||
super(loader);
|
||||
|
||||
templates.put(ShaderType.VERTEX, new ShaderTemplate(requiredVert, loader.getShaderSource(vert)));
|
||||
templates.put(ShaderType.FRAGMENT, new ShaderTemplate(requiredFrag, loader.getShaderSource(frag)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachAttributes(Program builder) {
|
||||
Shader shader = builder.attached.get(ShaderType.VERTEX);
|
||||
|
||||
shader.getTag(vertexData)
|
||||
.addPrefixedAttributes(builder, vertexPrefix);
|
||||
shader.getTag(instanceData)
|
||||
.addPrefixedAttributes(builder, instancePrefix);
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
||||
|
||||
public class LayoutTag {
|
||||
|
||||
public static final Pattern pattern = Pattern.compile("Layout\\((\\w+)(?:\\s*,\\s*(\\w*))?\\)");
|
||||
|
||||
final GlNumericType type;
|
||||
final boolean normalized;
|
||||
|
||||
public LayoutTag(Matcher matcher) {
|
||||
type = GlNumericType.byName(matcher.group(1));
|
||||
normalized = Boolean.parseBoolean(matcher.group(2));
|
||||
}
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class ModelTemplate extends ProgramTemplate {
|
||||
public static final String vertexData = "VertexData";
|
||||
public static final String fragment = "Fragment";
|
||||
|
||||
public static final String vertexPrefix = "a_v_";
|
||||
|
||||
public static final String[] requiredVert = new String[]{vertexData, fragment};
|
||||
|
||||
public static final String[] requiredFrag = {fragment};
|
||||
|
||||
public static final ResourceLocation vert = new ResourceLocation(Flywheel.ID, "template/model/model.vert");
|
||||
public static final ResourceLocation frag = new ResourceLocation(Flywheel.ID, "template/model/model.frag");
|
||||
|
||||
public ModelTemplate(ShaderSources loader) {
|
||||
super(loader);
|
||||
|
||||
templates.put(ShaderType.VERTEX, new ShaderTemplate(requiredVert, loader.getShaderSource(vert)));
|
||||
templates.put(ShaderType.FRAGMENT, new ShaderTemplate(requiredFrag, loader.getShaderSource(frag)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachAttributes(Program builder) {
|
||||
Shader shader = builder.attached.get(ShaderType.VERTEX);
|
||||
|
||||
shader.getTag(vertexData)
|
||||
.addPrefixedAttributes(builder, vertexPrefix);
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
|
||||
public abstract class ProgramTemplate implements IProcessingStage {
|
||||
|
||||
protected final ShaderSources loader;
|
||||
protected Map<ShaderType, ShaderTemplate> templates = new EnumMap<>(ShaderType.class);
|
||||
|
||||
public ProgramTemplate(ShaderSources loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(Shader shader) {
|
||||
ShaderTemplate template = templates.get(shader.type);
|
||||
|
||||
if (template == null) return;
|
||||
|
||||
shader.setSource(template.apply(shader));
|
||||
}
|
||||
|
||||
public void attachAttributes(Program builder) {
|
||||
|
||||
}
|
||||
}
|
|
@ -23,40 +23,32 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
|||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class Program {
|
||||
public class ProtoProgram {
|
||||
public final int program;
|
||||
|
||||
public ResourceLocation name;
|
||||
private int attributeIndex;
|
||||
|
||||
public final Map<ShaderType, Shader> attached;
|
||||
|
||||
private final IntList shaders;
|
||||
|
||||
public Program() {
|
||||
public ProtoProgram() {
|
||||
this.program = glCreateProgram();
|
||||
attached = new EnumMap<>(ShaderType.class);
|
||||
shaders = new IntArrayList(2);
|
||||
}
|
||||
|
||||
public Program attachShader(Shader shader, GlShader glShader) {
|
||||
public void attachShader(GlShader glShader) {
|
||||
glAttachShader(this.program, glShader.handle());
|
||||
|
||||
attached.put(shader.type, shader);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Program addAttribute(String name, int attributeCount) {
|
||||
public void addAttribute(String name, int attributeCount) {
|
||||
glBindAttribLocation(this.program, attributeIndex, name);
|
||||
attributeIndex += attributeCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links the attached shaders to this program.
|
||||
*/
|
||||
public Program link(ResourceLocation name) {
|
||||
public ProtoProgram link(ResourceLocation name) {
|
||||
this.name = name;
|
||||
glLinkProgram(this.program);
|
||||
|
||||
|
@ -75,7 +67,7 @@ public class Program {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Program deleteLinkedShaders() {
|
||||
public ProtoProgram deleteLinkedShaders() {
|
||||
shaders.forEach((IntConsumer) GL20::glDeleteShader);
|
||||
return this;
|
||||
}
|
|
@ -1,160 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class Shader {
|
||||
// #flwinclude <"valid_namespace:valid/path_to_file.glsl">
|
||||
private static final Pattern includePattern = Pattern.compile("#flwinclude <\"([\\w\\d_]+:[\\w\\d_./]+)\">");
|
||||
|
||||
public static final Pattern versionDetector = Pattern.compile("#version[^\\n]*");
|
||||
private static final Pattern decorator = Pattern.compile("#\\[([\\w_]*)]");
|
||||
|
||||
public final ResourceLocation name;
|
||||
public ShaderType type;
|
||||
private String source;
|
||||
private final ShaderSources loader;
|
||||
|
||||
private boolean parsed = false;
|
||||
final List<TaggedStruct> structs = new ArrayList<>(3);
|
||||
final Map<String, TaggedStruct> tag2Struct = new HashMap<>();
|
||||
final Map<String, TaggedStruct> name2Struct = new HashMap<>();
|
||||
|
||||
public Shader(ShaderSources loader, ShaderType type, ResourceLocation name, String source) {
|
||||
this.loader = loader;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public TaggedStruct getTag(String tag) {
|
||||
checkIfParsed();
|
||||
return tag2Struct.get(tag);
|
||||
}
|
||||
|
||||
public TaggedStruct getStruct(String name) {
|
||||
checkIfParsed();
|
||||
return name2Struct.get(name);
|
||||
}
|
||||
|
||||
private void checkIfParsed() {
|
||||
if (!parsed) {
|
||||
throw new IllegalStateException("tagged structs must be explicitly parsed before use");
|
||||
}
|
||||
}
|
||||
|
||||
public void defineAll(Collection<String> defines) {
|
||||
Matcher matcher = versionDetector.matcher(source);
|
||||
|
||||
if (matcher.find()) {
|
||||
StringBuffer sourceWithDefines = new StringBuffer();
|
||||
String lines = defines.stream()
|
||||
.map(it -> "#define " + it)
|
||||
.collect(Collectors.joining("\n"));
|
||||
|
||||
matcher.appendReplacement(sourceWithDefines, matcher.group() + '\n' + lines);
|
||||
|
||||
matcher.appendTail(sourceWithDefines);
|
||||
|
||||
source = sourceWithDefines.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public void parseStructs() {
|
||||
Matcher structMatcher = TaggedStruct.taggedStruct.matcher(source);
|
||||
|
||||
StringBuffer strippedSrc = new StringBuffer();
|
||||
|
||||
while (structMatcher.find()) {
|
||||
TaggedStruct struct = new TaggedStruct(structMatcher);
|
||||
|
||||
structs.add(struct);
|
||||
|
||||
String replacement = decorator.matcher(struct.source)
|
||||
.replaceFirst("");
|
||||
structMatcher.appendReplacement(strippedSrc, replacement);
|
||||
|
||||
tag2Struct.put(struct.tag, struct);
|
||||
name2Struct.put(struct.name, struct);
|
||||
}
|
||||
structMatcher.appendTail(strippedSrc);
|
||||
|
||||
this.source = strippedSrc.toString();
|
||||
parsed = true;
|
||||
}
|
||||
|
||||
public void processIncludes() {
|
||||
HashSet<ResourceLocation> seen = new HashSet<>();
|
||||
seen.add(name);
|
||||
|
||||
source = includeRecursive(source, seen).collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
private Stream<String> includeRecursive(String source, Set<ResourceLocation> seen) {
|
||||
return lines(source).flatMap(line -> {
|
||||
|
||||
Matcher matcher = includePattern.matcher(line);
|
||||
|
||||
if (matcher.find()) {
|
||||
String includeName = matcher.group(1);
|
||||
|
||||
ResourceLocation include = new ResourceLocation(includeName);
|
||||
|
||||
if (seen.add(include)) {
|
||||
try {
|
||||
return includeRecursive(loader.getShaderSource(include), seen);
|
||||
} catch (ShaderLoadingException e) {
|
||||
throw new ShaderLoadingException("could not resolve import: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return Stream.of(line);
|
||||
});
|
||||
}
|
||||
|
||||
public String printSource() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.append("Source for shader '")
|
||||
.append(name)
|
||||
.append("':\n");
|
||||
int i = 1;
|
||||
for (String s : source.split("\n")) {
|
||||
builder.append(String.format("%1$4s: ", i++))
|
||||
.append(s)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static Stream<String> lines(String s) {
|
||||
return new BufferedReader(new StringReader(s)).lines();
|
||||
}
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ShaderTemplate {
|
||||
|
||||
private static final String delimiter = "#flwbeginbody";
|
||||
private static final Pattern headerFinder = Pattern.compile(delimiter);
|
||||
|
||||
private static final Pattern prefixer = Pattern.compile("#FLWPrefixFields\\((\\w+),\\s*(\\w+),\\s*([\\w\\d]+)\\)");
|
||||
private static final Pattern assigner = Pattern.compile("#FLWAssignFields\\(([\\w\\d_]+),\\s*([\\w\\d_.]+),\\s*([\\w\\d_.]+)\\)");
|
||||
|
||||
final String[] requiredStructs;
|
||||
|
||||
final String header;
|
||||
final String body;
|
||||
|
||||
public ShaderTemplate(String[] requiredStructs, String templateSrc) {
|
||||
this.requiredStructs = requiredStructs;
|
||||
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(Shader shader) {
|
||||
shader.parseStructs();
|
||||
|
||||
return header + shader.getSource() + processBody(shader);
|
||||
}
|
||||
|
||||
public String processBody(Shader shader) {
|
||||
String s = body;
|
||||
|
||||
List<String> missing = new ArrayList<>();
|
||||
|
||||
for (String name : requiredStructs) {
|
||||
TaggedStruct struct = shader.getTag(name);
|
||||
|
||||
if (struct != null) {
|
||||
s = s.replace(name, struct.name);
|
||||
} else {
|
||||
missing.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!missing.isEmpty()) {
|
||||
String err = shader.name + " is missing: " + String.join(", ", missing);
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
|
||||
s = fillPrefixes(shader, s);
|
||||
s = fillAssigns(shader, s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
private String fillPrefixes(Shader shader, String s) {
|
||||
Matcher prefixMatches = prefixer.matcher(s);
|
||||
|
||||
StringBuffer out = new StringBuffer();
|
||||
while (prefixMatches.find()) {
|
||||
String structName = prefixMatches.group(1);
|
||||
String modifier = prefixMatches.group(2);
|
||||
String prefix = prefixMatches.group(3);
|
||||
|
||||
TaggedStruct struct = shader.getStruct(structName);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (TaggedField field : struct.fields) {
|
||||
builder.append(modifier);
|
||||
builder.append(' ');
|
||||
builder.append(field.getType());
|
||||
builder.append(' ');
|
||||
builder.append(prefix);
|
||||
builder.append(field.getName());
|
||||
builder.append(";\n");
|
||||
}
|
||||
|
||||
prefixMatches.appendReplacement(out, builder.toString());
|
||||
}
|
||||
prefixMatches.appendTail(out);
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
private String fillAssigns(Shader 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 (TaggedField field : struct.fields) {
|
||||
builder.append(lhs);
|
||||
builder.append(field.getName());
|
||||
builder.append(" = ");
|
||||
builder.append(rhs);
|
||||
builder.append(field.getName());
|
||||
builder.append(";\n");
|
||||
}
|
||||
|
||||
assignMatches.appendReplacement(out, builder.toString());
|
||||
}
|
||||
assignMatches.appendTail(out);
|
||||
return out.toString();
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class ShaderTransformer {
|
||||
|
||||
private final LinkedList<IProcessingStage> stages = new LinkedList<>();
|
||||
|
||||
public ShaderTransformer() {
|
||||
}
|
||||
|
||||
public ShaderTransformer pushStage(IProcessingStage stage) {
|
||||
if (stage != null) {
|
||||
stages.addLast(stage);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShaderTransformer popStage() {
|
||||
stages.removeLast();
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShaderTransformer prependStage(IProcessingStage stage) {
|
||||
if (stage != null) {
|
||||
stages.addFirst(stage);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void transformSource(Shader shader) {
|
||||
|
||||
for (IProcessingStage stage : this.stages) {
|
||||
stage.process(shader);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TaggedField {
|
||||
public static final Pattern fieldPattern = Pattern.compile("(?:#\\[([^\\n]*)]\\s*)?(\\S+)\\s*(\\S+);");
|
||||
|
||||
public String annotation;
|
||||
public String name;
|
||||
public String type;
|
||||
public LayoutTag layout;
|
||||
|
||||
public TaggedField(Matcher fieldMatcher) {
|
||||
annotation = fieldMatcher.group(1);
|
||||
type = fieldMatcher.group(2);
|
||||
name = fieldMatcher.group(3);
|
||||
|
||||
if (annotation != null) {
|
||||
Matcher matcher = LayoutTag.pattern.matcher(annotation);
|
||||
|
||||
if (matcher.find()) {
|
||||
layout = new LayoutTag(matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getAnnotation() {
|
||||
return annotation;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TaggedField{" + "name='" + name + '\'' + ", type='" + type + '\'' + '}';
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.loading;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TaggedStruct {
|
||||
|
||||
// https://regexr.com/5t207
|
||||
public static final Pattern taggedStruct = Pattern.compile("#\\[(\\w*)]\\s*struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d \\t#\\[\\](),;\\n]*)}\\s*;");
|
||||
|
||||
public int srcStart, srcEnd;
|
||||
public String source;
|
||||
public String tag;
|
||||
public String name;
|
||||
public String body;
|
||||
|
||||
List<TaggedField> fields = new ArrayList<>(4);
|
||||
Map<String, String> fields2Types = 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 = TaggedField.fieldPattern.matcher(body);
|
||||
|
||||
while (fielder.find()) {
|
||||
fields.add(new TaggedField(fielder));
|
||||
fields2Types.put(fielder.group(2), fielder.group(1));
|
||||
}
|
||||
}
|
||||
|
||||
public void addPrefixedAttributes(Program builder, String prefix) {
|
||||
for (TaggedField field : fields) {
|
||||
int attributeCount = TypeHelper.getAttributeCount(field.type);
|
||||
|
||||
builder.addAttribute(prefix + field.name, attributeCount);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.StructField;
|
||||
|
||||
public interface ITemplate {
|
||||
void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file);
|
||||
|
||||
void attachAttributes(ProtoProgram program, SourceFile file);
|
||||
|
||||
|
||||
static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
|
||||
ImmutableList<StructField> fields = struct.getFields();
|
||||
|
||||
for (StructField field : fields) {
|
||||
builder.append(qualifier)
|
||||
.append(' ')
|
||||
.append(field.type)
|
||||
.append(' ')
|
||||
.append(prefix)
|
||||
.append(field.name)
|
||||
.append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) {
|
||||
ImmutableList<StructField> fields = struct.getFields();
|
||||
|
||||
for (StructField field : fields) {
|
||||
builder.append(prefix1)
|
||||
.append(field.name)
|
||||
.append(" = ")
|
||||
.append(prefix2)
|
||||
.append(field.name)
|
||||
.append(";\n");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.Variable;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
public class InstanceTemplateData {
|
||||
|
||||
public final SourceFile file;
|
||||
public final ShaderFunction vertexMain;
|
||||
public final ShaderFunction fragmentMain;
|
||||
public final Span interpolantName;
|
||||
public final Span vertexName;
|
||||
public final Span instanceName;
|
||||
public final ShaderStruct interpolant;
|
||||
public final ShaderStruct vertex;
|
||||
public final ShaderStruct instance;
|
||||
|
||||
public InstanceTemplateData(SourceFile file) {
|
||||
this.file = file;
|
||||
|
||||
Optional<ShaderFunction> vertexFunc = file.findFunction("vertex");
|
||||
Optional<ShaderFunction> fragmentFunc = file.findFunction("fragment");
|
||||
|
||||
|
||||
if (!fragmentFunc.isPresent()) {
|
||||
ErrorReporter.generateFileError(file, "could not find \"fragment\" function");
|
||||
}
|
||||
if (!vertexFunc.isPresent()) {
|
||||
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
||||
}
|
||||
|
||||
if (!fragmentFunc.isPresent() || !vertexFunc.isPresent()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
fragmentMain = fragmentFunc.get();
|
||||
vertexMain = vertexFunc.get();
|
||||
ImmutableList<Variable> parameters = fragmentMain.getParameters();
|
||||
ImmutableList<Variable> vertexParams = vertexMain.getParameters();
|
||||
|
||||
if (parameters.size() != 1) {
|
||||
ErrorReporter.generateSpanError(fragmentMain.getArgs(), "instancing requires fragment function to have 1 argument");
|
||||
}
|
||||
|
||||
if (vertexParams.size() != 2) {
|
||||
ErrorReporter.generateSpanError(vertexMain.getArgs(), "instancing requires vertex function to have 2 arguments");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
interpolantName = vertexMain.getType();
|
||||
vertexName = vertexParams.get(0)
|
||||
.typeName();
|
||||
instanceName = vertexParams.get(1)
|
||||
.typeName();
|
||||
|
||||
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
|
||||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||
Optional<ShaderStruct> maybeInstance = file.findStruct(instanceName);
|
||||
|
||||
if (!maybeVertex.isPresent()) {
|
||||
ErrorReporter.generateMissingStruct(file, vertexName);
|
||||
}
|
||||
|
||||
if (!maybeInterpolant.isPresent()) {
|
||||
ErrorReporter.generateMissingStruct(file, interpolantName);
|
||||
}
|
||||
|
||||
if (!maybeInstance.isPresent()) {
|
||||
ErrorReporter.generateMissingStruct(file, instanceName);
|
||||
}
|
||||
|
||||
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent() || !maybeInstance.isPresent()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
interpolant = maybeInterpolant.get();
|
||||
vertex = maybeVertex.get();
|
||||
instance = maybeInstance.get();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
|
||||
|
||||
public class InstancingTemplate implements ITemplate {
|
||||
|
||||
public static final InstancingTemplate INSTANCE = new InstancingTemplate();
|
||||
|
||||
private final Map<SourceFile, InstanceTemplateData> datas = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) {
|
||||
if (type == ShaderType.VERTEX) {
|
||||
vertexFooter(builder, file);
|
||||
} else if (type == ShaderType.FRAGMENT) {
|
||||
fragmentFooter(builder, file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachAttributes(ProtoProgram program, SourceFile file) {
|
||||
InstanceTemplateData data = getData(file);
|
||||
data.vertex.addPrefixedAttributes(program, "a_v_");
|
||||
data.instance.addPrefixedAttributes(program, "a_i_");
|
||||
}
|
||||
|
||||
public InstanceTemplateData getData(SourceFile file) {
|
||||
return datas.computeIfAbsent(file, InstanceTemplateData::new);
|
||||
}
|
||||
|
||||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||
InstanceTemplateData data = getData(file);
|
||||
|
||||
ITemplate.prefixFields(template, data.vertex, "attribute", "a_v_");
|
||||
ITemplate.prefixFields(template, data.instance, "attribute", "a_i_");
|
||||
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.vertexName)
|
||||
.append(" v;\n");
|
||||
ITemplate.assignFields(template, data.vertex, "v.", "a_v_");
|
||||
|
||||
template.append(data.instanceName)
|
||||
.append(" i;\n");
|
||||
ITemplate.assignFields(template, data.instance, "i.", "a_i_");
|
||||
|
||||
template.append(data.interpolantName)
|
||||
.append(" o = ")
|
||||
.append(data.vertexMain.call("v", "i"))
|
||||
.append(";\n");
|
||||
|
||||
ITemplate.assignFields(template, data.interpolant, "v2f_", "o.");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
|
||||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||
InstanceTemplateData data = getData(file);
|
||||
|
||||
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.interpolantName)
|
||||
.append(" o;\n");
|
||||
ITemplate.assignFields(template, data.interpolant, "o.", "v2f_");
|
||||
|
||||
template.append(data.fragmentMain.call("o"))
|
||||
.append(";\n");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
}
|
|
@ -1,137 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.loading.Program;
|
||||
import com.jozufozu.flywheel.backend.loading.ProgramTemplate;
|
||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
||||
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
|
||||
import com.jozufozu.flywheel.backend.loading.ShaderTransformer;
|
||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||
import com.jozufozu.flywheel.core.shader.GameStateProgram;
|
||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
||||
|
||||
public class LegacyPipeline<P extends WorldProgram> implements IShaderPipeline<P> {
|
||||
|
||||
private static final String declaration = "#flwbuiltins";
|
||||
private static final Pattern builtinPattern = Pattern.compile(declaration);
|
||||
|
||||
|
||||
private final ShaderSources sources;
|
||||
protected ShaderTransformer transformer;
|
||||
private final ProgramTemplate template;
|
||||
private final ExtensibleGlProgram.Factory<P> factory;
|
||||
private final Map<ShaderType, String> builtinSources;
|
||||
|
||||
|
||||
public LegacyPipeline(ShaderSources sources, ProgramTemplate template, ExtensibleGlProgram.Factory<P> factory, Map<ShaderType, String> builtinSources) {
|
||||
this.sources = sources;
|
||||
this.template = template;
|
||||
this.factory = factory;
|
||||
|
||||
transformer = new ShaderTransformer().pushStage(this::injectBuiltins)
|
||||
.pushStage(Shader::processIncludes)
|
||||
.pushStage(template)
|
||||
.pushStage(Shader::processIncludes);
|
||||
this.builtinSources = builtinSources;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMultiProgram<P> compile(ProgramSpec spec) {
|
||||
GameStateProgram.Builder<P> builder = GameStateProgram.builder(compile(spec, null));
|
||||
|
||||
for (ProgramState state : spec.states) {
|
||||
|
||||
builder.withVariant(state.getContext(), compile(spec, state));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingests the given shaders, compiling them and linking them together after applying the transformer to the source.
|
||||
*
|
||||
* @param shaders What are the different shader stages that should be linked together?
|
||||
* @return A program with all provided shaders attached
|
||||
*/
|
||||
protected static Program buildProgram(Shader... shaders) {
|
||||
List<GlShader> compiled = new ArrayList<>(shaders.length);
|
||||
try {
|
||||
Program builder = new Program();
|
||||
|
||||
for (Shader shader : shaders) {
|
||||
GlShader sh = new GlShader(shader.name, shader.type, shader.getSource());
|
||||
compiled.add(sh);
|
||||
|
||||
builder.attachShader(shader, sh);
|
||||
}
|
||||
|
||||
return builder;
|
||||
} finally {
|
||||
compiled.forEach(GlObject::delete);
|
||||
}
|
||||
}
|
||||
|
||||
public Program loadAndLink(ProgramSpec spec, @Nullable ProgramState state) {
|
||||
Shader vertexFile = sources.source(spec.vert, ShaderType.VERTEX);
|
||||
Shader fragmentFile = sources.source(spec.frag, ShaderType.FRAGMENT);
|
||||
|
||||
transformer.transformSource(vertexFile);
|
||||
transformer.transformSource(fragmentFile);
|
||||
|
||||
if (state != null) {
|
||||
vertexFile.defineAll(state.getDefines());
|
||||
fragmentFile.defineAll(state.getDefines());
|
||||
}
|
||||
|
||||
Program program = buildProgram(vertexFile, fragmentFile);
|
||||
template.attachAttributes(program);
|
||||
|
||||
program.link(spec.name).deleteLinkedShaders();
|
||||
|
||||
String descriptor = program.program + ": " + spec.name;
|
||||
|
||||
if (state != null)
|
||||
descriptor += "#" + state;
|
||||
|
||||
Backend.log.debug(descriptor);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
private P compile(ProgramSpec spec, @Nullable ProgramState state) {
|
||||
if (state != null) {
|
||||
Program program = loadAndLink(spec, state);
|
||||
return factory.create(program.name, program.program, state.getExtensions());
|
||||
} else {
|
||||
Program program = loadAndLink(spec, null);
|
||||
return factory.create(program.name, program.program, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace #flwbuiltins with whatever expansion this context provides for the given shader.
|
||||
*/
|
||||
public void injectBuiltins(Shader shader) {
|
||||
Matcher matcher = builtinPattern.matcher(shader.getSource());
|
||||
|
||||
if (matcher.find()) shader.setSource(matcher.replaceFirst(builtinSources.get(shader.type)));
|
||||
else
|
||||
throw new ShaderLoadingException(String.format("%s is missing %s, cannot use in World Context", shader.type.name, declaration));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.Variable;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
public class OneShotData {
|
||||
|
||||
public final SourceFile file;
|
||||
public final ShaderFunction vertexMain;
|
||||
public final Span interpolantName;
|
||||
public final Span vertexName;
|
||||
public final ShaderStruct interpolant;
|
||||
public final ShaderStruct vertex;
|
||||
public final ShaderFunction fragmentMain;
|
||||
|
||||
public OneShotData(SourceFile file) {
|
||||
this.file = file;
|
||||
|
||||
Optional<ShaderFunction> maybeVertexMain = file.findFunction("vertex");
|
||||
Optional<ShaderFunction> maybeFragmentMain = file.findFunction("fragment");
|
||||
|
||||
if (!maybeVertexMain.isPresent()) {
|
||||
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
if (!maybeFragmentMain.isPresent()) {
|
||||
ErrorReporter.generateFileError(file, "could not find \"fragment\" function");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
vertexMain = maybeVertexMain.get();
|
||||
fragmentMain = maybeFragmentMain.get();
|
||||
ImmutableList<Variable> parameters = fragmentMain.getParameters();
|
||||
ImmutableList<Variable> vertexParameters = vertexMain.getParameters();
|
||||
|
||||
if (vertexParameters.size() != 1) {
|
||||
ErrorReporter.generateSpanError(vertexMain.getArgs(), "a basic model requires vertex function to have one argument");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
if (parameters.size() != 1) {
|
||||
ErrorReporter.generateSpanError(fragmentMain.getArgs(), "instancing requires fragment function to have 1 argument");
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
|
||||
interpolantName = vertexMain.getType();
|
||||
vertexName = vertexParameters.get(0)
|
||||
.typeName();
|
||||
|
||||
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
|
||||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||
|
||||
if (!maybeVertex.isPresent())
|
||||
ErrorReporter.generateMissingStruct(file, vertexName);
|
||||
|
||||
if (!maybeInterpolant.isPresent())
|
||||
ErrorReporter.generateMissingStruct(file, interpolantName);
|
||||
|
||||
if (!maybeVertex.isPresent() || !maybeInterpolant.isPresent()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
interpolant = maybeInterpolant.get();
|
||||
vertex = maybeVertex.get();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
|
||||
|
||||
public class OneShotTemplate implements ITemplate {
|
||||
|
||||
public static final OneShotTemplate INSTANCE = new OneShotTemplate();
|
||||
|
||||
|
||||
private final Map<SourceFile, OneShotData> datas = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file) {
|
||||
if (type == ShaderType.VERTEX) {
|
||||
vertexFooter(builder, file);
|
||||
} else if (type == ShaderType.FRAGMENT) {
|
||||
fragmentFooter(builder, file);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachAttributes(ProtoProgram program, SourceFile file) {
|
||||
OneShotData data = getData(file);
|
||||
data.vertex.addPrefixedAttributes(program, "a_v_");
|
||||
}
|
||||
|
||||
public OneShotData getData(SourceFile file) {
|
||||
return datas.computeIfAbsent(file, OneShotData::new);
|
||||
}
|
||||
|
||||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||
OneShotData data = getData(file);
|
||||
|
||||
ITemplate.prefixFields(template, data.vertex, "attribute", "a_v_");
|
||||
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.vertexName)
|
||||
.append(" v;\n");
|
||||
ITemplate.assignFields(template, data.vertex, "v.", "a_v_");
|
||||
|
||||
template.append(data.interpolantName)
|
||||
.append(" o = ")
|
||||
.append(data.vertexMain.call("v"))
|
||||
.append(";\n");
|
||||
|
||||
ITemplate.assignFields(template, data.interpolant, "v2f_", "o.");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
|
||||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||
OneShotData data = getData(file);
|
||||
|
||||
ITemplate.prefixFields(template, data.interpolant, "varying", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.interpolant.name)
|
||||
.append(" o;\n");
|
||||
ITemplate.assignFields(template, data.interpolant, "o.", "v2f_");
|
||||
|
||||
template.append(data.fragmentMain.call("o"))
|
||||
.append(";\n");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.backend.FileResolution;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
|
||||
|
@ -11,18 +11,19 @@ import net.minecraft.util.ResourceLocation;
|
|||
public class ShaderBuilder {
|
||||
|
||||
public final ResourceLocation name;
|
||||
public final Template template;
|
||||
public final ITemplate template;
|
||||
public final FileResolution header;
|
||||
|
||||
private SourceFile mainFile;
|
||||
public SourceFile mainFile;
|
||||
private GLSLVersion version;
|
||||
|
||||
private StringBuilder source;
|
||||
private StringBuilder defines;
|
||||
private CharSequence footer;
|
||||
|
||||
public ShaderBuilder(ResourceLocation name, Template template) {
|
||||
public ShaderBuilder(ResourceLocation name, ITemplate template, FileResolution header) {
|
||||
this.name = name;
|
||||
this.template = template;
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public ShaderBuilder setVersion(GLSLVersion version) {
|
||||
|
@ -41,21 +42,13 @@ public class ShaderBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ShaderBuilder setFooter(CharSequence footer) {
|
||||
this.footer = footer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ShaderBuilder setMainSource(SourceFile file) {
|
||||
if (mainFile == file) return this;
|
||||
|
||||
mainFile = file;
|
||||
source = new StringBuilder();
|
||||
|
||||
for (SourceFile includeFile : Includer.recurseIncludes(file)) {
|
||||
source.append(includeFile.getElidedSource());
|
||||
}
|
||||
source.append(file.getElidedSource());
|
||||
file.generateFinalSource(source);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
@ -70,9 +63,13 @@ public class ShaderBuilder {
|
|||
.append("#define ")
|
||||
.append(type.define)
|
||||
.append('\n')
|
||||
.append(defines != null ? defines : "")
|
||||
.append(source)
|
||||
.append(template.footer(type, mainFile));
|
||||
.append(defines != null ? defines : "");
|
||||
SourceFile file = header.getFile();
|
||||
if (file != null) {
|
||||
file.generateFinalSource(finalSource);
|
||||
}
|
||||
mainFile.generateFinalSource(finalSource);
|
||||
template.generateTemplateSource(finalSource, type, mainFile);
|
||||
|
||||
return new GlShader(name, type, finalSource);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.backend.FileResolution;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.Import;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
||||
|
@ -33,7 +40,7 @@ public class SourceFile {
|
|||
|
||||
public final ResourceLocation name;
|
||||
|
||||
private final ShaderSources parent;
|
||||
public final ShaderSources parent;
|
||||
private final String source;
|
||||
private final CharSequence elided;
|
||||
private final ImmutableList<String> lines;
|
||||
|
@ -89,6 +96,61 @@ public class SourceFile {
|
|||
return imports;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search this file and recursively search all imports to find a struct definition matching the given name.
|
||||
*
|
||||
* @param name The name of the struct to find.
|
||||
* @return null if no definition matches the name.
|
||||
*/
|
||||
public Optional<ShaderStruct> findStruct(CharSequence name) {
|
||||
ShaderStruct struct = getStructs().get(name.toString());
|
||||
|
||||
if (struct != null) return Optional.of(struct);
|
||||
|
||||
for (Import include : getIncludes()) {
|
||||
Optional<ShaderStruct> externalStruct = include.getOptional()
|
||||
.flatMap(file -> file.findStruct(name));
|
||||
|
||||
if (externalStruct.isPresent()) return externalStruct;
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search this file and recursively search all imports to find a function definition matching the given name.
|
||||
*
|
||||
* @param name The name of the function to find.
|
||||
* @return Optional#empty() if no definition matches the name.
|
||||
*/
|
||||
public Optional<ShaderFunction> findFunction(CharSequence name) {
|
||||
ShaderFunction local = getFunctions().get(name.toString());
|
||||
|
||||
if (local != null) return Optional.of(local);
|
||||
|
||||
for (Import include : getIncludes()) {
|
||||
Optional<ShaderFunction> external = include.getOptional()
|
||||
.flatMap(file -> file.findFunction(name));
|
||||
|
||||
if (external.isPresent()) return external;
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public CharSequence importStatement() {
|
||||
return "#use " + '"' + name + '"';
|
||||
}
|
||||
|
||||
public void generateFinalSource(StringBuilder source) {
|
||||
for (Import include : getIncludes()) {
|
||||
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++) {
|
||||
|
|
|
@ -1,123 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.StructField;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.Variable;
|
||||
|
||||
public class Template {
|
||||
|
||||
public CharSequence footer(ShaderType type, SourceFile file) {
|
||||
switch (type) {
|
||||
case VERTEX:
|
||||
return vertexFooter(file);
|
||||
case FRAGMENT:
|
||||
return fragmentFooter(file);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public CharSequence vertexFooter(SourceFile file) {
|
||||
ShaderFunction vertexMain = file.getFunctions()
|
||||
.get("vertex");
|
||||
|
||||
ImmutableList<Variable> parameters = vertexMain.getParameters();
|
||||
|
||||
ShaderStruct interpolant = file.getStructs()
|
||||
.get(vertexMain.returnType());
|
||||
|
||||
ShaderStruct vertex = file.getStructs()
|
||||
.get(parameters.get(0)
|
||||
.typeName()
|
||||
.get());
|
||||
|
||||
ShaderStruct instance = file.getStructs()
|
||||
.get(parameters.get(1)
|
||||
.typeName()
|
||||
.get());
|
||||
|
||||
StringBuilder template = new StringBuilder();
|
||||
|
||||
prefixFields(template, vertex, "attribute", "a_v_");
|
||||
prefixFields(template, instance, "attribute", "a_i_");
|
||||
prefixFields(template, interpolant, "varying", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(vertex.name)
|
||||
.append(" v;\n");
|
||||
assignFields(template, vertex, "v.", "a_v_");
|
||||
|
||||
template.append(instance.name)
|
||||
.append(" i;\n");
|
||||
assignFields(template, instance, "i.", "a_i_");
|
||||
|
||||
template.append(interpolant.name)
|
||||
.append(" o = ")
|
||||
.append(vertexMain.call("v", "i"))
|
||||
.append(";\n");
|
||||
|
||||
assignFields(template, interpolant, "v2f_", "o.");
|
||||
|
||||
template.append('}');
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
public CharSequence fragmentFooter(SourceFile file) {
|
||||
ShaderFunction fragmentMain = file.getFunctions()
|
||||
.get("fragment");
|
||||
|
||||
ImmutableList<Variable> parameters = fragmentMain.getParameters();
|
||||
|
||||
ShaderStruct interpolant = file.getStructs()
|
||||
.get(parameters.get(0)
|
||||
.typeName()
|
||||
.get());
|
||||
|
||||
StringBuilder template = new StringBuilder();
|
||||
|
||||
prefixFields(template, interpolant, "varying", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(interpolant.name)
|
||||
.append(" o;\n");
|
||||
assignFields(template, interpolant, "o.", "v2f_");
|
||||
|
||||
template.append(fragmentMain.call("o"))
|
||||
.append(";\n");
|
||||
|
||||
template.append('}');
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
|
||||
ImmutableList<StructField> fields = struct.getFields();
|
||||
|
||||
for (StructField field : fields) {
|
||||
builder.append(qualifier)
|
||||
.append(' ')
|
||||
.append(field.type)
|
||||
.append(' ')
|
||||
.append(prefix)
|
||||
.append(field.name)
|
||||
.append(";\n");
|
||||
}
|
||||
}
|
||||
|
||||
public static void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) {
|
||||
ImmutableList<StructField> fields = struct.getFields();
|
||||
|
||||
for (StructField field : fields) {
|
||||
builder.append(prefix1)
|
||||
.append(field.name)
|
||||
.append(" = ")
|
||||
.append(prefix2)
|
||||
.append(field.name)
|
||||
.append(";\n");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,9 +12,11 @@ import javax.annotation.Nullable;
|
|||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.FileResolution;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
|
||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||
import com.jozufozu.flywheel.core.shader.GameStateProgram;
|
||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
||||
|
@ -24,15 +26,20 @@ import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
|||
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class WorldShaderPipeline<P extends WorldProgram> {
|
||||
public class WorldShaderPipeline<P extends WorldProgram> implements IShaderPipeline<P> {
|
||||
|
||||
private final ShaderSources sources;
|
||||
|
||||
private final ExtensibleGlProgram.Factory<P> factory;
|
||||
|
||||
public WorldShaderPipeline(ShaderSources sources, ExtensibleGlProgram.Factory<P> factory) {
|
||||
private final ITemplate template;
|
||||
private final FileResolution header;
|
||||
|
||||
public WorldShaderPipeline(ShaderSources sources, ExtensibleGlProgram.Factory<P> factory, ITemplate template, FileResolution header) {
|
||||
this.sources = sources;
|
||||
this.factory = factory;
|
||||
this.template = template;
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public IMultiProgram<P> compile(ProgramSpec spec) {
|
||||
|
@ -43,9 +50,9 @@ public class WorldShaderPipeline<P extends WorldProgram> {
|
|||
}
|
||||
|
||||
public IMultiProgram<P> compile(ResourceLocation name, SourceFile file, List<ProgramState> variants) {
|
||||
ShaderBuilder shader = new ShaderBuilder(name, new Template())
|
||||
ShaderBuilder shader = new ShaderBuilder(name, template, header)
|
||||
.setMainSource(file)
|
||||
.setVersion(GLSLVersion.V120);
|
||||
.setVersion(GLSLVersion.V110);
|
||||
|
||||
GameStateProgram.Builder<P> builder = GameStateProgram.builder(compile(shader, name, null));
|
||||
|
||||
|
@ -65,27 +72,20 @@ public class WorldShaderPipeline<P extends WorldProgram> {
|
|||
GlShader vertex = shader.compile(name, ShaderType.VERTEX);
|
||||
GlShader fragment = shader.compile(name, ShaderType.FRAGMENT);
|
||||
|
||||
int program = GL20.glCreateProgram();
|
||||
ProtoProgram program = new ProtoProgram();
|
||||
|
||||
GL20.glAttachShader(program, vertex.handle());
|
||||
GL20.glAttachShader(program, fragment.handle());
|
||||
program.attachShader(vertex);
|
||||
program.attachShader(fragment);
|
||||
|
||||
String log = glGetProgramInfoLog(program);
|
||||
template.attachAttributes(program, shader.mainFile);
|
||||
|
||||
if (!log.isEmpty()) {
|
||||
Backend.log.debug("Program link log for " + name + ": " + log);
|
||||
}
|
||||
|
||||
int result = glGetProgrami(program, GL_LINK_STATUS);
|
||||
|
||||
if (result != GL_TRUE) {
|
||||
throw new RuntimeException("Shader program linking failed, see log for details");
|
||||
}
|
||||
program.link(name);
|
||||
program.deleteLinkedShaders();
|
||||
|
||||
if (variant != null) {
|
||||
return factory.create(name, program, variant.getExtensions());
|
||||
return factory.create(name, program.program, variant.getExtensions());
|
||||
} else {
|
||||
return factory.create(name, program, null);
|
||||
return factory.create(name, program.program, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,36 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.error;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
|
||||
public class ErrorBuilder {
|
||||
|
||||
private final StringBuilder internal = new StringBuilder();
|
||||
|
||||
public ErrorBuilder header(CharSequence msg) {
|
||||
public ErrorBuilder error(CharSequence msg) {
|
||||
internal.append("error: ")
|
||||
.append(msg);
|
||||
return endLine();
|
||||
}
|
||||
|
||||
public ErrorBuilder errorIn(SourceFile file) {
|
||||
public ErrorBuilder note(CharSequence msg) {
|
||||
internal.append("note: ")
|
||||
.append(msg);
|
||||
return endLine();
|
||||
}
|
||||
|
||||
public ErrorBuilder hint(CharSequence msg) {
|
||||
internal.append("hint: ")
|
||||
.append(msg);
|
||||
return endLine();
|
||||
}
|
||||
|
||||
public ErrorBuilder in(SourceFile file) {
|
||||
internal.append("--> ")
|
||||
.append(file.name);
|
||||
return endLine();
|
||||
|
@ -37,7 +54,18 @@ public class ErrorBuilder {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ErrorBuilder hintIncludeFor(Span span) {
|
||||
if (span == null) return this;
|
||||
|
||||
hint("add " + span.getSourceFile().importStatement())
|
||||
.in(span.getSourceFile())
|
||||
.pointAt(span, 1);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
||||
|
||||
SourceFile file = span.getSourceFile();
|
||||
|
||||
if (span.lines() == 1) {
|
||||
|
@ -47,6 +75,8 @@ public class ErrorBuilder {
|
|||
int firstLine = Math.max(0, spanLine - ctxLines);
|
||||
int lastLine = Math.min(file.getLineCount(), spanLine + ctxLines);
|
||||
|
||||
int digits = FlwUtil.numDigits(lastLine);
|
||||
|
||||
|
||||
int firstCol = span.getStart().getCol();
|
||||
int lastCol = span.getEnd().getCol();
|
||||
|
@ -57,7 +87,7 @@ public class ErrorBuilder {
|
|||
numberedLine(i + 1, line);
|
||||
|
||||
if (i == spanLine) {
|
||||
line(" ", generateUnderline(firstCol, lastCol));
|
||||
line(FlwUtil.repeatChar(' ', digits), generateUnderline(firstCol, lastCol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.error;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
public class ErrorReporter {
|
||||
|
@ -11,11 +14,37 @@ public class ErrorReporter {
|
|||
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
CharSequence error = builder.header(message)
|
||||
.errorIn(file)
|
||||
CharSequence error = builder.error(message)
|
||||
.in(file)
|
||||
.pointAt(span, 2)
|
||||
.build();
|
||||
|
||||
Backend.log.info(error);
|
||||
Backend.log.error(error);
|
||||
}
|
||||
|
||||
public static void generateFileError(SourceFile file, String message) {
|
||||
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
CharSequence error = builder.error(message)
|
||||
.in(file)
|
||||
.build();
|
||||
|
||||
Backend.log.error(error);
|
||||
}
|
||||
|
||||
public static void generateMissingStruct(SourceFile file, Span vertexName) {
|
||||
Optional<Span> span = file.parent.index.getStructDefinitionsMatching(vertexName)
|
||||
.stream()
|
||||
.findFirst()
|
||||
.map(ShaderStruct::getName);
|
||||
ErrorBuilder builder = new ErrorBuilder();
|
||||
|
||||
ErrorBuilder error = builder.error("struct not defined")
|
||||
.in(file)
|
||||
.pointAt(vertexName, 2)
|
||||
.hintIncludeFor(span.orElse(null));
|
||||
|
||||
Backend.log.error(error.build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.jozufozu.flywheel.backend.pipeline.parse;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
|
@ -43,6 +44,14 @@ public class Import extends AbstractShaderElement {
|
|||
return new ResourceLocation("");
|
||||
}
|
||||
|
||||
public FileResolution getResolution() {
|
||||
return resolution;
|
||||
}
|
||||
|
||||
public Optional<SourceFile> getOptional() {
|
||||
return Optional.ofNullable(resolution.getFile());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public SourceFile getFile() {
|
||||
return resolution.getFile();
|
||||
|
|
|
@ -29,6 +29,22 @@ public class ShaderFunction extends AbstractShaderElement {
|
|||
this.parameters = parseArguments();
|
||||
}
|
||||
|
||||
public Span getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Span getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Span getArgs() {
|
||||
return args;
|
||||
}
|
||||
|
||||
public Span getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public String call(String... args) {
|
||||
return name + "(" + String.join(", ", args) + ")";
|
||||
}
|
||||
|
@ -37,7 +53,7 @@ public class ShaderFunction extends AbstractShaderElement {
|
|||
return parameters;
|
||||
}
|
||||
|
||||
public String returnType() {
|
||||
public String returnTypeName() {
|
||||
return type.get();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline.parse;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.backend.loading.Program;
|
||||
import com.jozufozu.flywheel.backend.loading.ProtoProgram;
|
||||
import com.jozufozu.flywheel.backend.loading.TypeHelper;
|
||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||
|
||||
public class ShaderStruct extends AbstractShaderElement {
|
||||
|
@ -30,6 +28,14 @@ public class ShaderStruct extends AbstractShaderElement {
|
|||
this.fields2Types = createTypeLookup();
|
||||
}
|
||||
|
||||
public Span getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Span getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public ImmutableList<StructField> getFields() {
|
||||
return fields;
|
||||
}
|
||||
|
@ -59,7 +65,7 @@ public class ShaderStruct extends AbstractShaderElement {
|
|||
return fields.build();
|
||||
}
|
||||
|
||||
public void addPrefixedAttributes(Program builder, String prefix) {
|
||||
public void addPrefixedAttributes(ProtoProgram builder, String prefix) {
|
||||
for (StructField field : fields) {
|
||||
int attributeCount = TypeHelper.getAttributeCount(field.type);
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
package com.jozufozu.flywheel.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.FileResolution;
|
||||
import com.jozufozu.flywheel.backend.ResourceUtil;
|
||||
import com.jozufozu.flywheel.backend.SpecMetaRegistry;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline;
|
||||
import com.jozufozu.flywheel.backend.pipeline.InstancingTemplate;
|
||||
import com.jozufozu.flywheel.backend.pipeline.WorldShaderPipeline;
|
||||
import com.jozufozu.flywheel.core.crumbling.CrumblingProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldFog;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.extension.IProgramExtension;
|
||||
import com.jozufozu.flywheel.core.shader.gamestate.FogStateProvider;
|
||||
import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider;
|
||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
||||
|
@ -33,13 +34,14 @@ public class Contexts {
|
|||
SpecMetaRegistry.register(WorldFog.LINEAR);
|
||||
SpecMetaRegistry.register(WorldFog.EXP2);
|
||||
|
||||
CRUMBLING = backend.register(new WorldContext<>(backend, CrumblingProgram::new).withName(Names.CRUMBLING)
|
||||
.withBuiltin(ShaderType.FRAGMENT, Names.CRUMBLING, "/builtin.frag")
|
||||
.withBuiltin(ShaderType.VERTEX, Names.CRUMBLING, "/builtin.vert"));
|
||||
FileResolution crumblingBuiltins = backend.sources.resolveFile(ResourceUtil.subPath(Names.CRUMBLING, ".glsl"));
|
||||
FileResolution worldBuiltins = backend.sources.resolveFile(ResourceUtil.subPath(Names.WORLD, ".glsl"));
|
||||
|
||||
WORLD = backend.register(new WorldContext<>(backend, WorldProgram::new).withName(Names.WORLD)
|
||||
.withBuiltin(ShaderType.FRAGMENT, Names.WORLD, "/builtin.frag")
|
||||
.withBuiltin(ShaderType.VERTEX, Names.WORLD, "/builtin.vert"));
|
||||
IShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(backend.sources, CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins);
|
||||
IShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(backend.sources, WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins);
|
||||
|
||||
CRUMBLING = backend.register(new WorldContext<>(backend, crumblingPipeline).withName(Names.CRUMBLING));
|
||||
WORLD = backend.register(new WorldContext<>(backend, worldPipeline).withName(Names.WORLD));
|
||||
}
|
||||
|
||||
public static class Names {
|
||||
|
|
|
@ -1,22 +1,12 @@
|
|||
package com.jozufozu.flywheel.core;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.ResourceUtil;
|
||||
import com.jozufozu.flywheel.backend.ShaderContext;
|
||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.loading.InstancedArraysTemplate;
|
||||
import com.jozufozu.flywheel.backend.loading.ProgramTemplate;
|
||||
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
|
||||
import com.jozufozu.flywheel.backend.material.MaterialSpec;
|
||||
import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline;
|
||||
import com.jozufozu.flywheel.backend.pipeline.LegacyPipeline;
|
||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
|
||||
|
@ -25,24 +15,16 @@ import net.minecraft.util.ResourceLocation;
|
|||
public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||
protected ResourceLocation name;
|
||||
protected Supplier<Stream<ResourceLocation>> specStream;
|
||||
protected TemplateFactory templateFactory;
|
||||
|
||||
private final Map<ShaderType, ResourceLocation> builtins = new EnumMap<>(ShaderType.class);
|
||||
private final Map<ShaderType, String> builtinSources = new EnumMap<>(ShaderType.class);
|
||||
public final IShaderPipeline<P> pipeline;
|
||||
|
||||
private final ExtensibleGlProgram.Factory<P> factory;
|
||||
|
||||
public IShaderPipeline<P> pipeline;
|
||||
|
||||
public WorldContext(Backend backend, ExtensibleGlProgram.Factory<P> factory) {
|
||||
public WorldContext(Backend backend, IShaderPipeline<P> factory) {
|
||||
super(backend);
|
||||
this.factory = factory;
|
||||
this.pipeline = factory;
|
||||
|
||||
specStream = () -> backend.allMaterials()
|
||||
.stream()
|
||||
.map(MaterialSpec::getProgramName);
|
||||
|
||||
templateFactory = InstancedArraysTemplate::new;
|
||||
}
|
||||
|
||||
public WorldContext<P> withName(ResourceLocation name) {
|
||||
|
@ -50,42 +32,16 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public WorldContext<P> withBuiltin(ShaderType shaderType, ResourceLocation folder, String file) {
|
||||
return withBuiltin(shaderType, ResourceUtil.subPath(folder, file));
|
||||
}
|
||||
|
||||
public WorldContext<P> withBuiltin(ShaderType shaderType, ResourceLocation file) {
|
||||
builtins.put(shaderType, file);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WorldContext<P> withSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
|
||||
this.specStream = specStream;
|
||||
return this;
|
||||
}
|
||||
|
||||
public WorldContext<P> withTemplateFactory(TemplateFactory templateFactory) {
|
||||
this.templateFactory = templateFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
|
||||
Backend.log.info("Loading context '{}'", name);
|
||||
|
||||
try {
|
||||
builtins.forEach((type, resourceLocation) -> builtinSources.put(type, backend.sources.getShaderSource(resourceLocation)));
|
||||
} catch (ShaderLoadingException e) {
|
||||
backend.sources.notifyError();
|
||||
|
||||
Backend.log.error(String.format("Could not find builtin: %s", e.getMessage()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pipeline = new LegacyPipeline<>(backend.sources, templateFactory.create(backend.sources), factory, builtinSources);
|
||||
|
||||
specStream.get()
|
||||
.map(backend::getSpec)
|
||||
.forEach(this::loadSpec);
|
||||
|
@ -98,12 +54,9 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
|||
|
||||
Backend.log.debug("Loaded program {}", spec.name);
|
||||
} catch (Exception e) {
|
||||
Backend.log.error("Program '{}': {}", spec.name, e);
|
||||
Backend.log.error("Error loading program {}", spec.name);
|
||||
Backend.log.error("", e);
|
||||
backend.sources.notifyError();
|
||||
}
|
||||
}
|
||||
|
||||
public interface TemplateFactory {
|
||||
ProgramTemplate create(ShaderSources loader);
|
||||
}
|
||||
}
|
||||
|
|
55
src/main/java/com/jozufozu/flywheel/util/FlwUtil.java
Normal file
55
src/main/java/com/jozufozu/flywheel/util/FlwUtil.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
package com.jozufozu.flywheel.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class FlwUtil {
|
||||
|
||||
public static String repeatChar(char c, int n) {
|
||||
char[] arr = new char[n];
|
||||
|
||||
Arrays.fill(arr, c);
|
||||
|
||||
return new String(arr);
|
||||
}
|
||||
|
||||
public static int numDigits(int number) {
|
||||
// cursed but allegedly the fastest algorithm, taken from https://www.baeldung.com/java-number-of-digits-in-int
|
||||
if (number < 100000) {
|
||||
if (number < 100) {
|
||||
if (number < 10) {
|
||||
return 1;
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
} else {
|
||||
if (number < 1000) {
|
||||
return 3;
|
||||
} else {
|
||||
if (number < 10000) {
|
||||
return 4;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (number < 10000000) {
|
||||
if (number < 1000000) {
|
||||
return 6;
|
||||
} else {
|
||||
return 7;
|
||||
}
|
||||
} else {
|
||||
if (number < 100000000) {
|
||||
return 8;
|
||||
} else {
|
||||
if (number < 1000000000) {
|
||||
return 9;
|
||||
} else {
|
||||
return 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,8 +1,13 @@
|
|||
#flwbuiltins
|
||||
|
||||
#flwinclude <"flywheel:data/blockfragment.glsl">
|
||||
struct BlockFrag {
|
||||
vec2 texCoords;
|
||||
vec4 color;
|
||||
float diffuse;
|
||||
vec2 light;
|
||||
};
|
||||
|
||||
void FLWMain(BlockFrag r) {
|
||||
#if defined(FRAGMENT_SHADER)
|
||||
void fragment(BlockFrag r) {
|
||||
vec4 tex = FLWBlockTexture(r.texCoords);
|
||||
|
||||
vec4 color = vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color;
|
||||
|
@ -15,3 +20,4 @@ void FLWMain(BlockFrag r) {
|
|||
// flw_Tint = r.color;
|
||||
FLWFinalizeColor(color);
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,10 +1,31 @@
|
|||
#flwinclude <"flywheel:context/world/fog.glsl">
|
||||
#use "flywheel:context/fog.glsl"
|
||||
|
||||
uniform float uTime;
|
||||
uniform mat4 uViewProjection;
|
||||
uniform vec3 uCameraPos;
|
||||
|
||||
uniform vec2 uTextureScale;
|
||||
uniform sampler2D uBlockAtlas;
|
||||
uniform sampler2D uLightMap;
|
||||
uniform sampler2D uCrumbling;
|
||||
|
||||
uniform vec2 uWindowSize;
|
||||
|
||||
void FLWFinalizeNormal(inout vec3 normal) {
|
||||
// noop
|
||||
}
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
||||
#if defined(USE_FOG)
|
||||
FragDistance = length(worldPos.xyz - uCameraPos);
|
||||
#endif
|
||||
|
||||
gl_Position = uViewProjection * worldPos;
|
||||
}
|
||||
|
||||
#elif defined(FRAGMENT_SHADER)
|
||||
|
||||
vec4 FLWBlockTexture(vec2 texCoords) {
|
||||
vec4 cr = texture2D(uCrumbling, texCoords * uTextureScale);
|
||||
float diffuseAlpha = texture2D(uBlockAtlas, texCoords).a;
|
||||
|
@ -27,3 +48,4 @@ void FLWFinalizeColor(vec4 color) {
|
|||
vec4 FLWLight(vec2 lightCoords) {
|
||||
return vec4(1.);
|
||||
}
|
||||
#endif
|
|
@ -1 +0,0 @@
|
|||
#flwinclude <"flywheel:context/world/builtin.vert">
|
|
@ -1,10 +1,31 @@
|
|||
#flwinclude <"flywheel:context/world/fog.glsl">
|
||||
#flwinclude <"flywheel:core/lightutil.glsl">
|
||||
#use "flywheel:context/fog.glsl"
|
||||
|
||||
uniform float uTime;
|
||||
uniform mat4 uViewProjection;
|
||||
uniform vec3 uCameraPos;
|
||||
|
||||
uniform vec2 uTextureScale;
|
||||
uniform sampler2D uBlockAtlas;
|
||||
uniform sampler2D uLightMap;
|
||||
|
||||
uniform vec2 uWindowSize;
|
||||
|
||||
void FLWFinalizeNormal(inout vec3 normal) {
|
||||
// noop
|
||||
}
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
||||
#if defined(USE_FOG)
|
||||
FragDistance = length(worldPos.xyz - uCameraPos);
|
||||
#endif
|
||||
|
||||
gl_Position = uViewProjection * worldPos;
|
||||
}
|
||||
|
||||
#elif defined(FRAGMENT_SHADER)
|
||||
#use "flywheel:core/lightutil.glsl"
|
||||
|
||||
vec4 FLWBlockTexture(vec2 texCoords) {
|
||||
return texture2D(uBlockAtlas, texCoords);
|
||||
}
|
||||
|
@ -24,3 +45,4 @@ void FLWFinalizeColor(vec4 color) {
|
|||
vec4 FLWLight(vec2 lightCoords) {
|
||||
return texture2D(uLightMap, shiftLight(lightCoords));
|
||||
}
|
||||
#endif
|
|
@ -1,19 +0,0 @@
|
|||
uniform float uTime;
|
||||
uniform mat4 uViewProjection;
|
||||
uniform vec3 uCameraPos;
|
||||
|
||||
#if defined(USE_FOG)
|
||||
varying float FragDistance;
|
||||
#endif
|
||||
|
||||
void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
||||
#if defined(USE_FOG)
|
||||
FragDistance = length(worldPos.xyz - uCameraPos);
|
||||
#endif
|
||||
|
||||
gl_Position = uViewProjection * worldPos;
|
||||
}
|
||||
|
||||
void FLWFinalizeNormal(inout vec3 normal) {
|
||||
// noop
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#[Fragment]
|
||||
struct BlockFrag {
|
||||
vec2 texCoords;
|
||||
vec4 color;
|
||||
float diffuse;
|
||||
vec2 light;
|
||||
};
|
|
@ -1,4 +1,3 @@
|
|||
#[VertexData]
|
||||
struct Vertex {
|
||||
vec3 pos;
|
||||
vec3 normal;
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
struct Instance {
|
||||
vec2 light;
|
||||
vec4 color;
|
||||
mat4 transform;
|
||||
mat3 normalMat;
|
||||
};
|
||||
|
||||
struct Vertex {
|
||||
vec3 pos;
|
||||
vec3 normal;
|
||||
vec2 texCoords;
|
||||
};
|
||||
|
||||
struct BlockFrag {
|
||||
vec2 texCoords;
|
||||
vec4 color;
|
||||
float diffuse;
|
||||
vec2 light;
|
||||
};
|
||||
|
||||
BlockFrag vertex(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);
|
||||
|
||||
BlockFrag b;
|
||||
b.diffuse = diffuse(norm);
|
||||
b.texCoords = v.texCoords;
|
||||
b.light = i.light;
|
||||
#if defined(DEBUG_NORMAL)
|
||||
b.color = vec4(norm, 1.);
|
||||
#else
|
||||
b.color = i.color;
|
||||
#endif
|
||||
return b;
|
||||
}
|
||||
|
||||
void fragment(BlockFrag r) {
|
||||
vec4 tex = FLWBlockTexture(r.texCoords);
|
||||
|
||||
vec4 color = vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color;
|
||||
|
||||
// flw_WorldPos = ;
|
||||
// flw_Normal = ;
|
||||
// flw_Albedo = tex.rgb;
|
||||
// flw_Alpha = tex.a;
|
||||
// flw_LightMap = r.light;
|
||||
// flw_Tint = r.color;
|
||||
FLWFinalizeColor(color);
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
#flwbuiltins
|
||||
#flwinclude <"flywheel:core/diffuse.glsl">
|
||||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
#flwinclude <"flywheel:data/modelvertex.glsl">
|
||||
#flwinclude <"flywheel:data/blockfragment.glsl">
|
||||
#use "flywheel:data/modelvertex.glsl"
|
||||
|
||||
#use "flywheel:block.frag"
|
||||
|
||||
#[InstanceData]
|
||||
struct Instance {
|
||||
vec2 light;
|
||||
vec4 color;
|
||||
|
@ -12,7 +11,8 @@ struct Instance {
|
|||
mat3 normalMat;
|
||||
};
|
||||
|
||||
BlockFrag FLWMain(Vertex v, Instance i) {
|
||||
#if defined(VERTEX_SHADER)
|
||||
BlockFrag vertex(Vertex v, Instance i) {
|
||||
vec4 worldPos = i.transform * vec4(v.pos, 1.);
|
||||
|
||||
vec3 norm = i.normalMat * v.normal;
|
||||
|
@ -33,3 +33,4 @@ BlockFrag FLWMain(Vertex v, Instance i) {
|
|||
#endif
|
||||
return b;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#flwbuiltins
|
||||
#flwinclude <"flywheel:core/matutils.glsl">
|
||||
#flwinclude <"flywheel:core/quaternion.glsl">
|
||||
#flwinclude <"flywheel:core/diffuse.glsl">
|
||||
#use "flywheel:core/matutils.glsl"
|
||||
#use "flywheel:core/quaternion.glsl"
|
||||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
#use "flywheel:data/modelvertex.glsl"
|
||||
#use "flywheel:block.frag"
|
||||
|
||||
#[InstanceData]
|
||||
struct Oriented {
|
||||
vec2 light;
|
||||
vec4 color;
|
||||
|
@ -12,10 +13,8 @@ struct Oriented {
|
|||
vec4 rotation;
|
||||
};
|
||||
|
||||
#flwinclude <"flywheel:data/modelvertex.glsl">
|
||||
#flwinclude <"flywheel:data/blockfragment.glsl">
|
||||
|
||||
BlockFrag FLWMain(Vertex v, Oriented o) {
|
||||
#if defined(VERTEX_SHADER)
|
||||
BlockFrag vertex(Vertex v, Oriented o) {
|
||||
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
||||
|
||||
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
||||
|
@ -34,3 +33,4 @@ BlockFrag FLWMain(Vertex v, Oriented o) {
|
|||
#endif
|
||||
return b;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#flwbuiltins
|
||||
#flwinclude <"flywheel:core/matutils.glsl">
|
||||
#flwinclude <"flywheel:core/quaternion.glsl">
|
||||
#flwinclude <"flywheel:core/diffuse.glsl">
|
||||
#use "flywheel:core/matutils.glsl"
|
||||
#use "flywheel:core/quaternion.glsl"
|
||||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
#[InstanceData]
|
||||
struct Oriented {
|
||||
// each vec 4 is 2 light coords packed <lo y, hi y>
|
||||
// x z
|
||||
|
@ -21,10 +19,10 @@ struct Oriented {
|
|||
vec4 rotation;
|
||||
};
|
||||
|
||||
#flwinclude <"flywheel:data/modelvertex.glsl">
|
||||
#flwinclude <"flywheel:data/blockfragment.glsl">
|
||||
#use "flywheel:data/modelvertex.glsl"
|
||||
#use "flywheel:block.frag"
|
||||
|
||||
BlockFrag FLWMain(Vertex v, Oriented o) {
|
||||
BlockFrag vertex(Vertex v, Oriented o) {
|
||||
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
||||
|
||||
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
#version 110
|
||||
|
||||
#flwbeginbody
|
||||
|
||||
#FLWPrefixFields(Fragment, varying, v2f_)
|
||||
|
||||
//vec3 flw_WorldPos;
|
||||
//vec3 flw_Normal;
|
||||
//vec3 flw_Albedo;
|
||||
//float flw_Alpha;
|
||||
//vec2 flw_LightMap;
|
||||
//vec4 flw_Tint;
|
||||
|
||||
void main() {
|
||||
Fragment f;
|
||||
#FLWAssignFields(Fragment, f., v2f_)
|
||||
|
||||
FLWMain(f);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
#version 110
|
||||
|
||||
#flwbeginbody
|
||||
#FLWPrefixFields(VertexData, attribute, a_v_)
|
||||
#FLWPrefixFields(InstanceData, attribute, a_i_)
|
||||
|
||||
#FLWPrefixFields(Fragment, varying, v2f_)
|
||||
|
||||
void main() {
|
||||
VertexData v;
|
||||
#FLWAssignFields(VertexData, v., a_v_)
|
||||
|
||||
InstanceData i;
|
||||
#FLWAssignFields(InstanceData, i., a_i_)
|
||||
|
||||
Fragment o = FLWMain(v, i);
|
||||
|
||||
#FLWAssignFields(Fragment, v2f_, o.)
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
#version 450
|
||||
#extension GL_NV_mesh_shader : require
|
||||
|
||||
layout(local_size_x=32) in;
|
||||
|
||||
layout(max_vertices=64, max_primitives=32) out;
|
||||
|
||||
layout (std430, binding = 1) buffer _vertices {
|
||||
FLWVertexData vertices[];
|
||||
} vb;
|
||||
|
||||
struct s_meshlet {
|
||||
uint vertices[64];
|
||||
uint indices[96];
|
||||
uint vertex_count;
|
||||
uint index_count;
|
||||
};
|
||||
|
||||
layout (std430, binding = 2) buffer _meshlets {
|
||||
s_meshlet meshlets[];
|
||||
} mbuf;
|
||||
|
||||
layout (location = 0) out PerVertexData {
|
||||
vec4 color;
|
||||
} v_out[];// [max_vertices]
|
||||
|
||||
void main() {
|
||||
uint mi = gl_WorkGroupID.x;
|
||||
uint thread_id = gl_LocalInvocationID.x;
|
||||
|
||||
uint primIdx = thread_id * 3;
|
||||
uint vertStartIdx = thread_id * 2;
|
||||
|
||||
gl_MeshVerticesNV[vertStartIdx + 0].gl_Position;
|
||||
gl_MeshVerticesNV[vertStartIdx + 1].gl_Position;
|
||||
|
||||
gl_PrimitiveIndicesNV[primIdx + 0] = mbuf.meshlets[mi].indices[primIdx + 0];
|
||||
gl_PrimitiveIndicesNV[primIdx + 1] = mbuf.meshlets[mi].indices[primIdx + 1];
|
||||
gl_PrimitiveIndicesNV[primIdx + 2] = mbuf.meshlets[mi].indices[primIdx + 2];
|
||||
|
||||
gl_PrimitiveCountNV = mbuf.meshlets[mi].vertex_count / 2;
|
||||
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#version 110
|
||||
|
||||
#flwbeginbody
|
||||
|
||||
#FLWPrefixFields(Fragment, varying, v2f_)
|
||||
|
||||
void main() {
|
||||
Fragment f;
|
||||
#FLWAssignFields(Fragment, f., v2f_)
|
||||
|
||||
FLWMain(f);
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#version 110
|
||||
|
||||
#flwbeginbody
|
||||
#FLWPrefixFields(VertexData, attribute, a_v_)
|
||||
|
||||
#FLWPrefixFields(Fragment, varying, v2f_)
|
||||
|
||||
void main() {
|
||||
VertexData v;
|
||||
#FLWAssignFields(VertexData, v., a_v_)
|
||||
|
||||
Fragment o = FLWMain(v);
|
||||
|
||||
#FLWAssignFields(Fragment, v2f_, o.)
|
||||
}
|
Loading…
Add table
Reference in a new issue