mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-26 15:06:28 +01:00
Shader sanity
- Drastically lower shader boilerplate for instance materials - Somewhat lower boilerplate for contexts - VertexTypes are responsible for shader headers - Better shader compiler errors (at least on nvidia) - Simplify template classes - Begin work on lazy shader compilation
This commit is contained in:
parent
b7e1aa581d
commit
c265a17f55
55 changed files with 813 additions and 666 deletions
|
@ -0,0 +1,15 @@
|
|||
package com.jozufozu.flywheel.api.shader;
|
||||
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
|
||||
/**
|
||||
* Represents a vertex format agnostic shader.
|
||||
*/
|
||||
public interface FlexibleShader<P extends GlProgram> {
|
||||
|
||||
/**
|
||||
* Get a version of this shader that accepts the given VertexType as input.
|
||||
*/
|
||||
P get(VertexType type);
|
||||
}
|
|
@ -39,4 +39,6 @@ public interface VertexType {
|
|||
default int byteOffset(int vertexIndex) {
|
||||
return getStride() * vertexIndex;
|
||||
}
|
||||
|
||||
String writeShaderHeader();
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ public class Backend {
|
|||
|
||||
enabled = switch (engine) {
|
||||
case OFF -> false;
|
||||
case BATCHING -> !usingShaders;
|
||||
case BATCHING -> true;
|
||||
case INSTANCING -> !usingShaders && compat.instancedArraysSupported();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
package com.jozufozu.flywheel.backend;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public interface ShaderContext<P extends GlProgram> {
|
||||
|
||||
default P getProgram(ResourceLocation loc) {
|
||||
default P getProgram(ResourceLocation loc, VertexType inputType) {
|
||||
return this.getProgramSupplier(loc)
|
||||
.get();
|
||||
.get(inputType);
|
||||
}
|
||||
|
||||
Supplier<P> getProgramSupplier(ResourceLocation loc);
|
||||
FlexibleShader<P> getProgramSupplier(ResourceLocation loc);
|
||||
|
||||
/**
|
||||
* Load all programs associated with this context. This might be just one, if the context is very specialized.
|
||||
|
|
|
@ -26,15 +26,15 @@ public enum GlNumericType {
|
|||
|
||||
private static final GlNumericType[] VALUES = values();
|
||||
private static final Map<String, GlNumericType> NAME_LOOKUP = Arrays.stream(VALUES)
|
||||
.collect(Collectors.toMap(GlNumericType::getDisplayName, type -> type));
|
||||
.collect(Collectors.toMap(GlNumericType::getTypeName, type -> type));
|
||||
|
||||
private final int byteWidth;
|
||||
private final String displayName;
|
||||
private final String typeName;
|
||||
private final int glEnum;
|
||||
|
||||
GlNumericType(int bytes, String name, int glEnum) {
|
||||
this.byteWidth = bytes;
|
||||
this.displayName = name;
|
||||
this.typeName = name;
|
||||
this.glEnum = glEnum;
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@ public enum GlNumericType {
|
|||
return this.byteWidth;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return this.displayName;
|
||||
public String getTypeName() {
|
||||
return this.typeName;
|
||||
}
|
||||
|
||||
public int getGlEnum() {
|
||||
|
@ -64,4 +64,9 @@ public enum GlNumericType {
|
|||
public static GlNumericType byName(String name) {
|
||||
return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return typeName;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ public class GlVertexArray extends GlObject {
|
|||
int offset = 0;
|
||||
for (LayoutItem spec : type.getLayoutItems()) {
|
||||
spec.vertexAttribPointer(type.getStride(), startIndex, offset);
|
||||
startIndex += spec.getAttributeCount();
|
||||
offset += spec.getSize();
|
||||
startIndex += spec.attributeCount();
|
||||
offset += spec.size();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
package com.jozufozu.flywheel.backend.gl.shader;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
||||
import com.jozufozu.flywheel.backend.pipeline.ShaderCompiler;
|
||||
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
||||
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
|
||||
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
|
@ -13,8 +19,8 @@ public class GlShader extends GlObject {
|
|||
public final ResourceLocation name;
|
||||
public final ShaderType type;
|
||||
|
||||
public GlShader(ResourceLocation name, ShaderType type, CharSequence source) {
|
||||
this.name = name;
|
||||
public GlShader(ShaderCompiler env, ShaderType type, String source) {
|
||||
name = env.name;
|
||||
this.type = type;
|
||||
int handle = GL20.glCreateShader(type.glEnum);
|
||||
|
||||
|
@ -24,13 +30,31 @@ public class GlShader extends GlObject {
|
|||
String log = GL20.glGetShaderInfoLog(handle);
|
||||
|
||||
if (!log.isEmpty()) {
|
||||
Backend.log.error("Shader compilation log for " + name + ": " + log);
|
||||
Backend.log.error(source);
|
||||
List<String> lines = log.lines()
|
||||
.toList();
|
||||
|
||||
boolean needsSourceDump = false;
|
||||
|
||||
StringBuilder errors = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
ErrorBuilder builder = env.parseCompilerError(line);
|
||||
|
||||
if (builder != null) {
|
||||
errors.append(builder.build());
|
||||
} else {
|
||||
errors.append(line).append('\n');
|
||||
needsSourceDump = true;
|
||||
}
|
||||
}
|
||||
Backend.log.error("Errors compiling '" + name + "': \n" + errors);
|
||||
if (needsSourceDump) {
|
||||
// TODO: generated code gets its own "file"
|
||||
ErrorReporter.printLines(source);
|
||||
}
|
||||
}
|
||||
//Backend.log.debug(shader.printSource());
|
||||
|
||||
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
|
||||
throw new RuntimeException("Could not compile " + name + ". See log for details.");
|
||||
throw new ShaderLoadingException("Could not compile " + name + ". See log for details.");
|
||||
}
|
||||
|
||||
setHandle(handle);
|
||||
|
|
|
@ -1,20 +1,17 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.jozufozu.flywheel.api.InstanceData;
|
||||
import com.jozufozu.flywheel.api.Instancer;
|
||||
import com.jozufozu.flywheel.api.Material;
|
||||
import com.jozufozu.flywheel.api.struct.Instanced;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.RenderWork;
|
||||
import com.jozufozu.flywheel.backend.model.ImmediateAllocator;
|
||||
import com.jozufozu.flywheel.backend.model.ModelAllocator;
|
||||
import com.jozufozu.flywheel.backend.model.ModelPool;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.core.model.Model;
|
||||
|
||||
/**
|
||||
|
@ -23,24 +20,14 @@ import com.jozufozu.flywheel.core.model.Model;
|
|||
*/
|
||||
public class InstancedMaterial<D extends InstanceData> implements Material<D> {
|
||||
|
||||
final ModelAllocator allocator;
|
||||
protected final Cache<Object, GPUInstancer<D>> models;
|
||||
protected final ModelAllocator allocator;
|
||||
protected final Map<Object, GPUInstancer<D>> models = new HashMap<>();
|
||||
protected final Instanced<D> type;
|
||||
protected final List<GPUInstancer<D>> uninitialized = new ArrayList<>();
|
||||
|
||||
public InstancedMaterial(Instanced<D> type) {
|
||||
public InstancedMaterial(Instanced<D> type, ModelAllocator allocator) {
|
||||
this.type = type;
|
||||
|
||||
if (Backend.getInstance().compat.onAMDWindows()) {
|
||||
allocator = ImmediateAllocator.INSTANCE;
|
||||
} else {
|
||||
allocator = new ModelPool(Formats.POS_TEX_NORMAL, 64);
|
||||
}
|
||||
this.models = CacheBuilder.newBuilder()
|
||||
.removalListener(notification -> {
|
||||
GPUInstancer<?> instancer = (GPUInstancer<?>) notification.getValue();
|
||||
RenderWork.enqueue(instancer::delete);
|
||||
})
|
||||
.build();
|
||||
this.allocator = allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,32 +39,33 @@ public class InstancedMaterial<D extends InstanceData> implements Material<D> {
|
|||
*/
|
||||
@Override
|
||||
public Instancer<D> model(Object key, Supplier<Model> modelSupplier) {
|
||||
try {
|
||||
return models.get(key, () -> new GPUInstancer<>(type, modelSupplier.get(), allocator));
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException("error creating instancer", e);
|
||||
}
|
||||
return models.computeIfAbsent(key, $ -> {
|
||||
GPUInstancer<D> instancer = new GPUInstancer<>(type, modelSupplier.get(), allocator);
|
||||
uninitialized.add(instancer);
|
||||
return instancer;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean nothingToRender() {
|
||||
return models.size() > 0 && models.asMap()
|
||||
.values()
|
||||
return models.size() > 0 && models.values()
|
||||
.stream()
|
||||
.allMatch(GPUInstancer::isEmpty);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
models.invalidateAll();
|
||||
if (allocator instanceof ModelPool pool) pool.delete();
|
||||
models.values().forEach(GPUInstancer::delete);
|
||||
models.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all instance data without freeing resources.
|
||||
*/
|
||||
public void clear() {
|
||||
models.asMap()
|
||||
.values()
|
||||
models.values()
|
||||
.forEach(GPUInstancer::clear);
|
||||
}
|
||||
|
||||
public Collection<GPUInstancer<D>> getAllInstancers() {
|
||||
return models.values();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.instancing;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -8,7 +7,11 @@ import com.jozufozu.flywheel.api.InstanceData;
|
|||
import com.jozufozu.flywheel.api.MaterialGroup;
|
||||
import com.jozufozu.flywheel.api.struct.Instanced;
|
||||
import com.jozufozu.flywheel.api.struct.StructType;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.model.ImmediateAllocator;
|
||||
import com.jozufozu.flywheel.backend.model.ModelAllocator;
|
||||
import com.jozufozu.flywheel.backend.model.ModelPool;
|
||||
import com.jozufozu.flywheel.core.Formats;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.util.Textures;
|
||||
import com.mojang.math.Matrix4f;
|
||||
|
@ -27,17 +30,23 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
|
|||
protected final RenderType type;
|
||||
|
||||
private final Map<Instanced<? extends InstanceData>, InstancedMaterial<?>> materials = new HashMap<>();
|
||||
private final ModelAllocator allocator;
|
||||
|
||||
public InstancedMaterialGroup(InstancingEngine<P> owner, RenderType type) {
|
||||
this.owner = owner;
|
||||
this.type = type;
|
||||
if (Backend.getInstance().compat.onAMDWindows()) {
|
||||
this.allocator = ImmediateAllocator.INSTANCE;
|
||||
} else {
|
||||
this.allocator = new ModelPool(Formats.POS_TEX_NORMAL, 2048);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <D extends InstanceData> InstancedMaterial<D> material(StructType<D> type) {
|
||||
if (type instanceof Instanced<D> instanced) {
|
||||
return (InstancedMaterial<D>) materials.computeIfAbsent(instanced, InstancedMaterial::new);
|
||||
return (InstancedMaterial<D>) materials.computeIfAbsent(instanced, t -> new InstancedMaterial<>(t, allocator));
|
||||
} else {
|
||||
throw new ClassCastException("Cannot use type '" + type + "' with GPU instancing.");
|
||||
}
|
||||
|
@ -51,25 +60,25 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
|
|||
}
|
||||
|
||||
protected void renderAll(Matrix4f viewProjection, double camX, double camY, double camZ) {
|
||||
// initialize all uninitialized instancers...
|
||||
for (InstancedMaterial<?> material : materials.values()) {
|
||||
for (GPUInstancer<?> instancer : material.uninitialized) {
|
||||
instancer.init();
|
||||
}
|
||||
material.uninitialized.clear();
|
||||
}
|
||||
|
||||
if (allocator instanceof ModelPool pool) {
|
||||
// ...and then flush the model arena in case anything was marked for upload
|
||||
pool.flush();
|
||||
}
|
||||
|
||||
for (Map.Entry<Instanced<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
|
||||
InstancedMaterial<?> material = entry.getValue();
|
||||
if (material.nothingToRender()) continue;
|
||||
|
||||
Collection<? extends GPUInstancer<?>> instancers = material.models.asMap()
|
||||
.values();
|
||||
|
||||
// initialize all uninitialized instancers...
|
||||
for (GPUInstancer<?> gpuInstancer : instancers) {
|
||||
gpuInstancer.init();
|
||||
}
|
||||
|
||||
if (material.allocator instanceof ModelPool pool) {
|
||||
// ...and then flush the model arena in case anything was marked for upload
|
||||
pool.flush();
|
||||
}
|
||||
|
||||
P program = owner.getProgram(entry.getKey()
|
||||
.getProgramSpec()).get();
|
||||
P program = owner.context.getProgram(entry.getKey()
|
||||
.getProgramSpec(), Formats.POS_TEX_NORMAL);
|
||||
|
||||
program.bind();
|
||||
program.uploadViewProjection(viewProjection);
|
||||
|
@ -77,7 +86,7 @@ public class InstancedMaterialGroup<P extends WorldProgram> implements MaterialG
|
|||
|
||||
setup(program);
|
||||
|
||||
for (GPUInstancer<?> instancer : instancers) {
|
||||
for (GPUInstancer<?> instancer : material.getAllInstancers()) {
|
||||
instancer.render();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,17 +3,17 @@ package com.jozufozu.flywheel.backend.instancing.instancing;
|
|||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.MaterialGroup;
|
||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
||||
import com.jozufozu.flywheel.backend.RenderLayer;
|
||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
||||
import com.jozufozu.flywheel.backend.instancing.Engine;
|
||||
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
|
||||
import com.jozufozu.flywheel.core.WorldContext;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.event.RenderLayerEvent;
|
||||
|
@ -126,7 +126,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
|
|||
}
|
||||
}
|
||||
|
||||
public Supplier<P> getProgram(ResourceLocation name) {
|
||||
public FlexibleShader<P> getProgram(ResourceLocation name) {
|
||||
return context.getProgramSupplier(name);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
|
||||
public class InstancingTemplate extends Template<InstancingProgramMetaData> {
|
||||
|
||||
public static final InstancingTemplate INSTANCE = new InstancingTemplate();
|
||||
|
||||
public InstancingTemplate() {
|
||||
super(InstancingProgramMetaData::new);
|
||||
}
|
||||
|
||||
@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 Collection<ShaderInput> getShaderInputs(SourceFile file) {
|
||||
InstancingProgramMetaData data = getMetadata(file);
|
||||
|
||||
List<ShaderInput> inputs = new ArrayList<>(ShaderInput.fromStruct(data.vertex, "a_v_"));
|
||||
inputs.addAll(ShaderInput.fromStruct(data.instance, "a_i_"));
|
||||
|
||||
return inputs;
|
||||
}
|
||||
|
||||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||
InstancingProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.vertex, "in", "a_v_");
|
||||
Template.prefixFields(template, data.instance, "in", "a_i_");
|
||||
Template.prefixFields(template, data.interpolant, "out", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.vertexName)
|
||||
.append(" v;\n");
|
||||
Template.assignFields(template, data.vertex, "v.", "a_v_");
|
||||
|
||||
template.append(data.instanceName)
|
||||
.append(" i;\n");
|
||||
Template.assignFields(template, data.instance, "i.", "a_i_");
|
||||
|
||||
template.append(data.interpolantName)
|
||||
.append(" o = ")
|
||||
.append(data.vertexMain.call("v", "i"))
|
||||
.append(";\n");
|
||||
|
||||
Template.assignFields(template, data.interpolant, "v2f_", "o.");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
|
||||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||
InstancingProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.interpolant, "in", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.interpolantName)
|
||||
.append(" o;\n");
|
||||
Template.assignFields(template, data.interpolant, "o.", "v2f_");
|
||||
|
||||
template.append(data.fragmentMain.call("o"))
|
||||
.append(";\n");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
}
|
|
@ -3,15 +3,17 @@ package com.jozufozu.flywheel.backend.pipeline;
|
|||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.source.parse.StructField;
|
||||
import com.jozufozu.flywheel.backend.source.parse.Variable;
|
||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||
|
||||
public class InstancingProgramMetaData {
|
||||
public class InstancingTemplateData implements TemplateData {
|
||||
|
||||
public final SourceFile file;
|
||||
public final ShaderFunction vertexMain;
|
||||
|
@ -20,10 +22,9 @@ public class InstancingProgramMetaData {
|
|||
public final Span vertexName;
|
||||
public final Span instanceName;
|
||||
public final ShaderStruct interpolant;
|
||||
public final ShaderStruct vertex;
|
||||
public final ShaderStruct instance;
|
||||
|
||||
public InstancingProgramMetaData(SourceFile file) {
|
||||
public InstancingTemplateData(SourceFile file) {
|
||||
this.file = file;
|
||||
|
||||
Optional<ShaderFunction> vertexFunc = file.findFunction("vertex");
|
||||
|
@ -54,20 +55,25 @@ public class InstancingProgramMetaData {
|
|||
throw new ShaderLoadingException();
|
||||
}
|
||||
|
||||
interpolantName = vertexMain.getType();
|
||||
vertexName = vertexParams.get(0)
|
||||
.typeName();
|
||||
instanceName = vertexParams.get(1)
|
||||
.typeName();
|
||||
Variable vertexParam = vertexParams.get(0);
|
||||
vertexName = vertexParam.type;
|
||||
|
||||
boolean namedVertex = vertexParam.type
|
||||
.toString()
|
||||
.equals("Vertex");
|
||||
|
||||
|
||||
if (!(namedVertex && vertexParam.qualifier == Variable.Qualifier.INOUT)) {
|
||||
ErrorReporter.generateSpanError(vertexParam.qualifierSpan, "first parameter must be inout Vertex");
|
||||
throw new ShaderLoadingException();
|
||||
}
|
||||
|
||||
interpolantName = parameters.get(0).type;
|
||||
instanceName = vertexParams.get(1).type;
|
||||
|
||||
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
|
||||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||
Optional<ShaderStruct> maybeInstance = file.findStruct(instanceName);
|
||||
|
||||
if (maybeVertex.isEmpty()) {
|
||||
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
||||
}
|
||||
|
||||
if (maybeInterpolant.isEmpty()) {
|
||||
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
||||
}
|
||||
|
@ -76,12 +82,74 @@ public class InstancingProgramMetaData {
|
|||
ErrorReporter.generateMissingStruct(file, instanceName, "struct not defined");
|
||||
}
|
||||
|
||||
if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty() || maybeInstance.isEmpty()) {
|
||||
if (maybeInterpolant.isEmpty() || maybeInstance.isEmpty()) {
|
||||
throw new ShaderLoadingException();
|
||||
}
|
||||
|
||||
interpolant = maybeInterpolant.get();
|
||||
vertex = maybeVertex.get();
|
||||
instance = maybeInstance.get();
|
||||
}
|
||||
|
||||
public void vertexFooter(StringBuilder template, ShaderCompiler shader) {
|
||||
ImmutableList<StructField> fields = instance.getFields();
|
||||
VertexType vertexType = shader.vertexType;
|
||||
|
||||
int attributeBinding = vertexType.getLayout()
|
||||
.getAttributeCount();
|
||||
|
||||
for (StructField field : fields) {
|
||||
template.append("layout(location = ")
|
||||
.append(attributeBinding)
|
||||
.append(") in")
|
||||
.append(' ')
|
||||
.append(field.type)
|
||||
.append(' ')
|
||||
.append("a_i_")
|
||||
.append(field.name)
|
||||
.append(";\n");
|
||||
attributeBinding += ShaderInput.from(field).attribCount;
|
||||
}
|
||||
Template.prefixFields(template, interpolant, "out", "v2f_");
|
||||
|
||||
template.append(String.format("""
|
||||
void main() {
|
||||
Vertex v = FLWCreateVertex();
|
||||
%s i;
|
||||
%s
|
||||
vertex(v, i);
|
||||
gl_Position = FLWVertex(v);
|
||||
v.normal = normalize(v.normal);
|
||||
|
||||
v2f_color = v.color;
|
||||
v2f_texCoords = v.texCoords;
|
||||
v2f_light = v.light;
|
||||
v2f_diffuse = diffuse(v.normal);
|
||||
#if defined(DEBUG_NORMAL)
|
||||
v2f_color = vec4(v.normal, 1.);
|
||||
#endif
|
||||
}
|
||||
""",
|
||||
instanceName,
|
||||
Template.assignFields(instance, "i.", "a_i_")
|
||||
));
|
||||
}
|
||||
|
||||
public void fragmentFooter(StringBuilder template, ShaderCompiler shader) {
|
||||
Template.prefixFields(template, interpolant, "in", "v2f_");
|
||||
|
||||
template.append(String.format("""
|
||||
void main() {
|
||||
Fragment o;
|
||||
o.color = v2f_color;
|
||||
o.texCoords = v2f_texCoords;
|
||||
o.light = v2f_light;
|
||||
o.diffuse = v2f_diffuse;
|
||||
|
||||
vec4 color = %s;
|
||||
FLWFinalizeColor(color);
|
||||
}
|
||||
""",
|
||||
fragmentMain.call("o")
|
||||
));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
|
||||
public class LazyCompiler<P extends WorldProgram> implements FlexibleShader<P> {
|
||||
|
||||
private final ShaderPipeline<P> pipeline;
|
||||
private final ProgramSpec spec;
|
||||
|
||||
private final Map<VertexType, ContextAwareProgram<P>> cache = new HashMap<>();
|
||||
|
||||
public LazyCompiler(ShaderPipeline<P> pipeline, ProgramSpec spec) {
|
||||
|
||||
this.pipeline = pipeline;
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
cache.values().forEach(ContextAwareProgram::delete);
|
||||
}
|
||||
|
||||
@Override
|
||||
public P get(VertexType type) {
|
||||
return cache.computeIfAbsent(type, t -> pipeline.compile(spec, t)).get();
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
|
||||
public class OneShotTemplate extends Template<OneShotProgramMetaData> {
|
||||
|
||||
public static final OneShotTemplate INSTANCE = new OneShotTemplate();
|
||||
|
||||
public OneShotTemplate() {
|
||||
super(OneShotProgramMetaData::new);
|
||||
}
|
||||
|
||||
@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 Collection<ShaderInput> getShaderInputs(SourceFile file) {
|
||||
OneShotProgramMetaData data = getMetadata(file);
|
||||
|
||||
return ShaderInput.fromStruct(data.vertex, "a_v_");
|
||||
}
|
||||
|
||||
public void vertexFooter(StringBuilder template, SourceFile file) {
|
||||
OneShotProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.vertex, "in", "a_v_");
|
||||
Template.prefixFields(template, data.interpolant, "out", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.vertexName)
|
||||
.append(" v;\n");
|
||||
Template.assignFields(template, data.vertex, "v.", "a_v_");
|
||||
|
||||
template.append(data.interpolantName)
|
||||
.append(" o = ")
|
||||
.append(data.vertexMain.call("v"))
|
||||
.append(";\n");
|
||||
|
||||
Template.assignFields(template, data.interpolant, "v2f_", "o.");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
|
||||
public void fragmentFooter(StringBuilder template, SourceFile file) {
|
||||
OneShotProgramMetaData data = getMetadata(file);
|
||||
|
||||
Template.prefixFields(template, data.interpolant, "in", "v2f_");
|
||||
|
||||
template.append("void main() {\n");
|
||||
template.append(data.interpolant.name)
|
||||
.append(" o;\n");
|
||||
Template.assignFields(template, data.interpolant, "o.", "v2f_");
|
||||
|
||||
template.append(data.fragmentMain.call("o"))
|
||||
.append(";\n");
|
||||
|
||||
template.append('}');
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.jozufozu.flywheel.backend.pipeline;
|
|||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.source.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
||||
|
@ -10,17 +11,15 @@ import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
|||
import com.jozufozu.flywheel.backend.source.parse.Variable;
|
||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||
|
||||
public class OneShotProgramMetaData {
|
||||
public class OneShotTemplateData implements TemplateData {
|
||||
|
||||
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 OneShotProgramMetaData(SourceFile file) {
|
||||
public OneShotTemplateData(SourceFile file) {
|
||||
this.file = file;
|
||||
|
||||
Optional<ShaderFunction> maybeVertexMain = file.findFunction("vertex");
|
||||
|
@ -55,24 +54,67 @@ public class OneShotProgramMetaData {
|
|||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
interpolantName = vertexMain.getType();
|
||||
vertexName = vertexParameters.get(0)
|
||||
.typeName();
|
||||
Variable vertexParam = vertexMain.getParameters().get(0);
|
||||
|
||||
boolean namedVertex = vertexParam.type
|
||||
.toString()
|
||||
.equals("Vertex");
|
||||
|
||||
if (!(namedVertex && vertexParam.qualifier == Variable.Qualifier.INOUT)) {
|
||||
ErrorReporter.generateSpanError(vertexParam.qualifierSpan, "first parameter must be inout Vertex");
|
||||
throw new ShaderLoadingException();
|
||||
}
|
||||
|
||||
interpolantName = fragmentMain.getParameters().get(0).type;
|
||||
|
||||
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
|
||||
Optional<ShaderStruct> maybeVertex = file.findStruct(vertexName);
|
||||
|
||||
if (maybeVertex.isEmpty())
|
||||
ErrorReporter.generateMissingStruct(file, vertexName, "struct not defined");
|
||||
|
||||
if (maybeInterpolant.isEmpty())
|
||||
if (maybeInterpolant.isEmpty()) {
|
||||
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
|
||||
|
||||
if (maybeVertex.isEmpty() || maybeInterpolant.isEmpty()) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
interpolant = maybeInterpolant.get();
|
||||
vertex = maybeVertex.get();
|
||||
}
|
||||
|
||||
public void vertexFooter(StringBuilder template, ShaderCompiler file) {
|
||||
Template.prefixFields(template, interpolant, "out", "v2f_");
|
||||
|
||||
template.append("""
|
||||
void main() {
|
||||
Vertex v = FLWCreateVertex();
|
||||
vertex(v);
|
||||
gl_Position = FLWVertex(v);
|
||||
v.normal = normalize(v.normal);
|
||||
|
||||
v2f_color = v.color;
|
||||
v2f_texCoords = v.texCoords;
|
||||
v2f_light = v.light;
|
||||
v2f_diffuse = diffuse(v.normal);
|
||||
#if defined(DEBUG_NORMAL)
|
||||
v2f_color = vec4(v.normal, 1.);
|
||||
#endif
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
public void fragmentFooter(StringBuilder template, ShaderCompiler file) {
|
||||
Template.prefixFields(template, interpolant, "in", "v2f_");
|
||||
|
||||
template.append(String.format("""
|
||||
void main() {
|
||||
Fragment o;
|
||||
o.color = v2f_color;
|
||||
o.texCoords = v2f_texCoords;
|
||||
o.light = v2f_light;
|
||||
o.diffuse = v2f_diffuse;
|
||||
|
||||
vec4 color = %s;
|
||||
FLWFinalizeColor(color);
|
||||
}
|
||||
""",
|
||||
fragmentMain.call("o")
|
||||
));
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ package com.jozufozu.flywheel.backend.pipeline;
|
|||
import static org.lwjgl.opengl.GL11.GL_TRUE;
|
||||
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
|
||||
import static org.lwjgl.opengl.GL20.glAttachShader;
|
||||
import static org.lwjgl.opengl.GL20.glBindAttribLocation;
|
||||
import static org.lwjgl.opengl.GL20.glCreateProgram;
|
||||
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
|
||||
import static org.lwjgl.opengl.GL20.glGetProgrami;
|
||||
|
@ -13,44 +12,33 @@ import java.util.List;
|
|||
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class ProtoProgram {
|
||||
public class ProgramAssembler {
|
||||
public final int program;
|
||||
public final WorldShader parent;
|
||||
private final ResourceLocation name;
|
||||
|
||||
private int attributeIndex;
|
||||
private final List<GlShader> shaders = new ObjectArrayList<>();
|
||||
|
||||
private final List<GlShader> shaders;
|
||||
|
||||
public ProtoProgram(WorldShader parent) {
|
||||
this.parent = parent;
|
||||
public ProgramAssembler(ResourceLocation name) {
|
||||
this.name = name;
|
||||
this.program = glCreateProgram();
|
||||
shaders = new ObjectArrayList<>();
|
||||
}
|
||||
|
||||
public ProtoProgram compilePart(ShaderType type) {
|
||||
GlShader shader = parent.compile(type);
|
||||
attachShader(shader);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Links the attached shaders to this program.
|
||||
*/
|
||||
public ProtoProgram link() {
|
||||
|
||||
parent.template.getShaderInputs(parent.mainFile)
|
||||
.forEach(this::addAttribute);
|
||||
|
||||
public ProgramAssembler link() {
|
||||
glLinkProgram(this.program);
|
||||
|
||||
String log = glGetProgramInfoLog(this.program);
|
||||
|
||||
if (!log.isEmpty()) {
|
||||
Backend.log.debug("Program link log for " + parent.name + ": " + log);
|
||||
Backend.log.debug("Program link log for " + name + ": " + log);
|
||||
}
|
||||
|
||||
int result = glGetProgrami(this.program, GL_LINK_STATUS);
|
||||
|
@ -62,19 +50,18 @@ public class ProtoProgram {
|
|||
return this;
|
||||
}
|
||||
|
||||
public ProtoProgram deleteLinkedShaders() {
|
||||
public ProgramAssembler deleteLinkedShaders() {
|
||||
shaders.forEach(GlShader::delete);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void attachShader(GlShader glShader) {
|
||||
public ProgramAssembler attachShader(GlShader glShader) {
|
||||
shaders.add(glShader);
|
||||
glAttachShader(this.program, glShader.handle());
|
||||
return this;
|
||||
}
|
||||
|
||||
private void addAttribute(ShaderInput shaderInput) {
|
||||
glBindAttribLocation(this.program, attributeIndex, shaderInput.name);
|
||||
attributeIndex += shaderInput.attribCount;
|
||||
public <P extends WorldProgram> P build(ExtensibleGlProgram.Factory<P> factory) {
|
||||
return factory.create(name, program);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.source.error.ErrorBuilder;
|
||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class ShaderCompiler {
|
||||
|
||||
public final ResourceLocation name;
|
||||
public final Template<?> template;
|
||||
private final FileResolution header;
|
||||
|
||||
@Nullable
|
||||
private ProgramState variant;
|
||||
|
||||
public final VertexType vertexType;
|
||||
|
||||
public SourceFile mainFile;
|
||||
|
||||
public ShaderCompiler(ResourceLocation name, SourceFile mainSource, Template<?> template, FileResolution header, VertexType vertexType) {
|
||||
this.name = name;
|
||||
this.template = template;
|
||||
this.header = header;
|
||||
this.mainFile = mainSource;
|
||||
this.vertexType = vertexType;
|
||||
}
|
||||
|
||||
public ShaderCompiler setMainSource(SourceFile file) {
|
||||
if (mainFile == file) return this;
|
||||
|
||||
mainFile = file;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public GlShader compile(ShaderType type) {
|
||||
|
||||
StringBuilder finalSource = new StringBuilder();
|
||||
|
||||
finalSource.append("#version ")
|
||||
.append(template.getVersion())
|
||||
.append('\n')
|
||||
.append("#extension GL_ARB_explicit_attrib_location : enable\n")
|
||||
.append("#extension GL_ARB_conservative_depth : enable\n")
|
||||
.append("#define ")
|
||||
.append(type.define) // special case shader type declaration
|
||||
.append('\n');
|
||||
|
||||
ProgramState variant = getVariant();
|
||||
if (variant != null) {
|
||||
for (String def : variant.defines()) {
|
||||
finalSource.append("#define ")
|
||||
.append(def)
|
||||
.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (type == ShaderType.VERTEX) {
|
||||
finalSource.append("""
|
||||
struct Vertex {
|
||||
vec3 pos;
|
||||
vec4 color;
|
||||
vec2 texCoords;
|
||||
vec2 light;
|
||||
vec3 normal;
|
||||
};
|
||||
""");
|
||||
finalSource.append(vertexType.writeShaderHeader());
|
||||
}
|
||||
|
||||
files.clear();
|
||||
if (header.getFile() != null) {
|
||||
header.getFile().generateFinalSource(this, finalSource);
|
||||
}
|
||||
mainFile.generateFinalSource(this, finalSource);
|
||||
|
||||
template.getMetadata(mainFile).generateFooter(finalSource, type, this);
|
||||
|
||||
return new GlShader(this, type, finalSource.toString());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ProgramState getVariant() {
|
||||
return variant;
|
||||
}
|
||||
|
||||
public void setVariant(@Nullable ProgramState variant) {
|
||||
this.variant = variant;
|
||||
}
|
||||
|
||||
private final List<SourceFile> files = new ArrayList<>();
|
||||
|
||||
public int allocateFile(SourceFile sourceFile) {
|
||||
int i = files.indexOf(sourceFile);
|
||||
if (i != -1) {
|
||||
return i;
|
||||
}
|
||||
|
||||
int size = files.size();
|
||||
files.add(sourceFile);
|
||||
return size;
|
||||
}
|
||||
|
||||
public Span getLineSpan(int fileId, int lineNo) {
|
||||
SourceFile file = files.get(fileId);
|
||||
|
||||
return file.getLineSpanNoWhitespace(lineNo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ErrorBuilder parseCompilerError(String line) {
|
||||
try {
|
||||
ErrorBuilder error = ErrorBuilder.fromLogLine(this, line);
|
||||
if (error != null) {
|
||||
return error;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public <P extends WorldProgram> P compile(ExtensibleGlProgram.Factory<P> worldShaderPipeline) {
|
||||
return new ProgramAssembler(this.name)
|
||||
.attachShader(compile(ShaderType.VERTEX))
|
||||
.attachShader(compile(ShaderType.FRAGMENT))
|
||||
.link()
|
||||
.deleteLinkedShaders()
|
||||
.build(worldShaderPipeline);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
|
@ -10,6 +11,6 @@ import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
|||
*/
|
||||
public interface ShaderPipeline<P extends WorldProgram> {
|
||||
|
||||
ContextAwareProgram<P> compile(ProgramSpec spec);
|
||||
ContextAwareProgram<P> compile(ProgramSpec spec, VertexType vertexType);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.source.parse.StructField;
|
||||
|
@ -21,37 +19,25 @@ import com.jozufozu.flywheel.backend.source.parse.StructField;
|
|||
* </p>
|
||||
* @param <D> Holds metadata, generates errors.
|
||||
*/
|
||||
public abstract class Template<D> {
|
||||
public class Template<D extends TemplateData> {
|
||||
|
||||
private final Map<SourceFile, D> metadata = new HashMap<>();
|
||||
|
||||
private final Function<SourceFile, D> reader;
|
||||
private final GLSLVersion glslVersion;
|
||||
|
||||
protected Template(Function<SourceFile, D> reader) {
|
||||
public Template(GLSLVersion glslVersion, Function<SourceFile, D> reader) {
|
||||
this.reader = reader;
|
||||
this.glslVersion = glslVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the necessary glue code here.
|
||||
*
|
||||
* <p>
|
||||
* See {@link InstancingTemplate} and {@link OneShotTemplate} for examples.
|
||||
* </p>
|
||||
* @param builder The builder to generate the source into.
|
||||
* @param type The shader stage glue code is needed for.
|
||||
* @param file The SourceFile with user written code.
|
||||
*/
|
||||
public abstract void generateTemplateSource(StringBuilder builder, ShaderType type, SourceFile file);
|
||||
|
||||
public abstract Collection<ShaderInput> getShaderInputs(SourceFile file);
|
||||
|
||||
public D getMetadata(SourceFile file) {
|
||||
// lazily read files, cache results
|
||||
return metadata.computeIfAbsent(file, reader);
|
||||
}
|
||||
|
||||
public GLSLVersion getVersion() {
|
||||
return GLSLVersion.V150;
|
||||
return glslVersion;
|
||||
}
|
||||
|
||||
public static void prefixFields(StringBuilder builder, ShaderStruct struct, String qualifier, String prefix) {
|
||||
|
@ -68,9 +54,11 @@ public abstract class Template<D> {
|
|||
}
|
||||
}
|
||||
|
||||
public static void assignFields(StringBuilder builder, ShaderStruct struct, String prefix1, String prefix2) {
|
||||
public static StringBuilder assignFields(ShaderStruct struct, String prefix1, String prefix2) {
|
||||
ImmutableList<StructField> fields = struct.getFields();
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (StructField field : fields) {
|
||||
builder.append(prefix1)
|
||||
.append(field.name)
|
||||
|
@ -79,5 +67,7 @@ public abstract class Template<D> {
|
|||
.append(field.name)
|
||||
.append(";\n");
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
|
||||
public interface TemplateData {
|
||||
void vertexFooter(StringBuilder builder, ShaderCompiler file);
|
||||
void fragmentFooter(StringBuilder builder, ShaderCompiler file);
|
||||
|
||||
/**
|
||||
* Generate the necessary glue code here.
|
||||
*
|
||||
* @param builder The builder to generate the source into.
|
||||
* @param type The shader stage glue code is needed for.
|
||||
* @param file The SourceFile with user written code.
|
||||
*/
|
||||
default void generateFooter(StringBuilder builder, ShaderType type, ShaderCompiler file) {
|
||||
if (type == ShaderType.VERTEX) {
|
||||
vertexFooter(builder, file);
|
||||
} else if (type == ShaderType.FRAGMENT) {
|
||||
fragmentFooter(builder, file);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class WorldShader {
|
||||
|
||||
public final ResourceLocation name;
|
||||
public final Template<?> template;
|
||||
public final CharSequence header;
|
||||
|
||||
public SourceFile mainFile;
|
||||
|
||||
private CharSequence source;
|
||||
private StringBuilder defines;
|
||||
|
||||
public WorldShader(ResourceLocation name, Template<?> template, FileResolution header) {
|
||||
this.name = name;
|
||||
this.template = template;
|
||||
this.header = Optional.ofNullable(header.getFile())
|
||||
.map(SourceFile::generateFinalSource)
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
public WorldShader setDefines(List<String> defs) {
|
||||
defines = new StringBuilder();
|
||||
|
||||
for (String def : defs) {
|
||||
defines.append("#define ")
|
||||
.append(def)
|
||||
.append('\n');
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public WorldShader setMainSource(SourceFile file) {
|
||||
if (mainFile == file) return this;
|
||||
|
||||
mainFile = file;
|
||||
source = file.generateFinalSource();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public GlShader compile(ShaderType type) {
|
||||
|
||||
StringBuilder finalSource = new StringBuilder();
|
||||
|
||||
finalSource.append("#version ")
|
||||
.append(template.getVersion())
|
||||
.append('\n')
|
||||
.append("#extension GL_ARB_conservative_depth : enable\n")
|
||||
.append("#define ")
|
||||
.append(type.define) // special case shader type declaration
|
||||
.append('\n')
|
||||
.append(defines != null ? defines : "")
|
||||
.append(header)
|
||||
.append('\n')
|
||||
.append(source)
|
||||
.append('\n');
|
||||
|
||||
template.generateTemplateSource(finalSource, type, mainFile);
|
||||
|
||||
return new GlShader(name, type, finalSource);
|
||||
}
|
||||
|
||||
public ProtoProgram createProgram() {
|
||||
return new ProtoProgram(this);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.pipeline;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
|
||||
|
@ -14,8 +10,6 @@ import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class WorldShaderPipeline<P extends WorldProgram> implements ShaderPipeline<P> {
|
||||
|
||||
private final ExtensibleGlProgram.Factory<P> factory;
|
||||
|
@ -29,38 +23,18 @@ public class WorldShaderPipeline<P extends WorldProgram> implements ShaderPipeli
|
|||
this.header = header;
|
||||
}
|
||||
|
||||
public ContextAwareProgram<P> compile(ProgramSpec spec) {
|
||||
public ContextAwareProgram<P> compile(ProgramSpec spec, VertexType vertexType) {
|
||||
|
||||
SourceFile file = spec.getSource().getFile();
|
||||
|
||||
return compile(spec.name, file, spec.getStates());
|
||||
}
|
||||
ShaderCompiler shader = new ShaderCompiler(spec.name, file, template, header, vertexType);
|
||||
|
||||
public ContextAwareProgram<P> compile(ResourceLocation name, SourceFile file, List<ProgramState> variants) {
|
||||
WorldShader shader = new WorldShader(name, template, header)
|
||||
.setMainSource(file);
|
||||
|
||||
GameStateProgram.Builder<P> builder = GameStateProgram.builder(compile(shader, null));
|
||||
|
||||
for (ProgramState variant : variants) {
|
||||
builder.withVariant(variant.context(), compile(shader, variant));
|
||||
GameStateProgram.Builder<P> builder = GameStateProgram.builder(shader.compile(this.factory));
|
||||
for (ProgramState variant : spec.getStates()) {
|
||||
shader.setVariant(variant);
|
||||
builder.withVariant(variant.context(), shader.compile(this.factory));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private P compile(WorldShader shader, @Nullable ProgramState variant) {
|
||||
|
||||
if (variant != null) {
|
||||
shader.setDefines(variant.defines());
|
||||
}
|
||||
|
||||
ProtoProgram program = shader.createProgram()
|
||||
.compilePart(ShaderType.VERTEX)
|
||||
.compilePart(ShaderType.FRAGMENT)
|
||||
.link()
|
||||
.deleteLinkedShaders();
|
||||
|
||||
return factory.create(shader.name, program.program);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,16 +62,15 @@ public class FileResolution {
|
|||
* Called after all files are loaded. If we can't find the file here, it doesn't exist.
|
||||
* </p>
|
||||
*/
|
||||
void resolve(ISourceHolder sources) {
|
||||
void resolve(SourceFinder sources) {
|
||||
file = sources.findSource(fileLoc);
|
||||
|
||||
try {
|
||||
file = sources.findSource(fileLoc);
|
||||
} catch (RuntimeException error) {
|
||||
if (file == null) {
|
||||
ErrorBuilder builder = ErrorBuilder.error(String.format("could not find source for file %s", fileLoc));
|
||||
// print the location of all places where this file was referenced
|
||||
for (Span span : foundSpans) {
|
||||
builder.pointAtFile(span.getSourceFile())
|
||||
.pointAt(span, 2);
|
||||
.pointAt(span, 1);
|
||||
}
|
||||
Backend.log.error(builder.build());
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public class Resolver {
|
|||
/**
|
||||
* Try and resolve all referenced source files, printing errors if any aren't found.
|
||||
*/
|
||||
public void resolve(ISourceHolder sources) {
|
||||
public void resolve(SourceFinder sources) {
|
||||
for (FileResolution resolution : resolutions.values()) {
|
||||
resolution.resolve(sources);
|
||||
}
|
||||
|
|
|
@ -1,22 +1,11 @@
|
|||
package com.jozufozu.flywheel.backend.source;
|
||||
|
||||
public class ShaderLoadingException extends RuntimeException {
|
||||
|
||||
public ShaderLoadingException() {
|
||||
}
|
||||
|
||||
public ShaderLoadingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ShaderLoadingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ShaderLoadingException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ShaderLoadingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ import java.util.Collection;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.jozufozu.flywheel.util.ResourceUtil;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
@ -17,7 +19,7 @@ import net.minecraft.server.packs.resources.ResourceManager;
|
|||
/**
|
||||
* The main object for loading and parsing source files.
|
||||
*/
|
||||
public class ShaderSources implements ISourceHolder {
|
||||
public class ShaderSources implements SourceFinder {
|
||||
public static final String SHADER_DIR = "flywheel/shaders/";
|
||||
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
|
||||
|
||||
|
@ -51,13 +53,9 @@ public class ShaderSources implements ISourceHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public SourceFile findSource(ResourceLocation name) {
|
||||
SourceFile source = shaderSources.get(name);
|
||||
|
||||
if (source == null) {
|
||||
throw new ShaderLoadingException(String.format("shader '%s' does not exist", name));
|
||||
}
|
||||
|
||||
return source;
|
||||
return shaderSources.get(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import java.util.regex.Pattern;
|
|||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.backend.pipeline.ShaderCompiler;
|
||||
import com.jozufozu.flywheel.backend.source.parse.Import;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
||||
|
@ -74,6 +75,23 @@ public class SourceFile {
|
|||
this.elided = elideSource(source, elisions).toString();
|
||||
}
|
||||
|
||||
public Span getLineSpan(int line) {
|
||||
int begin = lines.getLineStart(line);
|
||||
int end = begin + lines.getLine(line).length();
|
||||
return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end));
|
||||
}
|
||||
|
||||
public Span getLineSpanNoWhitespace(int line) {
|
||||
int begin = lines.getLineStart(line);
|
||||
int end = begin + lines.getLine(line).length();
|
||||
|
||||
while (begin < end && Character.isWhitespace(source.charAt(begin))) {
|
||||
begin++;
|
||||
}
|
||||
|
||||
return new StringSpan(this, lines.getCharPos(begin), lines.getCharPos(end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Search this file and recursively search all imports to find a struct definition matching the given name.
|
||||
*
|
||||
|
@ -120,18 +138,25 @@ public class SourceFile {
|
|||
return "#use " + '"' + name + '"';
|
||||
}
|
||||
|
||||
public CharSequence generateFinalSource() {
|
||||
public CharSequence generateFinalSource(ShaderCompiler env) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
generateFinalSource(builder);
|
||||
generateFinalSource(env, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
public void generateFinalSource(StringBuilder source) {
|
||||
public void generateFinalSource(ShaderCompiler env, StringBuilder source) {
|
||||
for (Import include : imports) {
|
||||
SourceFile file = include.getFile();
|
||||
|
||||
if (file != null) file.generateFinalSource(source);
|
||||
if (file != null) file.generateFinalSource(env, source);
|
||||
}
|
||||
|
||||
int i = env.allocateFile(this);
|
||||
source.append("#line ")
|
||||
.append(0)
|
||||
.append(' ')
|
||||
.append(i)
|
||||
.append('\n');
|
||||
source.append(elided);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
package com.jozufozu.flywheel.backend.source;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* A minimal source file lookup function.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ISourceHolder {
|
||||
public interface SourceFinder {
|
||||
|
||||
@Nullable
|
||||
SourceFile findSource(ResourceLocation name);
|
||||
}
|
|
@ -29,6 +29,48 @@ public class SourceLines {
|
|||
this.lines = getLines(source, lineStarts);
|
||||
}
|
||||
|
||||
public int getLineCount() {
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
public String getLine(int lineNo) {
|
||||
return lines.get(lineNo);
|
||||
}
|
||||
|
||||
public int getLineStart(int lineNo) {
|
||||
|
||||
return lineStarts.getInt(lineNo);
|
||||
}
|
||||
|
||||
public CharPos getCharPos(int charPos) {
|
||||
int lineNo = 0;
|
||||
for (; lineNo < lineStarts.size(); lineNo++) {
|
||||
int ls = lineStarts.getInt(lineNo);
|
||||
|
||||
if (charPos < ls) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineNo -= 1;
|
||||
|
||||
int lineStart = lineStarts.getInt(lineNo);
|
||||
|
||||
return new CharPos(charPos, lineNo, charPos - lineStart);
|
||||
}
|
||||
|
||||
public String printLinesWithNumbers() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
|
||||
builder.append(String.format("%1$4s: ", i + 1))
|
||||
.append(lines.get(i))
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the source for line breaks, recording the position of the first character of each line.
|
||||
* @param source
|
||||
|
@ -57,41 +99,4 @@ public class SourceLines {
|
|||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public CharPos getCharPos(int charPos) {
|
||||
int lineNo = 0;
|
||||
for (; lineNo < lineStarts.size(); lineNo++) {
|
||||
int ls = lineStarts.getInt(lineNo);
|
||||
|
||||
if (charPos < ls) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lineNo -= 1;
|
||||
|
||||
int lineStart = lineStarts.getInt(lineNo);
|
||||
|
||||
return new CharPos(charPos, lineNo, charPos - lineStart);
|
||||
}
|
||||
|
||||
public int getLineCount() {
|
||||
return lines.size();
|
||||
}
|
||||
|
||||
public String getLine(int lineNo) {
|
||||
return lines.get(lineNo);
|
||||
}
|
||||
|
||||
public String printLinesWithNumbers() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
for (int i = 0, linesSize = lines.size(); i < linesSize; i++) {
|
||||
builder.append(String.format("%1$4s: ", i + 1))
|
||||
.append(lines.get(i))
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ package com.jozufozu.flywheel.backend.source.error;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.backend.pipeline.ShaderCompiler;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.source.SourceLines;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.ErrorLine;
|
||||
|
@ -12,27 +15,60 @@ import com.jozufozu.flywheel.backend.source.error.lines.FileLine;
|
|||
import com.jozufozu.flywheel.backend.source.error.lines.HeaderLine;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.SourceLine;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.SpanHighlightLine;
|
||||
import com.jozufozu.flywheel.backend.source.error.lines.TextLine;
|
||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
|
||||
public class ErrorBuilder {
|
||||
|
||||
private static final Pattern ERROR_LINE = Pattern.compile("(\\d+)\\((\\d+)\\) : (.*)");
|
||||
|
||||
private final List<ErrorLine> lines = new ArrayList<>();
|
||||
|
||||
private final Level level;
|
||||
public ErrorBuilder() {
|
||||
|
||||
public ErrorBuilder(Level level, CharSequence msg) {
|
||||
this.level = level;
|
||||
|
||||
lines.add(new HeaderLine(level.toString(), msg));
|
||||
}
|
||||
|
||||
public static ErrorBuilder error(CharSequence msg) {
|
||||
return new ErrorBuilder(Level.ERROR, msg);
|
||||
return new ErrorBuilder()
|
||||
.header(ErrorLevel.ERROR, msg);
|
||||
}
|
||||
|
||||
public static ErrorBuilder compError(CharSequence msg) {
|
||||
return new ErrorBuilder()
|
||||
.extra(msg);
|
||||
}
|
||||
|
||||
public static ErrorBuilder warn(CharSequence msg) {
|
||||
return new ErrorBuilder(Level.WARN, msg);
|
||||
return new ErrorBuilder()
|
||||
.header(ErrorLevel.WARN, msg);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ErrorBuilder fromLogLine(ShaderCompiler env, String s) {
|
||||
Matcher matcher = ERROR_LINE.matcher(s);
|
||||
|
||||
if (matcher.find()) {
|
||||
String fileId = matcher.group(1);
|
||||
String lineNo = matcher.group(2);
|
||||
String msg = matcher.group(3);
|
||||
Span span = env.getLineSpan(Integer.parseInt(fileId), Integer.parseInt(lineNo));
|
||||
return ErrorBuilder.compError(msg)
|
||||
.pointAtFile(span.getSourceFile())
|
||||
.pointAt(span, 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ErrorBuilder header(ErrorLevel level, CharSequence msg) {
|
||||
lines.add(new HeaderLine(level.toString(), msg));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorBuilder extra(CharSequence msg) {
|
||||
lines.add(new TextLine(msg.toString()));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ErrorBuilder pointAtFile(SourceFile file) {
|
||||
|
@ -42,14 +78,14 @@ public class ErrorBuilder {
|
|||
|
||||
public ErrorBuilder hintIncludeFor(@Nullable Span span, CharSequence msg) {
|
||||
if (span == null) return this;
|
||||
SourceFile sourceFile = span.getSourceFile();
|
||||
|
||||
String builder = "add " + span.getSourceFile()
|
||||
.importStatement() + ' ' + msg;
|
||||
String builder = "add " + sourceFile.importStatement() + ' ' + msg + "\n defined here:";
|
||||
|
||||
lines.add(new HeaderLine("hint", builder));
|
||||
header(ErrorLevel.HINT, builder);
|
||||
|
||||
return this.pointAtFile(span.getSourceFile())
|
||||
.pointAt(span, 1);
|
||||
return this.pointAtFile(sourceFile)
|
||||
.pointAt(span, 0);
|
||||
}
|
||||
|
||||
public ErrorBuilder pointAt(Span span, int ctxLines) {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
package com.jozufozu.flywheel.backend.source.error;
|
||||
|
||||
public enum Level {
|
||||
public enum ErrorLevel {
|
||||
WARN("warn"),
|
||||
ERROR("error"),
|
||||
HINT("hint"),
|
||||
;
|
||||
|
||||
private final String error;
|
||||
|
||||
Level(String error) {
|
||||
ErrorLevel(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
|
@ -1,12 +1,15 @@
|
|||
package com.jozufozu.flywheel.backend.source.error;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.source.SourceFile;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderFunction;
|
||||
import com.jozufozu.flywheel.backend.source.parse.ShaderStruct;
|
||||
import com.jozufozu.flywheel.backend.source.span.Span;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
|
||||
public class ErrorReporter {
|
||||
|
||||
|
@ -42,7 +45,7 @@ public class ErrorReporter {
|
|||
|
||||
ErrorBuilder error = ErrorBuilder.error(msg)
|
||||
.pointAtFile(file)
|
||||
.pointAt(vertexName, 2)
|
||||
.pointAt(vertexName, 1)
|
||||
.hintIncludeFor(span.orElse(null), hint);
|
||||
|
||||
Backend.log.error(error.build());
|
||||
|
@ -64,4 +67,29 @@ public class ErrorReporter {
|
|||
|
||||
Backend.log.error(error.build());
|
||||
}
|
||||
|
||||
public static void printLines(CharSequence source) {
|
||||
String string = source.toString();
|
||||
|
||||
List<String> lines = string.lines()
|
||||
.toList();
|
||||
|
||||
int size = lines.size();
|
||||
|
||||
int maxWidth = FlwUtil.numDigits(size) + 1;
|
||||
|
||||
StringBuilder builder = new StringBuilder().append('\n');
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
|
||||
builder.append(i)
|
||||
.append(FlwUtil.repeatChar(' ', maxWidth - FlwUtil.numDigits(i)))
|
||||
.append("| ")
|
||||
.append(lines.get(i))
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
Flywheel.LOGGER.error(builder.toString());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ public interface ErrorLine {
|
|||
return left() + divider() + right();
|
||||
}
|
||||
|
||||
String left();
|
||||
String right();
|
||||
default String left() {
|
||||
return "";
|
||||
}
|
||||
default String right() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,14 +11,4 @@ public record HeaderLine(String level, CharSequence message) implements ErrorLin
|
|||
public String build() {
|
||||
return level + ": " + message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String left() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String right() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package com.jozufozu.flywheel.backend.source.error.lines;
|
||||
|
||||
public record TextLine(String msg) implements ErrorLine {
|
||||
|
||||
@Override
|
||||
public String build() {
|
||||
return msg;
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import com.jozufozu.flywheel.backend.source.span.Span;
|
|||
|
||||
public class ShaderFunction extends AbstractShaderElement {
|
||||
|
||||
public static final Pattern argument = Pattern.compile("(\\w+)\\s+(\\w+)");
|
||||
public static final Pattern argument = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)");
|
||||
public static final Pattern assignment = Pattern.compile("(\\w+)\\s*=");
|
||||
|
||||
private final Span type;
|
||||
|
@ -66,10 +66,11 @@ public class ShaderFunction extends AbstractShaderElement {
|
|||
|
||||
while (arguments.find()) {
|
||||
Span self = Span.fromMatcher(args, arguments);
|
||||
Span type = Span.fromMatcher(args, arguments, 1);
|
||||
Span name = Span.fromMatcher(args, arguments, 2);
|
||||
Span qualifier = Span.fromMatcher(args, arguments, 1);
|
||||
Span type = Span.fromMatcher(args, arguments, 2);
|
||||
Span name = Span.fromMatcher(args, arguments, 3);
|
||||
|
||||
builder.add(new Variable(self, type, name));
|
||||
builder.add(new Variable(self, qualifier, type, name));
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
|
@ -79,7 +80,7 @@ public class ShaderFunction extends AbstractShaderElement {
|
|||
public String toString() {
|
||||
|
||||
String p = parameters.stream()
|
||||
.map(Variable::typeName)
|
||||
.map(variable -> variable.type)
|
||||
.map(Span::get)
|
||||
.collect(Collectors.joining(","));
|
||||
|
||||
|
|
|
@ -4,25 +4,41 @@ import com.jozufozu.flywheel.backend.source.span.Span;
|
|||
|
||||
public class Variable extends AbstractShaderElement {
|
||||
|
||||
private final Span type;
|
||||
private final Span name;
|
||||
public final Span qualifierSpan;
|
||||
public final Span type;
|
||||
public final Span name;
|
||||
public final Qualifier qualifier;
|
||||
|
||||
public Variable(Span self, Span type, Span name) {
|
||||
public Variable(Span self, Span qualifier, Span type, Span name) {
|
||||
super(self);
|
||||
this.qualifierSpan = qualifier;
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Span typeName() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Span getName() {
|
||||
return name;
|
||||
this.qualifier = Qualifier.fromSpan(qualifierSpan);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return type + " " + name;
|
||||
}
|
||||
|
||||
public enum Qualifier {
|
||||
NONE,
|
||||
IN,
|
||||
OUT,
|
||||
INOUT,
|
||||
ERROR;
|
||||
|
||||
public static Qualifier fromSpan(Span s) {
|
||||
String span = s.toString();
|
||||
|
||||
return switch (span) {
|
||||
case "" -> NONE;
|
||||
case "in" -> IN;
|
||||
case "inout" -> INOUT;
|
||||
case "out" -> OUT;
|
||||
default -> ERROR;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.jozufozu.flywheel.core;
|
|||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.GameStateRegistry;
|
||||
import com.jozufozu.flywheel.backend.pipeline.InstancingTemplate;
|
||||
import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline;
|
||||
import com.jozufozu.flywheel.backend.pipeline.WorldShaderPipeline;
|
||||
import com.jozufozu.flywheel.backend.source.FileResolution;
|
||||
|
@ -32,8 +31,8 @@ public class Contexts {
|
|||
FileResolution crumblingBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.CRUMBLING, ".glsl"));
|
||||
FileResolution worldBuiltins = Resolver.INSTANCE.findShader(ResourceUtil.subPath(Names.WORLD, ".glsl"));
|
||||
|
||||
ShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(CrumblingProgram::new, InstancingTemplate.INSTANCE, crumblingBuiltins);
|
||||
ShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(WorldProgram::new, InstancingTemplate.INSTANCE, worldBuiltins);
|
||||
ShaderPipeline<CrumblingProgram> crumblingPipeline = new WorldShaderPipeline<>(CrumblingProgram::new, Templates.INSTANCING, crumblingBuiltins);
|
||||
ShaderPipeline<WorldProgram> worldPipeline = new WorldShaderPipeline<>(WorldProgram::new, Templates.INSTANCING, worldBuiltins);
|
||||
|
||||
CRUMBLING = backend.register(WorldContext.builder(backend, Names.CRUMBLING).build(crumblingPipeline));
|
||||
WORLD = backend.register(WorldContext.builder(backend, Names.WORLD).build(worldPipeline));
|
||||
|
|
12
src/main/java/com/jozufozu/flywheel/core/Templates.java
Normal file
12
src/main/java/com/jozufozu/flywheel/core/Templates.java
Normal file
|
@ -0,0 +1,12 @@
|
|||
package com.jozufozu.flywheel.core;
|
||||
|
||||
import com.jozufozu.flywheel.backend.gl.GLSLVersion;
|
||||
import com.jozufozu.flywheel.backend.pipeline.InstancingTemplateData;
|
||||
import com.jozufozu.flywheel.backend.pipeline.OneShotTemplateData;
|
||||
import com.jozufozu.flywheel.backend.pipeline.Template;
|
||||
|
||||
public class Templates {
|
||||
|
||||
public static final Template<InstancingTemplateData> INSTANCING = new Template<>(GLSLVersion.V330, InstancingTemplateData::new);
|
||||
public static final Template<OneShotTemplateData> ONE_SHOT = new Template<>(GLSLVersion.V150, OneShotTemplateData::new);
|
||||
}
|
|
@ -6,11 +6,13 @@ import java.util.Objects;
|
|||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jozufozu.flywheel.api.shader.FlexibleShader;
|
||||
import com.jozufozu.flywheel.api.struct.Instanced;
|
||||
import com.jozufozu.flywheel.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.ShaderContext;
|
||||
import com.jozufozu.flywheel.backend.pipeline.LazyCompiler;
|
||||
import com.jozufozu.flywheel.backend.pipeline.ShaderPipeline;
|
||||
import com.jozufozu.flywheel.core.shader.ContextAwareProgram;
|
||||
import com.jozufozu.flywheel.backend.source.ShaderLoadingException;
|
||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
||||
|
||||
|
@ -18,7 +20,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||
|
||||
public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
|
||||
public final Backend backend;
|
||||
protected final Map<ResourceLocation, ContextAwareProgram<P>> programs = new HashMap<>();
|
||||
protected final Map<ResourceLocation, LazyCompiler<P>> programs = new HashMap<>();
|
||||
protected final ResourceLocation name;
|
||||
protected final Supplier<Stream<ResourceLocation>> specStream;
|
||||
|
||||
|
@ -44,25 +46,27 @@ public class WorldContext<P extends WorldProgram> implements ShaderContext<P> {
|
|||
private void loadSpec(ProgramSpec spec) {
|
||||
|
||||
try {
|
||||
programs.put(spec.name, pipeline.compile(spec));
|
||||
programs.put(spec.name, new LazyCompiler<>(pipeline, spec));
|
||||
|
||||
Backend.log.debug("Loaded program {}", spec.name);
|
||||
} catch (Exception e) {
|
||||
Backend.log.error("Error loading program {}", spec.name);
|
||||
Backend.log.error("", e);
|
||||
if (!(e instanceof ShaderLoadingException)) {
|
||||
Backend.log.error("", e);
|
||||
}
|
||||
backend.loader.notifyError();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<P> getProgramSupplier(ResourceLocation spec) {
|
||||
public FlexibleShader<P> getProgramSupplier(ResourceLocation spec) {
|
||||
return programs.get(spec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
programs.values()
|
||||
.forEach(ContextAwareProgram::delete);
|
||||
.forEach(LazyCompiler::delete);
|
||||
programs.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,8 +27,8 @@ public class BufferLayout {
|
|||
|
||||
int numAttributes = 0, stride = 0;
|
||||
for (LayoutItem spec : allAttributes) {
|
||||
numAttributes += spec.getAttributeCount();
|
||||
stride += spec.getSize();
|
||||
numAttributes += spec.attributeCount();
|
||||
stride += spec.size();
|
||||
}
|
||||
this.numAttributes = numAttributes;
|
||||
this.stride = stride;
|
||||
|
|
|
@ -19,6 +19,6 @@ public class CommonItems {
|
|||
public static final PrimitiveItem LIGHT_SHORT = new PrimitiveItem(GlNumericType.USHORT, 2, true);
|
||||
|
||||
public static final PrimitiveItem NORMALIZED_BYTE = new PrimitiveItem(GlNumericType.BYTE, 1, true);
|
||||
public static final LayoutItem PADDING_BYTE = new PaddingItem(1);
|
||||
public static final LayoutItem PADDING_BYTE = new Padding(1);
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ public interface LayoutItem {
|
|||
|
||||
void vertexAttribPointer(int stride, int index, int offset);
|
||||
|
||||
int getSize();
|
||||
int size();
|
||||
|
||||
int getAttributeCount();
|
||||
int attributeCount();
|
||||
}
|
||||
|
|
|
@ -26,12 +26,13 @@ public enum MatrixItems implements LayoutItem {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
public int size() {
|
||||
return GlNumericType.FLOAT.getByteWidth() * rows * cols;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAttributeCount() {
|
||||
public int attributeCount() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.jozufozu.flywheel.core.layout;
|
||||
|
||||
record PaddingItem(int bytes) implements LayoutItem {
|
||||
record Padding(int bytes) implements LayoutItem {
|
||||
|
||||
@Override
|
||||
public void vertexAttribPointer(int stride, int index, int offset) {
|
||||
|
@ -8,12 +8,13 @@ record PaddingItem(int bytes) implements LayoutItem {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
public int size() {
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAttributeCount() {
|
||||
public int attributeCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -30,12 +30,13 @@ public class PrimitiveItem implements LayoutItem {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAttributeCount() {
|
||||
public int attributeCount() {
|
||||
return attributeCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import java.nio.ByteBuffer;
|
|||
|
||||
import com.jozufozu.flywheel.api.vertex.VertexList;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.core.layout.CommonItems;
|
||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||
import com.jozufozu.flywheel.core.layout.CommonItems;
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
|
@ -36,6 +36,27 @@ public class BlockVertex implements VertexType {
|
|||
return new BlockVertexListUnsafe(buffer, vertexCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeShaderHeader() {
|
||||
return """
|
||||
layout (location = 0) in vec3 _flw_v_pos;
|
||||
layout (location = 1) in vec4 _flw_v_color;
|
||||
layout (location = 2) in vec2 _flw_v_texCoords;
|
||||
layout (location = 3) in vec2 _flw_v_light;
|
||||
layout (location = 4) in vec3 _flw_v_normal;
|
||||
|
||||
Vertex FLWCreateVertex() {
|
||||
Vertex v;
|
||||
v.pos = _flw_v_pos;
|
||||
v.color = _flw_v_color;
|
||||
v.texCoords = _flw_v_texCoords;
|
||||
v.light = _flw_v_light;
|
||||
v.normal = _flw_v_normal;
|
||||
return v;
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
public VertexList createReader(BufferBuilder bufferBuilder) {
|
||||
// TODO: try to avoid virtual model rendering
|
||||
Pair<BufferBuilder.DrawState, ByteBuffer> pair = bufferBuilder.popNextBuffer();
|
||||
|
|
|
@ -3,8 +3,8 @@ package com.jozufozu.flywheel.core.vertex;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.core.layout.CommonItems;
|
||||
import com.jozufozu.flywheel.core.layout.BufferLayout;
|
||||
import com.jozufozu.flywheel.core.layout.CommonItems;
|
||||
|
||||
public class PosTexNormalVertex implements VertexType {
|
||||
|
||||
|
@ -26,4 +26,23 @@ public class PosTexNormalVertex implements VertexType {
|
|||
public PosTexNormalVertexListUnsafe createReader(ByteBuffer buffer, int vertexCount) {
|
||||
return new PosTexNormalVertexListUnsafe(buffer, vertexCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String writeShaderHeader() {
|
||||
return """
|
||||
layout (location = 0) in vec3 _flw_v_pos;
|
||||
layout (location = 1) in vec2 _flw_v_texCoords;
|
||||
layout (location = 2) in vec3 _flw_v_normal;
|
||||
|
||||
Vertex FLWCreateVertex() {
|
||||
Vertex v;
|
||||
v.pos = _flw_v_pos;
|
||||
v.color = vec4(1.);
|
||||
v.texCoords = _flw_v_texCoords;
|
||||
v.light = vec2(0.);
|
||||
v.normal = _flw_v_normal;
|
||||
return v;
|
||||
}
|
||||
""";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
struct BlockFrag {
|
||||
struct Fragment {
|
||||
vec2 texCoords;
|
||||
vec4 color;
|
||||
float diffuse;
|
||||
|
@ -7,11 +7,9 @@ struct BlockFrag {
|
|||
};
|
||||
|
||||
#if defined(FRAGMENT_SHADER)
|
||||
void fragment(BlockFrag r) {
|
||||
vec4 fragment(Fragment r) {
|
||||
vec4 tex = FLWBlockTexture(r.texCoords);
|
||||
|
||||
vec4 color = vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color;
|
||||
|
||||
FLWFinalizeColor(color);
|
||||
return vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#use "flywheel:context/fog.glsl"
|
||||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
uniform float uTime;
|
||||
uniform mat4 uViewProjection;
|
||||
|
@ -11,17 +12,11 @@ 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 = cylindrical_distance(worldPos.xyz, uCameraPos);
|
||||
#endif
|
||||
vec4 FLWVertex(inout Vertex v) {
|
||||
FragDistance = cylindrical_distance(v.pos, uCameraPos);
|
||||
|
||||
gl_Position = uViewProjection * worldPos;
|
||||
return uViewProjection * vec4(v.pos, 1.);
|
||||
}
|
||||
|
||||
#elif defined(FRAGMENT_SHADER)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#use "flywheel:context/fog.glsl"
|
||||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
uniform float uTime;
|
||||
uniform mat4 uViewProjection;
|
||||
|
@ -10,15 +11,12 @@ uniform sampler2D uLightMap;
|
|||
|
||||
uniform vec2 uWindowSize;
|
||||
|
||||
void FLWFinalizeNormal(inout vec3 normal) {
|
||||
// noop
|
||||
}
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
void FLWFinalizeWorldPos(inout vec4 worldPos) {
|
||||
FragDistance = cylindrical_distance(worldPos.xyz, uCameraPos);
|
||||
|
||||
gl_Position = uViewProjection * worldPos;
|
||||
vec4 FLWVertex(inout Vertex v) {
|
||||
FragDistance = cylindrical_distance(v.pos, uCameraPos);
|
||||
|
||||
return uViewProjection * vec4(v.pos, 1.);
|
||||
}
|
||||
|
||||
#elif defined(FRAGMENT_SHADER)
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
struct Vertex {
|
||||
vec3 pos;
|
||||
vec2 texCoords;
|
||||
vec3 normal;
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
#use "flywheel:core/diffuse.glsl"
|
||||
#use "flywheel:data/modelvertex.glsl"
|
||||
#use "flywheel:block.frag"
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
|
||||
struct Instance {
|
||||
vec2 light;
|
||||
vec4 color;
|
||||
|
@ -9,26 +9,10 @@ struct Instance {
|
|||
mat3 normalMat;
|
||||
};
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
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 vertex(inout Vertex v, Instance i) {
|
||||
v.pos = (i.transform * vec4(v.pos, 1.)).xyz;
|
||||
v.normal = i.normalMat * v.normal;
|
||||
v.color = i.color;
|
||||
v.light = i.light;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#use "flywheel:core/quaternion.glsl"
|
||||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
#use "flywheel:data/modelvertex.glsl"
|
||||
#use "flywheel:block.frag"
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
#use "flywheel:core/quaternion.glsl"
|
||||
|
||||
struct Oriented {
|
||||
vec2 light;
|
||||
vec4 color;
|
||||
|
@ -12,24 +11,10 @@ struct Oriented {
|
|||
vec4 rotation;
|
||||
};
|
||||
|
||||
#if defined(VERTEX_SHADER)
|
||||
BlockFrag vertex(Vertex v, Oriented o) {
|
||||
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
||||
|
||||
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
||||
|
||||
FLWFinalizeWorldPos(worldPos);
|
||||
FLWFinalizeNormal(norm);
|
||||
|
||||
BlockFrag b;
|
||||
b.diffuse = diffuse(norm);
|
||||
b.texCoords = v.texCoords;
|
||||
b.light = o.light;
|
||||
#if defined(DEBUG_NORMAL)
|
||||
b.color = vec4(norm, 1.);
|
||||
#else
|
||||
b.color = o.color;
|
||||
#endif
|
||||
return b;
|
||||
void vertex(inout Vertex v, Oriented o) {
|
||||
v.pos = rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos;
|
||||
v.normal = rotateVertexByQuat(v.normal, o.rotation);
|
||||
v.color = o.color;
|
||||
v.light = o.light;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
#use "flywheel:core/matutils.glsl"
|
||||
#use "flywheel:core/quaternion.glsl"
|
||||
#use "flywheel:core/diffuse.glsl"
|
||||
|
||||
struct Oriented {
|
||||
// each vec 4 is 2 light coords packed <lo y, hi y>
|
||||
// x z
|
||||
vec4 lightA;// lo, lo
|
||||
vec4 lightB;// hi, lo
|
||||
vec4 lightC;// hi, hi
|
||||
vec4 lightD;// lo, hi
|
||||
|
||||
vec3 loCorner;
|
||||
vec3 size;
|
||||
|
||||
vec4 color;
|
||||
vec3 pos;
|
||||
vec3 pivot;
|
||||
vec4 rotation;
|
||||
};
|
||||
|
||||
#use "flywheel:data/modelvertex.glsl"
|
||||
#use "flywheel:block.frag"
|
||||
|
||||
BlockFrag vertex(Vertex v, Oriented o) {
|
||||
vec4 worldPos = vec4(rotateVertexByQuat(v.pos - o.pivot, o.rotation) + o.pivot + o.pos, 1.);
|
||||
|
||||
vec3 norm = rotateVertexByQuat(v.normal, o.rotation);
|
||||
|
||||
FLWFinalizeWorldPos(worldPos);
|
||||
FLWFinalizeNormal(norm);
|
||||
|
||||
// manual trilinear interpolation
|
||||
vec3 lightPos = (worldPos.xyz - o.loCorner) / o.size;
|
||||
|
||||
vec4 lightLoZ = mix(lightA, lightB, lightPos.x);// lo z
|
||||
vec4 lightHiZ = mix(lightD, lightC, lightPos.x);// hi z
|
||||
|
||||
vec4 lightE = mix(lightLoZ, lightHiZ, lightPos.z);// <lo y, hi y>
|
||||
|
||||
vec2 lightCoord = mix(lightE.xy, lightE.zw, lightPos.y);
|
||||
|
||||
BlockFrag b;
|
||||
b.diffuse = diffuse(norm);
|
||||
b.texCoords = v.texCoords;
|
||||
b.light = lightCoord;
|
||||
#if defined(DEBUG_NORMAL)
|
||||
b.color = vec4(norm, 1.);
|
||||
#else
|
||||
b.color = o.color;
|
||||
#endif
|
||||
return b;
|
||||
}
|
Loading…
Reference in a new issue