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 javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
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 com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
@ -40,7 +40,17 @@ public class FileResolution {
|
||||||
try {
|
try {
|
||||||
file = sources.source(fileLoc);
|
file = sources.source(fileLoc);
|
||||||
} catch (RuntimeException error) {
|
} 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;
|
package com.jozufozu.flywheel.backend;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Supplier;
|
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.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.IMultiProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,7 @@ package com.jozufozu.flywheel.backend;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
@ -15,16 +13,11 @@ import com.google.common.collect.Lists;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
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.loading.ShaderLoadingException;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
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.crumbling.CrumblingRenderer;
|
||||||
import com.jozufozu.flywheel.core.shader.extension.IProgramExtension;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
import com.jozufozu.flywheel.event.GatherContextEvent;
|
||||||
import com.jozufozu.flywheel.util.StreamUtil;
|
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");
|
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
|
||||||
private static final Gson GSON = new GsonBuilder().create();
|
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, SourceFile> shaderSources = new HashMap<>();
|
||||||
|
|
||||||
private final Map<ResourceLocation, FileResolution> resolutions = new HashMap<>();
|
private final Map<ResourceLocation, FileResolution> resolutions = new HashMap<>();
|
||||||
|
@ -57,6 +49,8 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
||||||
private boolean shouldCrash;
|
private boolean shouldCrash;
|
||||||
private final Backend backend;
|
private final Backend backend;
|
||||||
|
|
||||||
|
public Index index;
|
||||||
|
|
||||||
public ShaderSources(Backend backend) {
|
public ShaderSources(Backend backend) {
|
||||||
this.backend = backend;
|
this.backend = backend;
|
||||||
IResourceManager manager = backend.minecraft.getResourceManager();
|
IResourceManager manager = backend.minecraft.getResourceManager();
|
||||||
|
@ -79,43 +73,24 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
||||||
return resolutions.computeIfAbsent(fileLoc, FileResolution::new);
|
return resolutions.computeIfAbsent(fileLoc, FileResolution::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public Shader source(ResourceLocation name, ShaderType type) {
|
|
||||||
return new Shader(this, type, name, getShaderSource(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void notifyError() {
|
public void notifyError() {
|
||||||
shouldCrash = true;
|
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
|
@Override
|
||||||
public void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
|
public void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
|
||||||
if (predicate.test(VanillaResourceType.SHADERS)) {
|
if (predicate.test(VanillaResourceType.SHADERS)) {
|
||||||
backend.refresh();
|
backend.refresh();
|
||||||
|
|
||||||
if (backend.gl20()) {
|
if (backend.gl20()) {
|
||||||
shaderSource.clear();
|
|
||||||
|
|
||||||
shouldCrash = false;
|
shouldCrash = false;
|
||||||
|
|
||||||
backend.clearContexts();
|
backend.clearContexts();
|
||||||
ModLoader.get()
|
ModLoader.get()
|
||||||
.postEvent(new GatherContextEvent(backend));
|
.postEvent(new GatherContextEvent(backend));
|
||||||
|
|
||||||
resolutions.clear();
|
resolutions.values().forEach(FileResolution::invalidate);
|
||||||
|
|
||||||
loadProgramSpecs(manager);
|
loadProgramSpecs(manager);
|
||||||
loadShaderSources(manager);
|
loadShaderSources(manager);
|
||||||
|
@ -124,12 +99,6 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
||||||
resolution.resolve(this);
|
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()) {
|
for (IShaderContext<?> context : backend.allContexts()) {
|
||||||
context.load();
|
context.load();
|
||||||
}
|
}
|
||||||
|
@ -140,9 +109,6 @@ public class ShaderSources implements ISelectiveResourceReloadListener {
|
||||||
|
|
||||||
Backend.log.info("Loaded all shader programs.");
|
Backend.log.info("Loaded all shader programs.");
|
||||||
|
|
||||||
// no need to hog all that memory
|
|
||||||
shaderSource.clear();
|
|
||||||
|
|
||||||
ClientWorld world = Minecraft.getInstance().level;
|
ClientWorld world = Minecraft.getInstance().level;
|
||||||
if (Backend.isFlywheelWorld(world)) {
|
if (Backend.isFlywheelWorld(world)) {
|
||||||
// TODO: looks like it might be good to have another event here
|
// 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);
|
ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR);
|
||||||
|
|
||||||
shaderSource.put(name, source);
|
|
||||||
shaderSources.put(name, new SourceFile(this, name, source));
|
shaderSources.put(name, new SourceFile(this, name, source));
|
||||||
} catch (IOException e) {
|
} 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.Backend;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||||
import com.jozufozu.flywheel.backend.loading.Program;
|
|
||||||
import com.jozufozu.flywheel.util.RenderUtil;
|
import com.jozufozu.flywheel.util.RenderUtil;
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
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.Backend;
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
||||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
|
|
|
@ -100,7 +100,15 @@ public class InstancedRenderRegistry {
|
||||||
return skipRender.getBoolean(o);
|
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;
|
private final TileEntityType<T> type;
|
||||||
|
|
||||||
|
@ -112,14 +120,14 @@ public class InstancedRenderRegistry {
|
||||||
tiles.put(type, rendererFactory);
|
tiles.put(type, rendererFactory);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileConfig<T> setSkipRender(boolean skipRender) {
|
public TileConfig<T> setSkipRender(boolean skipRender) {
|
||||||
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
|
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
|
||||||
return this;
|
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;
|
private final EntityType<T> type;
|
||||||
|
|
||||||
|
@ -131,12 +139,12 @@ public class InstancedRenderRegistry {
|
||||||
entities.put(type, rendererFactory);
|
entities.put(type, rendererFactory);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityConfig<T> setSkipRender(boolean skipRender) {
|
public EntityConfig<T> setSkipRender(boolean skipRender) {
|
||||||
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
|
InstancedRenderRegistry.this.skipRender.put(type, skipRender);
|
||||||
|
|
||||||
return this;
|
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 it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
|
||||||
public class Program {
|
public class ProtoProgram {
|
||||||
public final int program;
|
public final int program;
|
||||||
|
|
||||||
public ResourceLocation name;
|
public ResourceLocation name;
|
||||||
private int attributeIndex;
|
private int attributeIndex;
|
||||||
|
|
||||||
public final Map<ShaderType, Shader> attached;
|
|
||||||
|
|
||||||
private final IntList shaders;
|
private final IntList shaders;
|
||||||
|
|
||||||
public Program() {
|
public ProtoProgram() {
|
||||||
this.program = glCreateProgram();
|
this.program = glCreateProgram();
|
||||||
attached = new EnumMap<>(ShaderType.class);
|
|
||||||
shaders = new IntArrayList(2);
|
shaders = new IntArrayList(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program attachShader(Shader shader, GlShader glShader) {
|
public void attachShader(GlShader glShader) {
|
||||||
glAttachShader(this.program, glShader.handle());
|
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);
|
glBindAttribLocation(this.program, attributeIndex, name);
|
||||||
attributeIndex += attributeCount;
|
attributeIndex += attributeCount;
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Links the attached shaders to this program.
|
* Links the attached shaders to this program.
|
||||||
*/
|
*/
|
||||||
public Program link(ResourceLocation name) {
|
public ProtoProgram link(ResourceLocation name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
glLinkProgram(this.program);
|
glLinkProgram(this.program);
|
||||||
|
|
||||||
|
@ -75,7 +67,7 @@ public class Program {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program deleteLinkedShaders() {
|
public ProtoProgram deleteLinkedShaders() {
|
||||||
shaders.forEach((IntConsumer) GL20::glDeleteShader);
|
shaders.forEach((IntConsumer) GL20::glDeleteShader);
|
||||||
return this;
|
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;
|
package com.jozufozu.flywheel.backend.pipeline;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
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.GlShader;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||||
|
|
||||||
|
@ -11,18 +11,19 @@ import net.minecraft.util.ResourceLocation;
|
||||||
public class ShaderBuilder {
|
public class ShaderBuilder {
|
||||||
|
|
||||||
public final ResourceLocation name;
|
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 GLSLVersion version;
|
||||||
|
|
||||||
private StringBuilder source;
|
private StringBuilder source;
|
||||||
private StringBuilder defines;
|
private StringBuilder defines;
|
||||||
private CharSequence footer;
|
|
||||||
|
|
||||||
public ShaderBuilder(ResourceLocation name, Template template) {
|
public ShaderBuilder(ResourceLocation name, ITemplate template, FileResolution header) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
|
this.header = header;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderBuilder setVersion(GLSLVersion version) {
|
public ShaderBuilder setVersion(GLSLVersion version) {
|
||||||
|
@ -41,21 +42,13 @@ public class ShaderBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderBuilder setFooter(CharSequence footer) {
|
|
||||||
this.footer = footer;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderBuilder setMainSource(SourceFile file) {
|
public ShaderBuilder setMainSource(SourceFile file) {
|
||||||
if (mainFile == file) return this;
|
if (mainFile == file) return this;
|
||||||
|
|
||||||
mainFile = file;
|
mainFile = file;
|
||||||
source = new StringBuilder();
|
source = new StringBuilder();
|
||||||
|
|
||||||
for (SourceFile includeFile : Includer.recurseIncludes(file)) {
|
file.generateFinalSource(source);
|
||||||
source.append(includeFile.getElidedSource());
|
|
||||||
}
|
|
||||||
source.append(file.getElidedSource());
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -70,9 +63,13 @@ public class ShaderBuilder {
|
||||||
.append("#define ")
|
.append("#define ")
|
||||||
.append(type.define)
|
.append(type.define)
|
||||||
.append('\n')
|
.append('\n')
|
||||||
.append(defines != null ? defines : "")
|
.append(defines != null ? defines : "");
|
||||||
.append(source)
|
SourceFile file = header.getFile();
|
||||||
.append(template.footer(type, mainFile));
|
if (file != null) {
|
||||||
|
file.generateFinalSource(finalSource);
|
||||||
|
}
|
||||||
|
mainFile.generateFinalSource(finalSource);
|
||||||
|
template.generateTemplateSource(finalSource, type, mainFile);
|
||||||
|
|
||||||
return new GlShader(name, type, finalSource);
|
return new GlShader(name, type, finalSource);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline;
|
package com.jozufozu.flywheel.backend.pipeline;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
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.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.jozufozu.flywheel.backend.FileResolution;
|
||||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.parse.Import;
|
import com.jozufozu.flywheel.backend.pipeline.parse.Import;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderFunction;
|
||||||
|
@ -33,7 +40,7 @@ public class SourceFile {
|
||||||
|
|
||||||
public final ResourceLocation name;
|
public final ResourceLocation name;
|
||||||
|
|
||||||
private final ShaderSources parent;
|
public final ShaderSources parent;
|
||||||
private final String source;
|
private final String source;
|
||||||
private final CharSequence elided;
|
private final CharSequence elided;
|
||||||
private final ImmutableList<String> lines;
|
private final ImmutableList<String> lines;
|
||||||
|
@ -89,6 +96,61 @@ public class SourceFile {
|
||||||
return imports;
|
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) {
|
public CharPos getCharPos(int charPos) {
|
||||||
int lineNo = 0;
|
int lineNo = 0;
|
||||||
for (; lineNo < lineStarts.size(); lineNo++) {
|
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 org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
|
import com.jozufozu.flywheel.backend.FileResolution;
|
||||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
import com.jozufozu.flywheel.backend.ShaderSources;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
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.ExtensibleGlProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.GameStateProgram;
|
import com.jozufozu.flywheel.core.shader.GameStateProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
||||||
|
@ -24,15 +26,20 @@ import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
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 ShaderSources sources;
|
||||||
|
|
||||||
private final ExtensibleGlProgram.Factory<P> factory;
|
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.sources = sources;
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
|
this.template = template;
|
||||||
|
this.header = header;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMultiProgram<P> compile(ProgramSpec spec) {
|
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) {
|
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)
|
.setMainSource(file)
|
||||||
.setVersion(GLSLVersion.V120);
|
.setVersion(GLSLVersion.V110);
|
||||||
|
|
||||||
GameStateProgram.Builder<P> builder = GameStateProgram.builder(compile(shader, name, null));
|
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 vertex = shader.compile(name, ShaderType.VERTEX);
|
||||||
GlShader fragment = shader.compile(name, ShaderType.FRAGMENT);
|
GlShader fragment = shader.compile(name, ShaderType.FRAGMENT);
|
||||||
|
|
||||||
int program = GL20.glCreateProgram();
|
ProtoProgram program = new ProtoProgram();
|
||||||
|
|
||||||
GL20.glAttachShader(program, vertex.handle());
|
program.attachShader(vertex);
|
||||||
GL20.glAttachShader(program, fragment.handle());
|
program.attachShader(fragment);
|
||||||
|
|
||||||
String log = glGetProgramInfoLog(program);
|
template.attachAttributes(program, shader.mainFile);
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
program.link(name);
|
||||||
Backend.log.debug("Program link log for " + name + ": " + log);
|
program.deleteLinkedShaders();
|
||||||
}
|
|
||||||
|
|
||||||
int result = glGetProgrami(program, GL_LINK_STATUS);
|
|
||||||
|
|
||||||
if (result != GL_TRUE) {
|
|
||||||
throw new RuntimeException("Shader program linking failed, see log for details");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (variant != null) {
|
if (variant != null) {
|
||||||
return factory.create(name, program, variant.getExtensions());
|
return factory.create(name, program.program, variant.getExtensions());
|
||||||
} else {
|
} 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;
|
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.SourceFile;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||||
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
|
||||||
public class ErrorBuilder {
|
public class ErrorBuilder {
|
||||||
|
|
||||||
private final StringBuilder internal = new StringBuilder();
|
private final StringBuilder internal = new StringBuilder();
|
||||||
|
|
||||||
public ErrorBuilder header(CharSequence msg) {
|
public ErrorBuilder error(CharSequence msg) {
|
||||||
internal.append("error: ")
|
internal.append("error: ")
|
||||||
.append(msg);
|
.append(msg);
|
||||||
return endLine();
|
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("--> ")
|
internal.append("--> ")
|
||||||
.append(file.name);
|
.append(file.name);
|
||||||
return endLine();
|
return endLine();
|
||||||
|
@ -37,7 +54,18 @@ public class ErrorBuilder {
|
||||||
return this;
|
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) {
|
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
||||||
|
|
||||||
SourceFile file = span.getSourceFile();
|
SourceFile file = span.getSourceFile();
|
||||||
|
|
||||||
if (span.lines() == 1) {
|
if (span.lines() == 1) {
|
||||||
|
@ -47,6 +75,8 @@ public class ErrorBuilder {
|
||||||
int firstLine = Math.max(0, spanLine - ctxLines);
|
int firstLine = Math.max(0, spanLine - ctxLines);
|
||||||
int lastLine = Math.min(file.getLineCount(), spanLine + ctxLines);
|
int lastLine = Math.min(file.getLineCount(), spanLine + ctxLines);
|
||||||
|
|
||||||
|
int digits = FlwUtil.numDigits(lastLine);
|
||||||
|
|
||||||
|
|
||||||
int firstCol = span.getStart().getCol();
|
int firstCol = span.getStart().getCol();
|
||||||
int lastCol = span.getEnd().getCol();
|
int lastCol = span.getEnd().getCol();
|
||||||
|
@ -57,7 +87,7 @@ public class ErrorBuilder {
|
||||||
numberedLine(i + 1, line);
|
numberedLine(i + 1, line);
|
||||||
|
|
||||||
if (i == spanLine) {
|
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;
|
package com.jozufozu.flywheel.backend.pipeline.error;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
import com.jozufozu.flywheel.backend.pipeline.SourceFile;
|
||||||
|
import com.jozufozu.flywheel.backend.pipeline.parse.ShaderStruct;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||||
|
|
||||||
public class ErrorReporter {
|
public class ErrorReporter {
|
||||||
|
@ -11,11 +14,37 @@ public class ErrorReporter {
|
||||||
|
|
||||||
ErrorBuilder builder = new ErrorBuilder();
|
ErrorBuilder builder = new ErrorBuilder();
|
||||||
|
|
||||||
CharSequence error = builder.header(message)
|
CharSequence error = builder.error(message)
|
||||||
.errorIn(file)
|
.in(file)
|
||||||
.pointAt(span, 2)
|
.pointAt(span, 2)
|
||||||
.build();
|
.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.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@ -43,6 +44,14 @@ public class Import extends AbstractShaderElement {
|
||||||
return new ResourceLocation("");
|
return new ResourceLocation("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FileResolution getResolution() {
|
||||||
|
return resolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<SourceFile> getOptional() {
|
||||||
|
return Optional.ofNullable(resolution.getFile());
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public SourceFile getFile() {
|
public SourceFile getFile() {
|
||||||
return resolution.getFile();
|
return resolution.getFile();
|
||||||
|
|
|
@ -29,6 +29,22 @@ public class ShaderFunction extends AbstractShaderElement {
|
||||||
this.parameters = parseArguments();
|
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) {
|
public String call(String... args) {
|
||||||
return name + "(" + String.join(", ", args) + ")";
|
return name + "(" + String.join(", ", args) + ")";
|
||||||
}
|
}
|
||||||
|
@ -37,7 +53,7 @@ public class ShaderFunction extends AbstractShaderElement {
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String returnType() {
|
public String returnTypeName() {
|
||||||
return type.get();
|
return type.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
package com.jozufozu.flywheel.backend.pipeline.parse;
|
package com.jozufozu.flywheel.backend.pipeline.parse;
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
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.loading.TypeHelper;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.error.ErrorReporter;
|
|
||||||
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
import com.jozufozu.flywheel.backend.pipeline.span.Span;
|
||||||
|
|
||||||
public class ShaderStruct extends AbstractShaderElement {
|
public class ShaderStruct extends AbstractShaderElement {
|
||||||
|
@ -30,6 +28,14 @@ public class ShaderStruct extends AbstractShaderElement {
|
||||||
this.fields2Types = createTypeLookup();
|
this.fields2Types = createTypeLookup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Span getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Span getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
public ImmutableList<StructField> getFields() {
|
public ImmutableList<StructField> getFields() {
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +65,7 @@ public class ShaderStruct extends AbstractShaderElement {
|
||||||
return fields.build();
|
return fields.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPrefixedAttributes(Program builder, String prefix) {
|
public void addPrefixedAttributes(ProtoProgram builder, String prefix) {
|
||||||
for (StructField field : fields) {
|
for (StructField field : fields) {
|
||||||
int attributeCount = TypeHelper.getAttributeCount(field.type);
|
int attributeCount = TypeHelper.getAttributeCount(field.type);
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
package com.jozufozu.flywheel.core;
|
package com.jozufozu.flywheel.core;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
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.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.crumbling.CrumblingProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldFog;
|
import com.jozufozu.flywheel.core.shader.WorldFog;
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
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.FogStateProvider;
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider;
|
import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider;
|
||||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
import com.jozufozu.flywheel.event.GatherContextEvent;
|
||||||
|
@ -33,13 +34,14 @@ public class Contexts {
|
||||||
SpecMetaRegistry.register(WorldFog.LINEAR);
|
SpecMetaRegistry.register(WorldFog.LINEAR);
|
||||||
SpecMetaRegistry.register(WorldFog.EXP2);
|
SpecMetaRegistry.register(WorldFog.EXP2);
|
||||||
|
|
||||||
CRUMBLING = backend.register(new WorldContext<>(backend, CrumblingProgram::new).withName(Names.CRUMBLING)
|
FileResolution crumblingBuiltins = backend.sources.resolveFile(ResourceUtil.subPath(Names.CRUMBLING, ".glsl"));
|
||||||
.withBuiltin(ShaderType.FRAGMENT, Names.CRUMBLING, "/builtin.frag")
|
FileResolution worldBuiltins = backend.sources.resolveFile(ResourceUtil.subPath(Names.WORLD, ".glsl"));
|
||||||
.withBuiltin(ShaderType.VERTEX, Names.CRUMBLING, "/builtin.vert"));
|
|
||||||
|
|
||||||
WORLD = backend.register(new WorldContext<>(backend, WorldProgram::new).withName(Names.WORLD)
|
IShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(backend.sources, CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins);
|
||||||
.withBuiltin(ShaderType.FRAGMENT, Names.WORLD, "/builtin.frag")
|
IShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(backend.sources, WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins);
|
||||||
.withBuiltin(ShaderType.VERTEX, Names.WORLD, "/builtin.vert"));
|
|
||||||
|
CRUMBLING = backend.register(new WorldContext<>(backend, crumblingPipeline).withName(Names.CRUMBLING));
|
||||||
|
WORLD = backend.register(new WorldContext<>(backend, worldPipeline).withName(Names.WORLD));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Names {
|
public static class Names {
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
package com.jozufozu.flywheel.core;
|
package com.jozufozu.flywheel.core;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
import com.jozufozu.flywheel.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.ResourceUtil;
|
|
||||||
import com.jozufozu.flywheel.backend.ShaderContext;
|
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.material.MaterialSpec;
|
||||||
import com.jozufozu.flywheel.backend.pipeline.IShaderPipeline;
|
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.WorldProgram;
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
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> {
|
public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||||
protected ResourceLocation name;
|
protected ResourceLocation name;
|
||||||
protected Supplier<Stream<ResourceLocation>> specStream;
|
protected Supplier<Stream<ResourceLocation>> specStream;
|
||||||
protected TemplateFactory templateFactory;
|
|
||||||
|
|
||||||
private final Map<ShaderType, ResourceLocation> builtins = new EnumMap<>(ShaderType.class);
|
public final IShaderPipeline<P> pipeline;
|
||||||
private final Map<ShaderType, String> builtinSources = new EnumMap<>(ShaderType.class);
|
|
||||||
|
|
||||||
private final ExtensibleGlProgram.Factory<P> factory;
|
public WorldContext(Backend backend, IShaderPipeline<P> factory) {
|
||||||
|
|
||||||
public IShaderPipeline<P> pipeline;
|
|
||||||
|
|
||||||
public WorldContext(Backend backend, ExtensibleGlProgram.Factory<P> factory) {
|
|
||||||
super(backend);
|
super(backend);
|
||||||
this.factory = factory;
|
this.pipeline = factory;
|
||||||
|
|
||||||
specStream = () -> backend.allMaterials()
|
specStream = () -> backend.allMaterials()
|
||||||
.stream()
|
.stream()
|
||||||
.map(MaterialSpec::getProgramName);
|
.map(MaterialSpec::getProgramName);
|
||||||
|
|
||||||
templateFactory = InstancedArraysTemplate::new;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldContext<P> withName(ResourceLocation name) {
|
public WorldContext<P> withName(ResourceLocation name) {
|
||||||
|
@ -50,42 +32,16 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||||
return this;
|
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) {
|
public WorldContext<P> withSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
|
||||||
this.specStream = specStream;
|
this.specStream = specStream;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldContext<P> withTemplateFactory(TemplateFactory templateFactory) {
|
|
||||||
this.templateFactory = templateFactory;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
|
|
||||||
Backend.log.info("Loading context '{}'", name);
|
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()
|
specStream.get()
|
||||||
.map(backend::getSpec)
|
.map(backend::getSpec)
|
||||||
.forEach(this::loadSpec);
|
.forEach(this::loadSpec);
|
||||||
|
@ -98,12 +54,9 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
||||||
|
|
||||||
Backend.log.debug("Loaded program {}", spec.name);
|
Backend.log.debug("Loaded program {}", spec.name);
|
||||||
} catch (Exception e) {
|
} 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();
|
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 tex = FLWBlockTexture(r.texCoords);
|
||||||
|
|
||||||
vec4 color = vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color;
|
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;
|
// flw_Tint = r.color;
|
||||||
FLWFinalizeColor(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 vec2 uTextureScale;
|
||||||
uniform sampler2D uBlockAtlas;
|
uniform sampler2D uBlockAtlas;
|
||||||
uniform sampler2D uLightMap;
|
uniform sampler2D uLightMap;
|
||||||
uniform sampler2D uCrumbling;
|
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 FLWBlockTexture(vec2 texCoords) {
|
||||||
vec4 cr = texture2D(uCrumbling, texCoords * uTextureScale);
|
vec4 cr = texture2D(uCrumbling, texCoords * uTextureScale);
|
||||||
float diffuseAlpha = texture2D(uBlockAtlas, texCoords).a;
|
float diffuseAlpha = texture2D(uBlockAtlas, texCoords).a;
|
||||||
|
@ -27,3 +48,4 @@ void FLWFinalizeColor(vec4 color) {
|
||||||
vec4 FLWLight(vec2 lightCoords) {
|
vec4 FLWLight(vec2 lightCoords) {
|
||||||
return vec4(1.);
|
return vec4(1.);
|
||||||
}
|
}
|
||||||
|
#endif
|
|
@ -1 +0,0 @@
|
||||||
#flwinclude <"flywheel:context/world/builtin.vert">
|
|
|
@ -1,10 +1,31 @@
|
||||||
#flwinclude <"flywheel:context/world/fog.glsl">
|
#use "flywheel:context/fog.glsl"
|
||||||
#flwinclude <"flywheel:core/lightutil.glsl">
|
|
||||||
|
|
||||||
|
uniform float uTime;
|
||||||
|
uniform mat4 uViewProjection;
|
||||||
|
uniform vec3 uCameraPos;
|
||||||
|
|
||||||
|
uniform vec2 uTextureScale;
|
||||||
uniform sampler2D uBlockAtlas;
|
uniform sampler2D uBlockAtlas;
|
||||||
uniform sampler2D uLightMap;
|
uniform sampler2D uLightMap;
|
||||||
|
|
||||||
uniform vec2 uWindowSize;
|
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) {
|
vec4 FLWBlockTexture(vec2 texCoords) {
|
||||||
return texture2D(uBlockAtlas, texCoords);
|
return texture2D(uBlockAtlas, texCoords);
|
||||||
}
|
}
|
||||||
|
@ -24,3 +45,4 @@ void FLWFinalizeColor(vec4 color) {
|
||||||
vec4 FLWLight(vec2 lightCoords) {
|
vec4 FLWLight(vec2 lightCoords) {
|
||||||
return texture2D(uLightMap, shiftLight(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 {
|
struct Vertex {
|
||||||
vec3 pos;
|
vec3 pos;
|
||||||
vec3 normal;
|
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
|
#use "flywheel:core/diffuse.glsl"
|
||||||
#flwinclude <"flywheel:core/diffuse.glsl">
|
|
||||||
|
|
||||||
#flwinclude <"flywheel:data/modelvertex.glsl">
|
#use "flywheel:data/modelvertex.glsl"
|
||||||
#flwinclude <"flywheel:data/blockfragment.glsl">
|
|
||||||
|
#use "flywheel:block.frag"
|
||||||
|
|
||||||
#[InstanceData]
|
|
||||||
struct Instance {
|
struct Instance {
|
||||||
vec2 light;
|
vec2 light;
|
||||||
vec4 color;
|
vec4 color;
|
||||||
|
@ -12,7 +11,8 @@ struct Instance {
|
||||||
mat3 normalMat;
|
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.);
|
vec4 worldPos = i.transform * vec4(v.pos, 1.);
|
||||||
|
|
||||||
vec3 norm = i.normalMat * v.normal;
|
vec3 norm = i.normalMat * v.normal;
|
||||||
|
@ -33,3 +33,4 @@ BlockFrag FLWMain(Vertex v, Instance i) {
|
||||||
#endif
|
#endif
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#flwbuiltins
|
#use "flywheel:core/matutils.glsl"
|
||||||
#flwinclude <"flywheel:core/matutils.glsl">
|
#use "flywheel:core/quaternion.glsl"
|
||||||
#flwinclude <"flywheel:core/quaternion.glsl">
|
#use "flywheel:core/diffuse.glsl"
|
||||||
#flwinclude <"flywheel:core/diffuse.glsl">
|
|
||||||
|
#use "flywheel:data/modelvertex.glsl"
|
||||||
|
#use "flywheel:block.frag"
|
||||||
|
|
||||||
#[InstanceData]
|
|
||||||
struct Oriented {
|
struct Oriented {
|
||||||
vec2 light;
|
vec2 light;
|
||||||
vec4 color;
|
vec4 color;
|
||||||
|
@ -12,10 +13,8 @@ struct Oriented {
|
||||||
vec4 rotation;
|
vec4 rotation;
|
||||||
};
|
};
|
||||||
|
|
||||||
#flwinclude <"flywheel:data/modelvertex.glsl">
|
#if defined(VERTEX_SHADER)
|
||||||
#flwinclude <"flywheel:data/blockfragment.glsl">
|
BlockFrag vertex(Vertex v, Oriented o) {
|
||||||
|
|
||||||
BlockFrag FLWMain(Vertex v, Oriented o) {
|
|
||||||
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
||||||
|
|
||||||
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
||||||
|
@ -34,3 +33,4 @@ BlockFrag FLWMain(Vertex v, Oriented o) {
|
||||||
#endif
|
#endif
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
#flwbuiltins
|
#use "flywheel:core/matutils.glsl"
|
||||||
#flwinclude <"flywheel:core/matutils.glsl">
|
#use "flywheel:core/quaternion.glsl"
|
||||||
#flwinclude <"flywheel:core/quaternion.glsl">
|
#use "flywheel:core/diffuse.glsl"
|
||||||
#flwinclude <"flywheel:core/diffuse.glsl">
|
|
||||||
|
|
||||||
#[InstanceData]
|
|
||||||
struct Oriented {
|
struct Oriented {
|
||||||
// each vec 4 is 2 light coords packed <lo y, hi y>
|
// each vec 4 is 2 light coords packed <lo y, hi y>
|
||||||
// x z
|
// x z
|
||||||
|
@ -21,10 +19,10 @@ struct Oriented {
|
||||||
vec4 rotation;
|
vec4 rotation;
|
||||||
};
|
};
|
||||||
|
|
||||||
#flwinclude <"flywheel:data/modelvertex.glsl">
|
#use "flywheel:data/modelvertex.glsl"
|
||||||
#flwinclude <"flywheel:data/blockfragment.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.);
|
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
||||||
|
|
||||||
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
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