Reduced to fragments

- Overhaul shader pipeline
  - Remove VertexType#getShaderHeader in favor of vertex-only "layout
shaders"
  - Remove program specs in favor of vertex-only "instance shaders"
  - Add API GLSL headers for vertex and fragment shaders that store
common values
  - Separate context shaders into vertex and fragment shader files
- Improve import handling
  - Ensure imports are not added more than once
  - Change regex for import directives to be more in line with C-style
directives
- Move some classes and GLSL files
This commit is contained in:
PepperCode1 2022-05-15 01:58:33 -07:00
parent 4c7b035f5d
commit a11bf3537b
73 changed files with 476 additions and 700 deletions

View file

@ -13,6 +13,8 @@ import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.StitchedSprite;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.materials.InstanceShaders;
import com.jozufozu.flywheel.core.vertex.LayoutShaders;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
import com.jozufozu.flywheel.vanilla.VanillaInstances;
@ -73,6 +75,8 @@ public class Flywheel {
forgeEventBus.<ReloadRenderersEvent>addListener(ProgramCompiler::invalidateAll);
forgeEventBus.addListener(Models::onReload);
modEventBus.addListener(LayoutShaders::flwInit);
modEventBus.addListener(InstanceShaders::flwInit);
modEventBus.addListener(Contexts::flwInit);
modEventBus.addListener(PartialModel::onModelRegistry);
modEventBus.addListener(PartialModel::onModelBake);

View file

@ -1,8 +1,7 @@
package com.jozufozu.flywheel.api.struct;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import net.minecraft.resources.ResourceLocation;
import com.jozufozu.flywheel.core.source.FileResolution;
public interface Instanced<S> extends StructType<S> {
/**
@ -12,6 +11,5 @@ public interface Instanced<S> extends StructType<S> {
*/
StructWriter<S> getWriter(VecBuffer backing);
ResourceLocation getProgramSpec();
FileResolution getInstanceShader();
}

View file

@ -3,6 +3,7 @@ package com.jozufozu.flywheel.api.vertex;
import java.nio.ByteBuffer;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.source.FileResolution;
/**
* A vertex type containing metadata about a specific vertex layout.
@ -32,7 +33,7 @@ public interface VertexType {
*/
VertexList createReader(ByteBuffer buffer, int vertexCount);
String getShaderHeader();
FileResolution getLayoutShader();
default int getStride() {
return getLayout().getStride();

View file

@ -8,11 +8,9 @@ import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.ParallelTaskEngine;
import com.jozufozu.flywheel.config.BackendType;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.core.shader.ProgramSpec;
import com.mojang.logging.LogUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
@ -53,11 +51,6 @@ public class Backend {
return backendType == null ? "Uninitialized" : backendType.getProperName();
}
@Nullable
public static ProgramSpec getSpec(ResourceLocation name) {
return loader.get(name);
}
public static void refresh() {
backendType = chooseEngine();
}

View file

@ -1,32 +1,15 @@
package com.jozufozu.flywheel.backend;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.Nullable;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.crumbling.CrumblingRenderer;
import com.jozufozu.flywheel.core.shader.ProgramSpec;
import com.jozufozu.flywheel.core.source.Resolver;
import com.jozufozu.flywheel.core.source.ShaderSources;
import com.jozufozu.flywheel.event.GatherContextEvent;
import com.jozufozu.flywheel.util.ResourceUtil;
import com.jozufozu.flywheel.util.StringUtil;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
import net.minecraftforge.fml.ModLoader;
@ -39,11 +22,6 @@ import net.minecraftforge.fml.ModLoader;
* </p>
*/
public class Loader implements ResourceManagerReloadListener {
public static final String PROGRAM_DIR = "flywheel/programs/";
private static final Gson GSON = new GsonBuilder().create();
private final Map<ResourceLocation, ProgramSpec> programs = new HashMap<>();
private boolean firstLoad = true;
Loader() {
@ -57,11 +35,6 @@ public class Loader implements ResourceManagerReloadListener {
}
}
@Nullable
public ProgramSpec get(ResourceLocation name) {
return programs.get(name);
}
@Override
public void onResourceManagerReload(ResourceManager manager) {
Backend.refresh();
@ -74,8 +47,6 @@ public class Loader implements ResourceManagerReloadListener {
ShaderSources sources = new ShaderSources(manager);
loadProgramSpecs(manager);
Resolver.INSTANCE.run(sources);
Backend.LOGGER.info("Loaded all shader sources.");
@ -89,35 +60,4 @@ public class Loader implements ResourceManagerReloadListener {
firstLoad = false;
}
private void loadProgramSpecs(ResourceManager manager) {
programs.clear();
Collection<ResourceLocation> programSpecs = manager.listResources(PROGRAM_DIR, s -> s.endsWith(".json"));
for (ResourceLocation location : programSpecs) {
try (Resource file = manager.getResource(location)) {
String s = StringUtil.readToString(file.getInputStream());
ResourceLocation specName = ResourceUtil.trim(location, PROGRAM_DIR, ".json");
DataResult<Pair<ProgramSpec, JsonElement>> result = ProgramSpec.CODEC.decode(JsonOps.INSTANCE, GSON.fromJson(s, JsonElement.class));
ProgramSpec spec = result.get()
.orThrow()
.getFirst();
spec.setName(specName);
if (programs.containsKey(specName)) {
throw new IllegalStateException("Program spec '" + specName + "' already registered.");
}
programs.put(specName, spec);
} catch (Exception e) {
Backend.LOGGER.error("Could not load program " + location, e);
}
}
}
}

View file

@ -21,10 +21,20 @@ public class GlShader extends GlObject {
GlCompat.safeShaderSource(handle, source);
GL20.glCompileShader(handle);
// File dir = new File(Minecraft.getInstance().gameDirectory, "flywheel_sources");
// dir.mkdirs();
// File file = new File(dir, name.toString().replaceAll("[:/]", "_"));
// try (FileWriter writer = new FileWriter(file)) {
// writer.write(source);
// } catch (Exception e) {
// e.printStackTrace();
// }
// String log = GL20.glGetShaderInfoLog(handle);
//
// if (!log.isEmpty()) {
// env.printShaderInfoLog(source, log, this.name);
// System.out.println(log);
//// env.printShaderInfoLog(source, log, this.name);
// }
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {

View file

@ -5,7 +5,7 @@ import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.AbstractInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.materials.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.util.box.GridAlignedBB;

View file

@ -14,13 +14,14 @@ import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.backend.instancing.Engine;
import com.jozufozu.flywheel.backend.instancing.TaskEngine;
import com.jozufozu.flywheel.backend.model.MeshPool;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.CoreShaderInfoMap;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.compile.ProgramCompiler;
import com.jozufozu.flywheel.core.compile.ProgramContext;
import com.jozufozu.flywheel.core.shader.WorldProgram;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.util.Textures;
import com.jozufozu.flywheel.util.WeakHashSet;
import com.mojang.blaze3d.systems.RenderSystem;
@ -32,7 +33,6 @@ import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
public class InstancingEngine<P extends WorldProgram> implements Engine {
@ -118,7 +118,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
if (!toRender.isEmpty()) {
Instanced<? extends InstanceData> instanceType = entry.getKey();
setup(instanceType.getProgramSpec(), coreShaderInfo, camX, camY, camZ, viewProjection, level);
setup(instanceType, coreShaderInfo, camX, camY, camZ, viewProjection, level);
instanceCount += material.getInstanceCount();
vertexCount += material.getVertexCount();
@ -145,7 +145,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
return coreShaderInfo;
}
protected P setup(ResourceLocation programSpec, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
protected P setup(Instanced<?> instanceType, CoreShaderInfo coreShaderInfo, double camX, double camY, double camZ, Matrix4f viewProjection, ClientLevel level) {
float alphaDiscard = coreShaderInfo.alphaDiscard();
if (alphaDiscard == 0) {
alphaDiscard = 0.0001f;
@ -153,7 +153,7 @@ public class InstancingEngine<P extends WorldProgram> implements Engine {
alphaDiscard = 0;
}
P program = context.getProgram(ProgramContext.create(programSpec, Formats.POS_TEX_NORMAL, alphaDiscard, coreShaderInfo.fogType()));
P program = context.getProgram(new ProgramContext(Formats.POS_TEX_NORMAL, instanceType.getInstanceShader(), alphaDiscard, coreShaderInfo.fogType(), GameStateRegistry.takeSnapshot()));
program.bind();
program.uploadUniforms(camX, camY, camZ, viewProjection, level);

View file

@ -20,11 +20,13 @@ public class Contexts {
public static void flwInit(GatherContextEvent event) {
GameStateRegistry.register(NormalDebugStateProvider.INSTANCE);
FileResolution worldBuiltins = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.WORLD, ".glsl"));
FileResolution crumblingBuiltins = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.CRUMBLING, ".glsl"));
FileResolution worldVertex = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.WORLD, ".vert"));
FileResolution worldFragment = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.WORLD, ".frag"));
FileResolution crumblingVertex = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.CRUMBLING, ".vert"));
FileResolution crumblingFragment = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.CRUMBLING, ".frag"));
WORLD = ProgramCompiler.create(Templates.INSTANCING, WorldProgram::new, worldBuiltins);
CRUMBLING = ProgramCompiler.create(Templates.INSTANCING, CrumblingProgram::new, crumblingBuiltins);
WORLD = ProgramCompiler.create(WorldProgram::new, Templates.INSTANCING, Templates.FRAGMENT, worldVertex, worldFragment);
CRUMBLING = ProgramCompiler.create(CrumblingProgram::new, Templates.INSTANCING, Templates.FRAGMENT, crumblingVertex, crumblingFragment);
}
public static class Names {

View file

@ -1,10 +0,0 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.Flywheel;
import net.minecraft.resources.ResourceLocation;
public class Programs {
public static final ResourceLocation TRANSFORMED = Flywheel.rl("model");
public static final ResourceLocation ORIENTED = Flywheel.rl("oriented");
}

View file

@ -12,12 +12,11 @@ public class CompileUtil {
public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$");
protected static String generateHeader(GLSLVersion version, ShaderType type) {
return "#version "
+ version
+ '\n'
return "#version " + version + '\n'
+ "#extension GL_ARB_explicit_attrib_location : enable\n"
+ "#extension GL_ARB_conservative_depth : enable\n"
+ type.getDefineStatement();
+ type.getDefineStatement()
+ '\n';
}
public static int getElementCount(String type) {

View file

@ -9,15 +9,14 @@ import com.jozufozu.flywheel.core.shader.ShaderConstants;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import com.jozufozu.flywheel.core.source.FileIndexImpl;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.SourceFile;
public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShader> {
private final Template<? extends FragmentData> template;
private final FileResolution header;
private final FileResolution contextShader;
public FragmentCompiler(Template<? extends FragmentData> template, FileResolution header) {
this.header = header;
public FragmentCompiler(Template<? extends FragmentData> template, FileResolution contextShader) {
this.template = template;
this.contextShader = contextShader;
}
@Override
@ -27,16 +26,16 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
finalSource.append(CompileUtil.generateHeader(template.getVersion(), ShaderType.FRAGMENT));
key.getShaderConstants().writeInto(finalSource);
finalSource.append('\n');
FileIndexImpl index = new FileIndexImpl();
header.getFile().generateFinalSource(index, finalSource);
key.file.generateFinalSource(index, finalSource);
contextShader.getFile().generateFinalSource(index, finalSource);
FragmentData appliedTemplate = template.apply(key.file);
FragmentData appliedTemplate = template.apply(contextShader.getFile());
finalSource.append(appliedTemplate.generateFooter());
return new GlShader(key.file.name, ShaderType.FRAGMENT, finalSource.toString());
return new GlShader(contextShader.getFile().name, ShaderType.FRAGMENT, finalSource.toString());
}
@Override
@ -48,11 +47,6 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
* Represents the conditions under which a shader is compiled.
*/
public static final class Context {
/**
* The file to compile.
*/
private final SourceFile file;
/**
* The shader constants to apply.
*/
@ -68,11 +62,10 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
*/
private final FogType fogType;
public Context(SourceFile file, StateSnapshot ctx, float alphaDiscard, FogType fogType) {
this.file = file;
this.ctx = ctx;
public Context(float alphaDiscard, FogType fogType, StateSnapshot ctx) {
this.alphaDiscard = alphaDiscard;
this.fogType = fogType;
this.ctx = ctx;
}
public ShaderConstants getShaderConstants() {
@ -91,17 +84,17 @@ public class FragmentCompiler extends Memoizer<FragmentCompiler.Context, GlShade
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (Context) obj;
return this.file == that.file && Objects.equals(this.ctx, that.ctx) && Float.floatToIntBits(this.alphaDiscard) == Float.floatToIntBits(that.alphaDiscard) && fogType == that.fogType;
return Objects.equals(this.ctx, that.ctx) && Float.floatToIntBits(this.alphaDiscard) == Float.floatToIntBits(that.alphaDiscard) && fogType == that.fogType;
}
@Override
public int hashCode() {
return Objects.hash(file, ctx, alphaDiscard, fogType);
return Objects.hash(alphaDiscard, fogType, ctx);
}
@Override
public String toString() {
return "Context[" + "file=" + file + ", " + "ctx=" + ctx + ", " + "alphaDiscard=" + alphaDiscard + ", " + "fogType=" + fogType + ']';
return "Context[" + "alphaDiscard=" + alphaDiscard + ", " + "fogType=" + fogType + ", " + "ctx=" + ctx + ']';
}
}

View file

@ -3,85 +3,46 @@ package com.jozufozu.flywheel.core.compile;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import com.jozufozu.flywheel.core.source.ShaderLoadingException;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.source.parse.ShaderFunction;
import com.jozufozu.flywheel.core.source.parse.ShaderStruct;
import com.jozufozu.flywheel.core.source.parse.StructField;
import com.jozufozu.flywheel.core.source.parse.Variable;
import com.jozufozu.flywheel.core.source.span.Span;
public class FragmentTemplateData implements FragmentData {
public final SourceFile file;
public final Span interpolantName;
public final ShaderStruct interpolant;
public final ShaderFunction fragmentMain;
public final ShaderFunction contextFragment;
public FragmentTemplateData(SourceFile file) {
this.file = file;
Optional<ShaderFunction> maybeFragmentMain = file.findFunction("fragment");
Optional<ShaderFunction> maybeContextFragment = file.findFunction("flw_contextFragment");
if (maybeFragmentMain.isEmpty()) {
ErrorReporter.generateMissingFunction(file, "fragment", "\"fragment\" function not defined");
throw new RuntimeException();
if (maybeContextFragment.isEmpty()) {
ErrorReporter.generateMissingFunction(file, "flw_contextFragment", "\"flw_contextFragment\" function not defined");
throw new ShaderLoadingException();
}
fragmentMain = maybeFragmentMain.get();
ImmutableList<Variable> fragmentParameters = fragmentMain.getParameters();
contextFragment = maybeContextFragment.get();
ImmutableList<Variable> params = contextFragment.getParameters();
if (fragmentParameters.size() != 1) {
ErrorReporter.generateSpanError(fragmentMain.getArgs(), "fragment function must have exactly one argument");
throw new RuntimeException();
if (params.size() != 0) {
ErrorReporter.generateSpanError(contextFragment.getArgs(), "\"flw_contextFragment\" function must not have any arguments");
throw new ShaderLoadingException();
}
interpolantName = fragmentMain.getParameters().get(0).type;
Optional<ShaderStruct> maybeInterpolant = file.findStruct(interpolantName);
if (maybeInterpolant.isEmpty()) {
ErrorReporter.generateMissingStruct(file, interpolantName, "struct not defined");
throw new RuntimeException();
}
interpolant = maybeInterpolant.get();
}
@Override
public String generateFooter() {
StringBuilder builder = new StringBuilder();
prefixFields(builder, interpolant, "in", "v2f_");
builder.append(String.format("""
builder.append("""
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);
flw_contextFragment();
}
""",
fragmentMain.call("o")
));
"""
);
return builder.toString();
}
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");
}
}
}

View file

@ -15,45 +15,30 @@ import com.jozufozu.flywheel.core.source.parse.Variable;
import com.jozufozu.flywheel.core.source.span.Span;
public class InstancingTemplateData implements VertexData {
public final SourceFile file;
public final ShaderFunction vertexMain;
public final Span vertexName;
public final ShaderFunction instanceVertex;
public final Span instanceName;
public final ShaderStruct instance;
public InstancingTemplateData(SourceFile file) {
this.file = file;
Optional<ShaderFunction> vertexFunc = file.findFunction("vertex");
Optional<ShaderFunction> maybeInstanceVertex = file.findFunction("flw_instanceVertex");
if (vertexFunc.isEmpty()) {
ErrorReporter.generateFileError(file, "could not find \"vertex\" function");
if (maybeInstanceVertex.isEmpty()) {
ErrorReporter.generateMissingFunction(file, "flw_instanceVertex", "\"flw_instanceVertex\" function not defined");
throw new ShaderLoadingException();
}
vertexMain = vertexFunc.get();
ImmutableList<Variable> vertexParams = vertexMain.getParameters();
instanceVertex = maybeInstanceVertex.get();
ImmutableList<Variable> params = instanceVertex.getParameters();
if (vertexParams.size() != 2) {
ErrorReporter.generateSpanError(vertexMain.getArgs(), "instancing requires vertex function to have 2 arguments");
if (params.size() != 1) {
ErrorReporter.generateSpanError(instanceVertex.getArgs(), "\"flw_contextFragment\" function must have exactly 1 argument");
throw new ShaderLoadingException();
}
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();
}
instanceName = vertexParams.get(1).type;
instanceName = params.get(0).type;
Optional<ShaderStruct> maybeInstance = file.findStruct(instanceName);
if (maybeInstance.isEmpty()) {
@ -80,36 +65,26 @@ public class InstancingTemplateData implements VertexData {
.append(' ')
.append(field.type)
.append(' ')
.append("a_i_")
.append("_flw_a_i_")
.append(field.name)
.append(";\n");
attributeBinding += CompileUtil.getAttributeCount(field.type);
}
template.append('\n');
template.append(String.format("""
out vec4 v2f_color;
out vec2 v2f_texCoords;
out vec2 v2f_light;
out float v2f_diffuse;
void main() {
Vertex v = FLWCreateVertex();
%s i;
%s
vertex(v, i);
gl_Position = FLWVertex(v);
v.normal = normalize(v.normal);
flw_layoutVertex();
v2f_color = v.color;
v2f_texCoords = v.texCoords;
v2f_light = v.light;
v2f_diffuse = FLWDiffuse(v.normal);
#if defined(DEBUG_NORMAL)
v2f_color = vec4(v.normal, 1.);
#endif
%s instance;
%s
flw_instanceVertex(instance);
flw_contextVertex();
}
""",
instanceName,
assignFields(instance, "i.", "a_i_")
assignFields(instance, "instance.", "_flw_a_i_")
));
return template.toString();

View file

@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.List;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.core.Templates;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
@ -33,15 +32,17 @@ public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramContex
}
/**
* Creates a program compiler using this template.
* @param template The vertex template to use.
* Creates a program compiler using provided templates and headers.
* @param factory A factory to add meaning to compiled programs.
* @param header The header file to use for the program.
* @param vertexTemplate The vertex template to use.
* @param fragmentTemplate The fragment template to use.
* @param vertexContextShader The context shader to use when compiling vertex shaders.
* @param fragmentContextShader The context shader to use when compiling fragment shaders.
* @param <P> The type of program to compile.
* @return A program compiler.
*/
public static <T extends VertexData, P extends GlProgram> ProgramCompiler<P> create(Template<T> template, GlProgram.Factory<P> factory, FileResolution header) {
return new ProgramCompiler<>(factory, new VertexCompiler(template, header), new FragmentCompiler(Templates.FRAGMENT, header));
public static <V extends VertexData, F extends FragmentData, P extends GlProgram> ProgramCompiler<P> create(GlProgram.Factory<P> factory, Template<V> vertexTemplate, Template<F> fragmentTemplate, FileResolution vertexContextShader, FileResolution fragmentContextShader) {
return new ProgramCompiler<>(factory, new VertexCompiler(vertexTemplate, vertexContextShader), new FragmentCompiler(fragmentTemplate, fragmentContextShader));
}
/**
@ -63,9 +64,9 @@ public class ProgramCompiler<P extends GlProgram> extends Memoizer<ProgramContex
@Override
protected P _create(ProgramContext ctx) {
return new ProgramAssembler(ctx.spec.name)
.attachShader(vertexCompiler.get(new VertexCompiler.Context(ctx.spec.getVertexFile(), ctx.ctx, ctx.vertexType)))
.attachShader(fragmentCompiler.get(new FragmentCompiler.Context(ctx.spec.getFragmentFile(), ctx.ctx, ctx.alphaDiscard, ctx.fogType)))
return new ProgramAssembler(ctx.instanceShader.getFileLoc())
.attachShader(vertexCompiler.get(new VertexCompiler.Context(ctx.vertexType, ctx.instanceShader.getFile(), ctx.ctx)))
.attachShader(fragmentCompiler.get(new FragmentCompiler.Context(ctx.alphaDiscard, ctx.fogType, ctx.ctx)))
.link()
.build(this.factory);
}

View file

@ -3,54 +3,33 @@ package com.jozufozu.flywheel.core.compile;
import java.util.Objects;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo.FogType;
import com.jozufozu.flywheel.core.GameStateRegistry;
import com.jozufozu.flywheel.core.shader.ProgramSpec;
import com.jozufozu.flywheel.core.shader.StateSnapshot;
import net.minecraft.resources.ResourceLocation;
import com.jozufozu.flywheel.core.source.FileResolution;
/**
* Represents the entire context of a program's usage.
*/
public final class ProgramContext {
/**
* Creates a compilation context for the given program, vertex type and render layer.
*
* @param programName The name of the program to use.
* @param vertexType The vertex type to use.
* @param alphaDiscard The alpha threshold below which pixels are discarded.
* @return A compilation context.
*/
public static ProgramContext create(ResourceLocation programName, VertexType vertexType, float alphaDiscard, FogType fogType) {
ProgramSpec spec = Backend.getSpec(programName);
if (spec == null) {
throw new NullPointerException("Cannot compile shader because '" + programName + "' is not recognized.");
}
return new ProgramContext(spec, alphaDiscard, fogType, vertexType, GameStateRegistry.takeSnapshot());
}
public final ProgramSpec spec;
public final VertexType vertexType;
public final FileResolution instanceShader;
public final float alphaDiscard;
public final FogType fogType;
public final VertexType vertexType;
public final StateSnapshot ctx;
/**
* @param vertexType The vertexType the program should be adapted for.
* @param spec The program to use.
* @param alphaDiscard Alpha threshold below which pixels are discarded.
* @param fogType Which type of fog should be applied.
* @param vertexType The vertexType the program should be adapted for.
* @param ctx A snapshot of the game state.
*/
public ProgramContext(ProgramSpec spec, float alphaDiscard, FogType fogType, VertexType vertexType, StateSnapshot ctx) {
this.spec = spec;
public ProgramContext(VertexType vertexType, FileResolution instanceShader, float alphaDiscard, FogType fogType, StateSnapshot ctx) {
this.vertexType = vertexType;
this.instanceShader = instanceShader;
this.alphaDiscard = alphaDiscard;
this.fogType = fogType;
this.vertexType = vertexType;
this.ctx = ctx;
}
@ -59,16 +38,16 @@ public final class ProgramContext {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
var that = (ProgramContext) o;
return spec == that.spec && vertexType == that.vertexType && ctx.equals(that.ctx) && Float.floatToIntBits(alphaDiscard) == Float.floatToIntBits(that.alphaDiscard) && fogType == that.fogType;
return instanceShader == that.instanceShader && vertexType == that.vertexType && ctx.equals(that.ctx) && Float.floatToIntBits(alphaDiscard) == Float.floatToIntBits(that.alphaDiscard) && fogType == that.fogType;
}
@Override
public int hashCode() {
return Objects.hash(spec, alphaDiscard, fogType, vertexType, ctx);
return Objects.hash(vertexType, instanceShader, alphaDiscard, fogType, ctx);
}
@Override
public String toString() {
return "ProgramContext{" + "spec=" + spec + ", alphaDiscard=" + alphaDiscard + ", fogType=" + fogType + ", vertexType=" + vertexType + ", ctx=" + ctx + '}';
return "ProgramContext{" + "vertexType=" + vertexType + ", instanceShader=" + instanceShader + ", alphaDiscard=" + alphaDiscard + ", fogType=" + fogType + ", ctx=" + ctx + '}';
}
}

View file

@ -12,11 +12,11 @@ import com.jozufozu.flywheel.core.source.SourceFile;
public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
private final Template<? extends VertexData> template;
private final FileResolution header;
private final FileResolution contextShader;
public VertexCompiler(Template<? extends VertexData> template, FileResolution header) {
public VertexCompiler(Template<? extends VertexData> template, FileResolution contextShader) {
this.template = template;
this.header = header;
this.contextShader = contextShader;
}
@Override
@ -26,28 +26,21 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
finalSource.append(CompileUtil.generateHeader(template.getVersion(), ShaderType.VERTEX));
key.ctx.getShaderConstants().writeInto(finalSource);
finalSource.append("""
struct Vertex {
vec3 pos;
vec4 color;
vec2 texCoords;
vec2 light;
vec3 normal;
};
""");
finalSource.append(key.vertexType.getShaderHeader());
finalSource.append('\n');
FileIndexImpl index = new FileIndexImpl();
header.getFile().generateFinalSource(index, finalSource);
FileResolution layoutShader = key.vertexType.getLayoutShader();
layoutShader.getFile().generateFinalSource(index, finalSource);
key.file.generateFinalSource(index, finalSource);
contextShader.getFile().generateFinalSource(index, finalSource);
VertexData appliedTemplate = template.apply(key.file);
key.instanceShader.generateFinalSource(index, finalSource);
VertexData appliedTemplate = template.apply(key.instanceShader);
finalSource.append(appliedTemplate.generateFooter(index, key.vertexType));
return new GlShader(key.file.name, ShaderType.VERTEX, finalSource.toString());
return new GlShader(key.instanceShader.name, ShaderType.VERTEX, finalSource.toString());
}
@Override
@ -57,24 +50,24 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
public static class Context {
/**
* The file to compile.
* The vertex type to use.
*/
private final SourceFile file;
private final VertexType vertexType;
/**
* The instance shader source.
*/
private final SourceFile instanceShader;
/**
* The shader constants to apply.
*/
private final StateSnapshot ctx;
/**
* The vertex type to use.
*/
private final VertexType vertexType;
public Context(SourceFile file, StateSnapshot ctx, VertexType vertexType) {
this.file = file;
this.ctx = ctx;
public Context(VertexType vertexType, SourceFile instanceShader, StateSnapshot ctx) {
this.vertexType = vertexType;
this.instanceShader = instanceShader;
this.ctx = ctx;
}
@Override
@ -82,12 +75,12 @@ public class VertexCompiler extends Memoizer<VertexCompiler.Context, GlShader> {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
var that = (Context) o;
return file == that.file && vertexType == that.vertexType && ctx.equals(that.ctx);
return vertexType == that.vertexType && instanceShader == that.instanceShader && ctx.equals(that.ctx);
}
@Override
public int hashCode() {
return Objects.hash(file, ctx, vertexType);
return Objects.hash(vertexType, instanceShader, ctx);
}
}
}

View file

@ -14,8 +14,8 @@ import com.jozufozu.flywheel.backend.instancing.SerialTaskEngine;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancedMaterial;
import com.jozufozu.flywheel.backend.instancing.instancing.InstancingEngine;
import com.jozufozu.flywheel.core.Contexts;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.core.CoreShaderInfoMap.CoreShaderInfo;
import com.jozufozu.flywheel.core.RenderContext;
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.mixin.LevelRendererAccessor;
import com.jozufozu.flywheel.util.Lazy;
@ -191,7 +191,7 @@ public class CrumblingRenderer {
CoreShaderInfo coreShaderInfo = getCoreShaderInfo();
for (Map.Entry<Instanced<? extends InstanceData>, InstancedMaterial<?>> entry : materials.entrySet()) {
CrumblingProgram program = setup(entry.getKey().getProgramSpec(), coreShaderInfo, camX, camY, camZ, viewProjection, level);
CrumblingProgram program = setup(entry.getKey(), coreShaderInfo, camX, camY, camZ, viewProjection, level);
program.setAtlasSize(width, height);

View file

@ -3,8 +3,8 @@ package com.jozufozu.flywheel.core.hardcoded;
import java.util.List;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.blaze3d.platform.MemoryTracker;

View file

@ -0,0 +1,24 @@
package com.jozufozu.flywheel.core.materials;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.Resolver;
import com.jozufozu.flywheel.event.GatherContextEvent;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class InstanceShaders {
public static FileResolution MODEL;
public static FileResolution ORIENTED;
public static void flwInit(GatherContextEvent event) {
MODEL = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.MODEL, ".vert"));
ORIENTED = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.ORIENTED, ".vert"));
}
public static class Names {
public static final ResourceLocation MODEL = Flywheel.rl("instance/model");
public static final ResourceLocation ORIENTED = Flywheel.rl("instance/oriented");
}
}

View file

@ -1,22 +1,12 @@
package com.jozufozu.flywheel.core;
package com.jozufozu.flywheel.core.materials;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.model.ModelType;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.core.materials.oriented.OrientedType;
import net.minecraft.resources.ResourceLocation;
public class Materials {
public static final StructType<OrientedData> ORIENTED = new OrientedType();
public static final StructType<ModelData> TRANSFORMED = new ModelType();
public static class Names {
public static final ResourceLocation MODEL = Flywheel.rl("model");
public static final ResourceLocation ORIENTED = Flywheel.rl("oriented");
public static final ResourceLocation PASSTHRU = Flywheel.rl("passthru");
}
public static final StructType<OrientedData> ORIENTED = new OrientedType();
}

View file

@ -4,12 +4,11 @@ import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.materials.InstanceShaders;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import net.minecraft.resources.ResourceLocation;
import com.jozufozu.flywheel.core.source.FileResolution;
public class ModelType implements Instanced<ModelData>, Batched<ModelData> {
@ -34,8 +33,8 @@ public class ModelType implements Instanced<ModelData>, Batched<ModelData> {
}
@Override
public ResourceLocation getProgramSpec() {
return Programs.TRANSFORMED;
public FileResolution getInstanceShader() {
return InstanceShaders.MODEL;
}
@Override

View file

@ -4,14 +4,13 @@ import com.jozufozu.flywheel.api.struct.Batched;
import com.jozufozu.flywheel.api.struct.Instanced;
import com.jozufozu.flywheel.api.struct.StructWriter;
import com.jozufozu.flywheel.backend.gl.buffer.VecBuffer;
import com.jozufozu.flywheel.core.Programs;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.materials.InstanceShaders;
import com.jozufozu.flywheel.core.model.ModelTransformer;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.math.Quaternion;
import net.minecraft.resources.ResourceLocation;
public class OrientedType implements Instanced<OrientedData>, Batched<OrientedData> {
public static final BufferLayout FORMAT = BufferLayout.builder()
@ -35,8 +34,8 @@ public class OrientedType implements Instanced<OrientedData>, Batched<OrientedDa
}
@Override
public ResourceLocation getProgramSpec() {
return Programs.ORIENTED;
public FileResolution getInstanceShader() {
return InstanceShaders.ORIENTED;
}
@Override

View file

@ -1,8 +1,8 @@
package com.jozufozu.flywheel.core.model;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.PartialModel;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;

View file

@ -8,7 +8,7 @@ import java.util.Random;
import java.util.function.Function;
import com.google.common.collect.ImmutableMap;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.vertex.Formats;
import com.jozufozu.flywheel.core.virtual.VirtualEmptyBlockGetter;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;

View file

@ -1,68 +0,0 @@
package com.jozufozu.flywheel.core.shader;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.Resolver;
import com.jozufozu.flywheel.core.source.SourceFile;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.resources.ResourceLocation;
/**
* An object describing a shader program that can be loaded by flywheel.
*
* <p>
* These are defined through json. All ProgramSpecs in {@code assets/modid/flywheel/programs} are parsed and
* processed. One ProgramSpec typically specifies one "material" that can be used in game to render things.
* </p>
* <p>
* All shader source files in {@code assets/modid/flywheel/shaders} are completely loaded and parsed into
* {@link SourceFile SourceFiles}, but not compiled until one of them is
* referenced by a ProgramSpec.
* </p>
*/
public class ProgramSpec {
public static final Codec<ProgramSpec> CODEC = RecordCodecBuilder.create(instance -> instance.group(
ResourceLocation.CODEC.fieldOf("vertex")
.forGetter(ProgramSpec::getSourceLoc),
ResourceLocation.CODEC.fieldOf("fragment")
.forGetter(ProgramSpec::getFragmentLoc))
.apply(instance, ProgramSpec::new));
public ResourceLocation name;
public final FileResolution vertex;
public final FileResolution fragment;
public ProgramSpec(ResourceLocation vertex, ResourceLocation fragment) {
this.vertex = Resolver.INSTANCE.get(vertex);
this.fragment = Resolver.INSTANCE.get(fragment);
}
public void setName(ResourceLocation name) {
this.name = name;
this.vertex.addSpec(name);
this.fragment.addSpec(name);
}
public ResourceLocation getSourceLoc() {
return vertex.getFileLoc();
}
public ResourceLocation getFragmentLoc() {
return fragment.getFileLoc();
}
public SourceFile getVertexFile() {
return vertex.getFile();
}
public SourceFile getFragmentFile() {
return fragment.getFile();
}
@Override
public String toString() {
return name.toString();
}
}

View file

@ -11,6 +11,8 @@ public interface FileIndex {
*/
int getFileID(SourceFile sourceFile);
boolean exists(SourceFile sourceFile);
SourceFile getFile(int fileID);
default Span getLineSpan(int fileId, int lineNo) {

View file

@ -31,12 +31,16 @@ public class FileIndexImpl implements FileIndex {
return size;
}
@Override
public boolean exists(SourceFile sourceFile) {
return files.indexOf(sourceFile) != -1;
}
@Override
public SourceFile getFile(int fileId) {
return files.get(fileId);
}
public void printShaderInfoLog(String source, String log, ResourceLocation name) {
List<String> lines = log.lines()
.toList();

View file

@ -93,4 +93,9 @@ public class FileResolution {
extraCrashInfoProviders.clear();
file = null;
}
@Override
public String toString() {
return "FileResolution[" + fileLoc + "]";
}
}

View file

@ -53,7 +53,6 @@ public class ShaderSources implements SourceFinder {
@Override
@Nullable
public SourceFile findSource(ResourceLocation name) {
return shaderSources.get(name);
}
}

View file

@ -2,11 +2,12 @@ package com.jozufozu.flywheel.core.source;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@ -27,10 +28,6 @@ import net.minecraft.resources.ResourceLocation;
* </p>
*/
public class SourceFile {
private static final Pattern includePattern = Pattern.compile("#use \"(.*)\"");
// https://regexr.com/60n3d
public static final Pattern functionDeclaration = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
public final ResourceLocation name;
@ -141,15 +138,20 @@ public class SourceFile {
for (Import include : imports) {
SourceFile file = include.getFile();
if (file != null) file.generateFinalSource(env, source);
if (file != null && !env.exists(file)) {
file.generateFinalSource(env, source);
}
}
source.append("#line ")
.append(0)
.append(' ')
.append(env.getFileID(this))
.append(" // ")
.append(name)
.append('\n');
source.append(elided);
source.append('\n');
}
public String printSource() {
@ -176,7 +178,7 @@ public class SourceFile {
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
*/
private ImmutableMap<String, ShaderFunction> parseFunctions() {
Matcher matcher = functionDeclaration.matcher(source);
Matcher matcher = ShaderFunction.functionDeclaration.matcher(source);
Map<String, ShaderFunction> functions = new HashMap<>();
@ -210,7 +212,7 @@ public class SourceFile {
* Scan the source for function definitions and "parse" them into objects that contain properties of the function.
*/
private ImmutableMap<String, ShaderStruct> parseStructs() {
Matcher matcher = ShaderStruct.struct.matcher(source);
Matcher matcher = ShaderStruct.PATTERN.matcher(source);
ImmutableMap.Builder<String, ShaderStruct> structs = ImmutableMap.builder();
while (matcher.find()) {
@ -232,15 +234,22 @@ public class SourceFile {
* @param elisions
*/
private ImmutableList<Import> parseImports(List<Span> elisions) {
Matcher uses = includePattern.matcher(source);
Matcher uses = Import.PATTERN.matcher(source);
Set<String> importedFiles = new HashSet<>();
List<Import> imports = new ArrayList<>();
while (uses.find()) {
Span use = Span.fromMatcher(this, uses);
Span file = Span.fromMatcher(this, uses, 1);
imports.add(new Import(Resolver.INSTANCE, use, file));
String fileName = file.get();
if (importedFiles.add(fileName)) {
Import import1 = Import.create(Resolver.INSTANCE, use, file);
if (import1 != null) {
imports.add(import1);
}
}
elisions.add(use); // we have to trim that later
}

View file

@ -25,7 +25,6 @@ public class ErrorReporter {
}
public static void generateFileError(SourceFile file, String message) {
String error = ErrorBuilder.error(message)
.pointAtFile(file)
.build();

View file

@ -1,8 +1,7 @@
package com.jozufozu.flywheel.core.source.parse;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.regex.Pattern;
import org.jetbrains.annotations.Nullable;
@ -12,34 +11,31 @@ import com.jozufozu.flywheel.core.source.SourceFile;
import com.jozufozu.flywheel.core.source.error.ErrorReporter;
import com.jozufozu.flywheel.core.source.span.Span;
import net.minecraft.ResourceLocationException;
import net.minecraft.resources.ResourceLocation;
public class Import extends AbstractShaderElement {
public static final List<Import> IMPORTS = new ArrayList<>();
private final Span file;
public static final Pattern PATTERN = Pattern.compile("^\\s*#\\s*use\\s+\"(.*)\"", Pattern.MULTILINE);
private final FileResolution resolution;
public Import(Resolver resolver, Span self, Span file) {
protected Import(Span self, FileResolution resolution, Span file) {
super(self);
this.file = file;
resolution = resolver.get(toRL(file))
.addSpan(file);
IMPORTS.add(this);
this.resolution = resolution.addSpan(file);
}
private ResourceLocation toRL(Span file) {
@Nullable
public static Import create(Resolver resolver, Span self, Span file) {
ResourceLocation fileLocation;
try {
return new ResourceLocation(file.get());
} catch (RuntimeException error) {
ErrorReporter.generateSpanError(file, "malformed source name");
fileLocation = new ResourceLocation(file.get());
} catch (ResourceLocationException e) {
ErrorReporter.generateSpanError(file, "malformed source location");
return null;
}
return new ResourceLocation("");
return new Import(self, resolver.get(fileLocation), file);
}
public FileResolution getResolution() {

View file

@ -9,6 +9,9 @@ import com.jozufozu.flywheel.core.source.span.Span;
public class ShaderFunction extends AbstractShaderElement {
// https://regexr.com/60n3d
public static final Pattern functionDeclaration = Pattern.compile("(\\w+)\\s+(\\w+)\\s*\\(([\\w,\\s]*)\\)\\s*\\{");
public static final Pattern argument = Pattern.compile("(?:(inout|in|out) )?(\\w+)\\s+(\\w+)");
public static final Pattern assignment = Pattern.compile("(\\w+)\\s*=");

View file

@ -10,7 +10,7 @@ import com.jozufozu.flywheel.core.source.span.Span;
public class ShaderStruct extends AbstractShaderElement {
// https://regexr.com/61rpe
public static final Pattern struct = Pattern.compile("struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d\\s,;]*)}\\s*;\\s");
public static final Pattern PATTERN = Pattern.compile("struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d\\s,;]*)}\\s*;\\s");
public final Span name;
public final Span body;

View file

@ -7,6 +7,7 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.datafixers.util.Pair;
@ -38,24 +39,8 @@ public class BlockVertex implements VertexType {
}
@Override
public String getShaderHeader() {
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 FileResolution getLayoutShader() {
return LayoutShaders.BLOCK;
}
public BlockVertexListUnsafe.Shaded createReader(ByteBuffer buffer, int vertexCount, int unshadedStartVertex) {

View file

@ -5,7 +5,6 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.ShadeSeparatedBufferBuilder;
import com.jozufozu.flywheel.util.RenderMath;
import com.mojang.blaze3d.vertex.BufferBuilder;

View file

@ -7,7 +7,6 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.ShadedVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.RenderMath;
public class BlockVertexListUnsafe implements VertexList {

View file

@ -1,10 +1,6 @@
package com.jozufozu.flywheel.core;
import com.jozufozu.flywheel.core.vertex.BlockVertex;
import com.jozufozu.flywheel.core.vertex.PosTexNormalVertex;
package com.jozufozu.flywheel.core.vertex;
public class Formats {
public static final PosTexNormalVertex POS_TEX_NORMAL = new PosTexNormalVertex();
public static final BlockVertex BLOCK = new BlockVertex();
public static final PosTexNormalVertex POS_TEX_NORMAL = new PosTexNormalVertex();
}

View file

@ -0,0 +1,24 @@
package com.jozufozu.flywheel.core.vertex;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.core.source.FileResolution;
import com.jozufozu.flywheel.core.source.Resolver;
import com.jozufozu.flywheel.event.GatherContextEvent;
import com.jozufozu.flywheel.util.ResourceUtil;
import net.minecraft.resources.ResourceLocation;
public class LayoutShaders {
public static FileResolution BLOCK;
public static FileResolution POS_TEX_NORMAL;
public static void flwInit(GatherContextEvent event) {
BLOCK = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.BLOCK, ".vert"));
POS_TEX_NORMAL = Resolver.INSTANCE.get(ResourceUtil.subPath(Names.POS_TEX_NORMAL, ".vert"));
}
public static class Names {
public static final ResourceLocation BLOCK = Flywheel.rl("layout/block");
public static final ResourceLocation POS_TEX_NORMAL = Flywheel.rl("layout/pos_tex_normal");
}
}

View file

@ -5,6 +5,7 @@ import java.nio.ByteBuffer;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.layout.BufferLayout;
import com.jozufozu.flywheel.core.layout.CommonItems;
import com.jozufozu.flywheel.core.source.FileResolution;
public class PosTexNormalVertex implements VertexType {
@ -28,21 +29,7 @@ public class PosTexNormalVertex implements VertexType {
}
@Override
public String getShaderHeader() {
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;
}
""";
public FileResolution getLayoutShader() {
return LayoutShaders.POS_TEX_NORMAL;
}
}

View file

@ -6,7 +6,6 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.util.RenderMath;
public class PosTexNormalVertexListUnsafe implements VertexList {

View file

@ -6,8 +6,8 @@ import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.Materials;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.mojang.math.Quaternion;

View file

@ -9,8 +9,8 @@ import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.jozufozu.flywheel.util.AnimationTickHolder;

View file

@ -7,9 +7,9 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.Models;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.core.model.Mesh;
import com.jozufozu.flywheel.util.AnimationTickHolder;

View file

@ -6,8 +6,8 @@ import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstance;
import com.jozufozu.flywheel.core.BasicModelSupplier;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.hardcoded.ModelPart;
import com.jozufozu.flywheel.core.materials.Materials;
import com.jozufozu.flywheel.core.materials.model.ModelData;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.transform.TransformStack;

View file

@ -1,4 +0,0 @@
{
"vertex": "flywheel:model.vert",
"fragment": "flywheel:block.frag"
}

View file

@ -1,4 +0,0 @@
{
"vertex": "flywheel:oriented.vert",
"fragment": "flywheel:block.frag"
}

View file

@ -1,4 +0,0 @@
{
"vertex": "flywheel:passthru.vert",
"fragment": "flywheel:block.frag"
}

View file

@ -0,0 +1,12 @@
in vec4 flw_vertexPos;
in vec4 flw_vertexColor;
in vec2 flw_vertexTexCoord;
in vec2 flw_vertexLight;
in vec3 flw_vertexNormal;
in float flw_distance;
in vec4 flw_var0;
in vec4 flw_var1;
in vec4 flw_var2;
in vec4 flw_var3;

View file

@ -0,0 +1,12 @@
out vec4 flw_vertexPos;
out vec4 flw_vertexColor;
out vec2 flw_vertexTexCoord;
out vec2 flw_vertexLight;
out vec3 flw_vertexNormal;
out float flw_distance;
out vec4 flw_var0;
out vec4 flw_var1;
out vec4 flw_var2;
out vec4 flw_var3;

View file

@ -1,13 +0,0 @@
struct Fragment {
vec2 texCoords;
vec4 color;
float diffuse;
vec2 light;
};
vec4 fragment(Fragment r) {
vec4 tex = FLWBlockTexture(r.texCoords);
return vec4(tex.rgb * FLWLight(r.light).rgb * r.diffuse, tex.a) * r.color;
}

View file

@ -0,0 +1,26 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/diffuse.glsl"
#use "flywheel:util/fog.glsl"
uniform mat4 uViewProjection;
uniform vec3 uCameraPos;
uniform int uConstantAmbientLight;
uniform int uFogShape;
out float _flw_diffuse;
void flw_contextVertex() {
flw_vertexNormal = normalize(flw_vertexNormal);
if (uConstantAmbientLight == 1) {
_flw_diffuse = diffuseNether(flw_vertexNormal);
} else {
_flw_diffuse = diffuse(flw_vertexNormal);
}
flw_distance = fog_distance(flw_vertexPos.xyz, uCameraPos, uFogShape);
gl_Position = uViewProjection * flw_vertexPos;
// TODO: remove this
#ifdef DEBUG_NORMAL
flw_vertexColor = vec4(flw_vertexNormal, 1.0);
#endif
}

View file

@ -0,0 +1,34 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
uniform vec2 uFogRange;
uniform vec4 uFogColor;
uniform vec2 uTextureScale;
uniform sampler2D uBlockAtlas;
uniform sampler2D uCrumbling;
in float _flw_diffuse;
out vec4 fragColor;
void flw_contextFragment() {
vec4 texColor = texture(uBlockAtlas, flw_vertexTexCoord);
vec4 crumblingColor = texture(uCrumbling, flw_vertexTexCoord * uTextureScale);
crumblingColor.a *= texColor.a;
vec4 color = flw_vertexColor * vec4(crumblingColor.rgb * _flw_diffuse, crumblingColor.a);
#ifdef ALPHA_DISCARD
if (color.a < ALPHA_DISCARD) {
discard;
}
#endif
#ifdef COLOR_FOG
color = linear_fog(color, flw_distance, uFogRange.x, uFogRange.y, uFogColor);
#elif defined(FADE_FOG)
color = linear_fog_fade(color, flw_distance, uFogRange.x, uFogRange.y);
#endif
fragColor = color;
}

View file

@ -1,64 +0,0 @@
#use "flywheel:context/fog.glsl"
uniform float uTime;
uniform mat4 uViewProjection;
uniform vec3 uCameraPos;
uniform int uConstantAmbientLight;
uniform vec2 uTextureScale;
uniform sampler2D uBlockAtlas;
uniform sampler2D uLightMap;
uniform sampler2D uCrumbling;
uniform vec2 uWindowSize;
#ifdef VERTEX_SHADER
#use "flywheel:context/diffuse.glsl"
vec4 FLWVertex(inout Vertex v) {
fragDistance = fog_distance(v.pos, uCameraPos);
return uViewProjection * vec4(v.pos, 1.);
}
float FLWDiffuse(vec3 normal) {
if (uConstantAmbientLight == 1) {
return diffuseNether(normal);
} else {
return diffuse(normal);
}
}
#elif defined(FRAGMENT_SHADER)
out vec4 fragColor;
vec4 FLWBlockTexture(vec2 texCoords) {
vec4 cr = texture(uCrumbling, texCoords * uTextureScale);
float diffuseAlpha = texture(uBlockAtlas, texCoords).a;
cr.a = cr.a * diffuseAlpha;
return cr;
}
vec4 FLWLight(vec2 lightCoords) {
return vec4(1.);
}
void FLWFinalizeColor(vec4 color) {
#ifdef ALPHA_DISCARD
if (color.a < ALPHA_DISCARD) {
discard;
}
#endif
#ifdef COLOR_FOG
color = linear_fog(color);
#elif defined(FADE_FOG)
color = linear_fog_fade(color);
#endif
fragColor = color;
}
#endif

View file

@ -0,0 +1 @@
#use "flywheel:context/common.vert"

View file

@ -1,50 +0,0 @@
uniform vec4 uFogColor;
uniform vec2 uFogRange;
uniform int uFogShape;
#ifdef VERTEX_SHADER
out float fragDistance;
#elif defined(FRAGMENT_SHADER)
in float fragDistance;
#endif
float spherical_distance(vec3 relativePos) {
return length(relativePos);
}
float cylindrical_distance(vec3 relativePos) {
float distXZ = length(relativePos.xz);
float distY = abs(relativePos.y);
return max(distXZ, distY);
}
float fog_distance(vec3 relativePos) {
if (uFogShape == 0) {
return spherical_distance(relativePos);
} else {
return cylindrical_distance(relativePos);
}
}
float fog_distance(vec3 worldPos, vec3 cameraPos) {
return fog_distance(worldPos - cameraPos);
}
vec4 linear_fog(vec4 color) {
if (fragDistance <= uFogRange.x) {
return color;
}
float fogValue = fragDistance < uFogRange.y ? smoothstep(uFogRange.x, uFogRange.y, fragDistance) : 1.0;
return vec4(mix(color.rgb, uFogColor.rgb, fogValue * uFogColor.a), color.a);
}
vec4 linear_fog_fade(vec4 color) {
if (fragDistance <= uFogRange.x) {
return color;
} else if (fragDistance >= uFogRange.y) {
return vec4(0.0);
}
return color * smoothstep(uFogRange.y, uFogRange.x, fragDistance);
}

View file

@ -0,0 +1,39 @@
#use "flywheel:api/fragment.glsl"
#use "flywheel:util/fog.glsl"
// optimize discard usage
#ifdef ALPHA_DISCARD
#ifdef GL_ARB_conservative_depth
layout (depth_greater) out float gl_FragDepth;
#endif
#endif
uniform vec2 uFogRange;
uniform vec4 uFogColor;
uniform sampler2D uBlockAtlas;
uniform sampler2D uLightMap;
in float _flw_diffuse;
out vec4 fragColor;
void flw_contextFragment() {
vec4 texColor = texture(uBlockAtlas, flw_vertexTexCoord);
vec4 lightColor = texture(uLightMap, flw_vertexLight);
vec4 color = flw_vertexColor * vec4(texColor.rgb * lightColor.rgb * _flw_diffuse, texColor.a);
#ifdef ALPHA_DISCARD
if (color.a < ALPHA_DISCARD) {
discard;
}
#endif
#ifdef COLOR_FOG
color = linear_fog(color, flw_distance, uFogRange.x, uFogRange.y, uFogColor);
#elif defined(FADE_FOG)
color = linear_fog_fade(color, flw_distance, uFogRange.x, uFogRange.y);
#endif
fragColor = color;
}

View file

@ -1,68 +0,0 @@
#use "flywheel:context/fog.glsl"
uniform float uTime;
uniform mat4 uViewProjection;
uniform vec3 uCameraPos;
uniform int uConstantAmbientLight;
uniform vec2 uTextureScale;
uniform sampler2D uBlockAtlas;
uniform sampler2D uLightMap;
uniform vec2 uWindowSize;
#ifdef VERTEX_SHADER
#use "flywheel:context/diffuse.glsl"
vec4 FLWVertex(inout Vertex v) {
fragDistance = fog_distance(v.pos, uCameraPos);
return uViewProjection * vec4(v.pos, 1.);
}
float FLWDiffuse(vec3 normal) {
if (uConstantAmbientLight == 1) {
return diffuseNether(normal);
} else {
return diffuse(normal);
}
}
#elif defined(FRAGMENT_SHADER)
#use "flywheel:core/lightutil.glsl"
// optimize discard usage
#ifdef ALPHA_DISCARD
#ifdef GL_ARB_conservative_depth
layout (depth_greater) out float gl_FragDepth;
#endif
#endif
out vec4 fragColor;
vec4 FLWBlockTexture(vec2 texCoords) {
return texture(uBlockAtlas, texCoords);
}
vec4 FLWLight(vec2 lightCoords) {
return texture(uLightMap, shiftLight(lightCoords));
}
void FLWFinalizeColor(vec4 color) {
#ifdef ALPHA_DISCARD
if (color.a < ALPHA_DISCARD) {
discard;
}
#endif
#ifdef COLOR_FOG
color = linear_fog(color);
#elif defined(FADE_FOG)
color = linear_fog_fade(color);
#endif
fragColor = color;
}
#endif

View file

@ -0,0 +1 @@
#use "flywheel:context/common.vert"

View file

@ -0,0 +1,16 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/light.glsl"
struct Instance {
vec2 light;
vec4 color;
mat4 transform;
mat3 normalMat;
};
void flw_instanceVertex(Instance instance) {
flw_vertexPos = instance.transform * flw_vertexPos;
flw_vertexNormal = instance.normalMat * flw_vertexNormal;
flw_vertexColor = instance.color;
flw_vertexLight = shiftLight(instance.light);
}

View file

@ -0,0 +1,18 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/light.glsl"
#use "flywheel:util/quaternion.glsl"
struct Oriented {
vec2 light;
vec4 color;
vec3 pos;
vec3 pivot;
vec4 rotation;
};
void flw_instanceVertex(Oriented oriented) {
flw_vertexPos = vec4(rotateVertexByQuat(flw_vertexPos.xyz - oriented.pivot, oriented.rotation) + oriented.pivot + oriented.pos, 1.0);
flw_vertexNormal = rotateVertexByQuat(flw_vertexNormal, oriented.rotation);
flw_vertexColor = oriented.color;
flw_vertexLight = shiftLight(oriented.light);
}

View file

@ -0,0 +1,16 @@
#use "flywheel:api/vertex.glsl"
#use "flywheel:util/light.glsl"
layout(location = 0) in vec3 _flw_v_pos;
layout(location = 1) in vec4 _flw_v_color;
layout(location = 2) in vec2 _flw_v_texCoord;
layout(location = 3) in vec2 _flw_v_light;
layout(location = 4) in vec3 _flw_v_normal;
void flw_layoutVertex() {
flw_vertexPos = vec4(_flw_v_pos, 1.0);
flw_vertexColor = _flw_v_color;
flw_vertexTexCoord = _flw_v_texCoord;
flw_vertexLight = shiftLight(_flw_v_light);
flw_vertexNormal = _flw_v_normal;
}

View file

@ -0,0 +1,13 @@
#use "flywheel:api/vertex.glsl"
layout(location = 0) in vec3 _flw_v_pos;
layout(location = 1) in vec2 _flw_v_texCoord;
layout(location = 2) in vec3 _flw_v_normal;
void flw_layoutVertex() {
flw_vertexPos = vec4(_flw_v_pos, 1.0);
flw_vertexColor = vec4(1.0);
flw_vertexTexCoord = _flw_v_texCoord;
flw_vertexLight = vec2(0.0);
flw_vertexNormal = _flw_v_normal;
}

View file

@ -1,14 +0,0 @@
struct Instance {
vec2 light;
vec4 color;
mat4 transform;
mat3 normalMat;
};
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;
}

View file

@ -1,16 +0,0 @@
#use "flywheel:core/quaternion.glsl"
struct Oriented {
vec2 light;
vec4 color;
vec3 pos;
vec3 pivot;
vec4 rotation;
};
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;
}

View file

@ -1,4 +0,0 @@
void vertex(inout Vertex v) {
}

View file

@ -0,0 +1,40 @@
float spherical_distance(vec3 relativePos) {
return length(relativePos);
}
float cylindrical_distance(vec3 relativePos) {
float distXZ = length(relativePos.xz);
float distY = abs(relativePos.y);
return max(distXZ, distY);
}
float fog_distance(vec3 relativePos, int fogShape) {
if (fogShape == 0) {
return spherical_distance(relativePos);
} else {
return cylindrical_distance(relativePos);
}
}
float fog_distance(vec3 worldPos, vec3 cameraPos, int fogShape) {
return fog_distance(worldPos - cameraPos, fogShape);
}
vec4 linear_fog(vec4 color, float distance, float fogStart, float fogEnd, vec4 fogColor) {
if (distance <= fogStart) {
return color;
}
float fogValue = distance < fogEnd ? smoothstep(fogStart, fogEnd, distance) : 1.0;
return vec4(mix(color.rgb, fogColor.rgb, fogValue * fogColor.a), color.a);
}
vec4 linear_fog_fade(vec4 color, float distance, float fogStart, float fogEnd) {
if (distance <= fogStart) {
return color;
} else if (distance >= fogEnd) {
return vec4(0.0);
}
return color * smoothstep(fogEnd, fogStart, distance);
}