Json program specs, new extension/gamestate system.

- Program specs are now loaded from json instead of being defined in code and registered manually.
 - Within the json spec, a program can define a list of states.
 - A state consists of:
   - A "when" clause.
   - A list of strings to be #defined.
   - A list of extensions to apply at program link time.
 - Each frame, the first state whose "when" clause returns true will be used.
 - A when clause consists of:
  - A state provider defined by a resource location.
  - A value to match.
 - When the value returned by the provider matches the value defined in the when clause, the when clause is considered to be 'true'.
 - There is syntactic sugar for when a provider returns a boolean value.
 - This system is in its infancy, and there is plenty of room for improvement.
This commit is contained in:
JozsefA 2021-05-22 17:45:01 -07:00
parent d58897d59a
commit 9352ef9ede
64 changed files with 1053 additions and 393 deletions

View file

@ -23,8 +23,8 @@ import org.lwjgl.opengl.GLCapabilities;
import com.jozufozu.flywheel.backend.core.CrumblingRenderer;
import com.jozufozu.flywheel.backend.core.WorldContext;
import com.jozufozu.flywheel.backend.core.WorldTileRenderer;
import com.jozufozu.flywheel.backend.core.shader.ProgramSpec;
import com.jozufozu.flywheel.backend.core.shader.WorldProgram;
import com.jozufozu.flywheel.backend.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
import com.jozufozu.flywheel.backend.instancing.IFlywheelWorld;
@ -137,6 +137,10 @@ public class Backend {
return spec;
}
public static ProgramSpec getSpec(ResourceLocation name) {
return programSpecRegistry.get(name);
}
public static boolean isFlywheelWorld(World world) {
return world == Minecraft.getInstance().world || (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel());
}

View file

@ -7,4 +7,13 @@ public class ResourceUtil {
public static ResourceLocation subPath(ResourceLocation root, String subPath) {
return new ResourceLocation(root.getNamespace(), root.getPath() + subPath);
}
public static ResourceLocation removePrefixUnchecked(ResourceLocation full, String root) {
return new ResourceLocation(full.getNamespace(), full.getPath().substring(root.length()));
}
public static ResourceLocation trim(ResourceLocation loc, String prefix, String suffix) {
String path = loc.getPath();
return new ResourceLocation(loc.getNamespace(), path.substring(prefix.length(), path.length() - suffix.length()));
}
}

View file

@ -4,25 +4,23 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.core.shader.ProgramSpec;
import com.jozufozu.flywheel.backend.core.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderSpecLoader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.loading.Program;
import com.jozufozu.flywheel.backend.loading.Shader;
import com.jozufozu.flywheel.backend.loading.ShaderTransformer;
import net.minecraft.util.ResourceLocation;
public abstract class ShaderContext<P extends GlProgram> {
public final Map<ProgramSpec, IMultiProgram<P>> programs = new HashMap<>();
protected final Map<ResourceLocation, IMultiProgram<P>> programs = new HashMap<>();
protected final ShaderSpecLoader<P> specLoader;
protected ShaderTransformer transformer = new ShaderTransformer();
public ShaderContext(ShaderSpecLoader<P> specLoader) {
this.specLoader = specLoader;
}
public ShaderContext() { }
// TODO: Untangle the loading functions
@ -31,14 +29,16 @@ public abstract class ShaderContext<P extends GlProgram> {
*/
public abstract void load(ShaderLoader loader);
protected abstract IMultiProgram<P> loadSpecInternal(ShaderLoader loader, ProgramSpec spec);
public void loadProgramFromSpec(ShaderLoader loader, ProgramSpec programSpec) {
try {
programs.put(programSpec, specLoader.create(loader, this, programSpec));
programs.put(programSpec.name, loadSpecInternal(loader, programSpec));
Backend.log.debug("Loaded program {}", programSpec.name);
} catch (Exception e) {
Backend.log.error("program '{}': {}", programSpec.name, e.getMessage());
Backend.log.error("Program '{}': {}", programSpec.name, e);
loader.notifyError();
}
}
@ -66,7 +66,7 @@ public abstract class ShaderContext<P extends GlProgram> {
}
public P getProgram(ProgramSpec spec) {
public P getProgram(ResourceLocation spec) {
return programs.get(spec).get();
}

View file

@ -27,6 +27,11 @@ import javax.annotation.Nonnull;
import org.lwjgl.system.MemoryUtil;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.jozufozu.flywheel.backend.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.backend.core.shader.spec.SpecMetaRegistry;
import com.jozufozu.flywheel.backend.gl.GlObject;
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
@ -34,6 +39,9 @@ import com.jozufozu.flywheel.backend.loading.Program;
import com.jozufozu.flywheel.backend.loading.Shader;
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.datafixers.util.Pair;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import net.minecraft.resources.IResource;
import net.minecraft.resources.IResourceManager;
@ -43,6 +51,7 @@ import net.minecraftforge.resource.VanillaResourceType;
public class ShaderLoader {
public static final String SHADER_DIR = "flywheel/shaders/";
public static final String PROGRAM_DIR = "flywheel/programs/";
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
// #flwinclude <"valid_namespace:valid/path_to_file.glsl">
@ -52,6 +61,7 @@ public class ShaderLoader {
private final Map<ResourceLocation, String> shaderSource = new HashMap<>();
private boolean shouldCrash;
private final Gson gson = new GsonBuilder().create();
void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
if (predicate.test(VanillaResourceType.SHADERS)) {
@ -63,6 +73,10 @@ public class ShaderLoader {
shouldCrash = false;
SpecMetaRegistry.init();
loadProgramSpecs(manager);
loadShaderSources(manager);
for (ShaderContext<?> context : Backend.contexts) {
@ -70,7 +84,7 @@ public class ShaderLoader {
}
if (shouldCrash) {
throw new ShaderLoadingException("could not load all shaders, see log for details");
throw new ShaderLoadingException("Could not load all shaders, see log for details");
}
Backend.log.info("Loaded all shader programs.");
@ -81,6 +95,30 @@ public class ShaderLoader {
}
}
private void loadProgramSpecs(IResourceManager manager) {
Collection<ResourceLocation> programSpecs = manager.getAllResourceLocations(PROGRAM_DIR, s -> s.endsWith(".json"));
for (ResourceLocation location : programSpecs) {
try {
IResource file = manager.getResource(location);
String s = 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);
Backend.register(spec);
} catch (Exception e) {
Backend.log.error(e);
}
}
}
public void notifyError() {
shouldCrash = true;
}
@ -110,8 +148,7 @@ public class ShaderLoader {
String file = readToString(resource.getInputStream());
ResourceLocation name = new ResourceLocation(location.getNamespace(),
location.getPath().substring(SHADER_DIR.length()));
ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR);
shaderSource.put(name, file);
} catch (IOException e) {

View file

@ -4,16 +4,16 @@ import static org.lwjgl.opengl.GL20.glUniform2f;
import java.util.List;
import com.jozufozu.flywheel.backend.core.shader.ProgramExtender;
import com.jozufozu.flywheel.backend.core.shader.WorldProgram;
import com.jozufozu.flywheel.backend.core.shader.extension.IProgramExtension;
import com.jozufozu.flywheel.backend.loading.Program;
public class CrumblingProgram extends WorldProgram {
protected final int uTextureScale;
protected int uCrumbling;
public CrumblingProgram(Program program, List<ProgramExtender> fogFactory) {
super(program, fogFactory);
public CrumblingProgram(Program program, List<IProgramExtension> extensions) {
super(program, extensions);
uTextureScale = getUniformLocation("uTextureScale");
}

View file

@ -1,59 +0,0 @@
package com.jozufozu.flywheel.backend.core;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.ShaderLoader;
import com.jozufozu.flywheel.backend.core.shader.ExtensibleGlProgram;
import com.jozufozu.flywheel.backend.core.shader.GlFog;
import com.jozufozu.flywheel.backend.core.shader.GlFogMode;
import com.jozufozu.flywheel.backend.core.shader.ProgramSpec;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderSpecLoader;
import com.jozufozu.flywheel.backend.loading.Program;
public class FogMultiProgram<P extends GlProgram> implements IMultiProgram<P> {
private final Map<GlFogMode, P> programs;
public FogMultiProgram(Map<GlFogMode, P> programs) {
this.programs = programs;
}
@Override
public P get() {
return programs.get(GlFog.getFogMode());
}
@Override
public void delete() {
programs.values().forEach(GlProgram::delete);
}
public static class SpecLoader<P extends GlProgram> implements ShaderSpecLoader<P> {
private final ExtensibleGlProgram.Factory<P> factory;
public SpecLoader(ExtensibleGlProgram.Factory<P> factory) {
this.factory = factory;
}
@Override
public IMultiProgram<P> create(ShaderLoader loader, ShaderContext<P> ctx, ProgramSpec spec) {
Map<GlFogMode, P> programs = new EnumMap<>(GlFogMode.class);
for (GlFogMode fogMode : GlFogMode.values()) {
Program builder = ctx.loadProgram(loader, spec, fogMode.getDefines());
programs.put(fogMode, factory.create(builder, Collections.singletonList(fogMode)));
}
return new FogMultiProgram<>(programs);
}
}
}

View file

@ -12,10 +12,11 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.ResourceUtil;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.ShaderLoader;
import com.jozufozu.flywheel.backend.core.shader.ProgramSpec;
import com.jozufozu.flywheel.backend.core.shader.ExtensibleGlProgram;
import com.jozufozu.flywheel.backend.core.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.core.shader.StateSensitiveMultiProgram;
import com.jozufozu.flywheel.backend.core.shader.WorldProgram;
import com.jozufozu.flywheel.backend.gl.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderSpecLoader;
import com.jozufozu.flywheel.backend.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
import com.jozufozu.flywheel.backend.instancing.MaterialSpec;
import com.jozufozu.flywheel.backend.loading.InstancedArraysTemplate;
@ -32,45 +33,59 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
private static final String declaration = "#flwbuiltins";
private static final Pattern builtinPattern = Pattern.compile(declaration);
public static final WorldContext<WorldProgram> INSTANCE = new WorldContext<>(new ResourceLocation(Flywheel.ID, "context/world"), new FogMultiProgram.SpecLoader<>(WorldProgram::new));
public static final WorldContext<CrumblingProgram> CRUMBLING = new WorldContext<>(new ResourceLocation(Flywheel.ID, "context/crumbling"), new FogMultiProgram.SpecLoader<>(CrumblingProgram::new));
public static final WorldContext<WorldProgram> INSTANCE = new WorldContext<>(new ResourceLocation(Flywheel.ID, "context/world"), WorldProgram::new);
public static final WorldContext<CrumblingProgram> CRUMBLING = new WorldContext<>(new ResourceLocation(Flywheel.ID, "context/crumbling"), CrumblingProgram::new);
protected ProgramTemplate template;
protected final ResourceLocation name;
protected final Supplier<Stream<ProgramSpec>> specStream;
protected final TemplateFactory templateFactory;
private final ExtensibleGlProgram.Factory<P> factory;
protected Supplier<Stream<ResourceLocation>> specStream;
protected TemplateFactory templateFactory;
private final Map<ShaderType, ResourceLocation> builtins = new EnumMap<>(ShaderType.class);
private final Map<ShaderType, String> builtinSources = new EnumMap<>(ShaderType.class);
public WorldContext(ResourceLocation root, ShaderSpecLoader<P> loader) {
this(root, loader, () -> Backend.allMaterials()
.stream()
.map(MaterialSpec::getProgramSpec), InstancedArraysTemplate::new);
}
public WorldContext(ResourceLocation root, ShaderSpecLoader<P> loader, Supplier<Stream<ProgramSpec>> specStream, TemplateFactory templateFactory) {
super(loader);
public WorldContext(ResourceLocation root, ExtensibleGlProgram.Factory<P> factory) {
this.factory = factory;
this.name = root;
this.specStream = specStream;
this.templateFactory = templateFactory;
builtins.put(ShaderType.FRAGMENT, ResourceUtil.subPath(root, "/builtin.frag"));
builtins.put(ShaderType.VERTEX, ResourceUtil.subPath(root, "/builtin.vert"));
specStream = () -> Backend.allMaterials()
.stream()
.map(MaterialSpec::getProgramSpec);
templateFactory = InstancedArraysTemplate::new;
}
public WorldContext<P> setSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
this.specStream = specStream;
return this;
}
public WorldContext<P> setTemplateFactory(TemplateFactory templateFactory) {
this.templateFactory = templateFactory;
return this;
}
@Override
protected IMultiProgram<P> loadSpecInternal(ShaderLoader loader, ProgramSpec spec) {
return new StateSensitiveMultiProgram<>(loader, factory, this, spec);
}
protected ProgramTemplate template;
@Override
public void load(ShaderLoader loader) {
programs.values().forEach(IMultiProgram::delete);
programs.clear();
Backend.log.info("loading context '{}'", name);
Backend.log.info("Loading context '{}'", name);
try {
builtins.forEach((type, resourceLocation) -> builtinSources.put(type, loader.getShaderSource(resourceLocation)));
} catch (ShaderLoadingException e) {
loader.notifyError();
Backend.log.error(String.format("could not find builtin: %s", e.getMessage()));
Backend.log.error(String.format("Could not find builtin: %s", e.getMessage()));
return;
}
@ -83,7 +98,9 @@ public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
.pushStage(template)
.pushStage(loader::processIncludes);
specStream.get().forEach(spec -> loadProgramFromSpec(loader, spec));
specStream.get()
.map(Backend::getSpec)
.forEach(spec -> loadProgramFromSpec(loader, spec));
}
@Override

View file

@ -2,8 +2,8 @@ package com.jozufozu.flywheel.backend.core;
import java.util.ArrayList;
import com.jozufozu.flywheel.backend.core.shader.ShaderCallback;
import com.jozufozu.flywheel.backend.core.shader.WorldProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderCallback;
import com.jozufozu.flywheel.backend.instancing.InstancedTileRenderer;
import net.minecraft.client.renderer.ActiveRenderInfo;

View file

@ -1,18 +1,20 @@
package com.jozufozu.flywheel.backend.core.shader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.jozufozu.flywheel.backend.core.shader.extension.IExtensionInstance;
import com.jozufozu.flywheel.backend.core.shader.extension.IProgramExtension;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.loading.Program;
/**
* A shader program that be arbitrarily "extended". This class can take in any number of program extensions, and
* will initialize them and then call their {@link IProgramExtension#bind() bind} function every subsequent time this
* will initialize them and then call their {@link IExtensionInstance#bind() bind} function every subsequent time this
* program is bound. An "extension" is something that interacts with the shader program in a way that is invisible to
* the caller using the program. This is used by some programs to implement the different fog modes. Other uses might
* include binding extra textures to allow for blocks to have normal maps, for example. As the extensions are
@ -21,15 +23,18 @@ import com.jozufozu.flywheel.backend.loading.Program;
*/
public class ExtensibleGlProgram extends GlProgram {
protected final List<IProgramExtension> extensions;
protected final List<IExtensionInstance> extensions;
public ExtensibleGlProgram(Program program, @Nullable List<ProgramExtender> extensions) {
public ExtensibleGlProgram(Program program, @Nullable List<IProgramExtension> extensions) {
super(program);
if (extensions != null) {
this.extensions = extensions.stream()
.map(e -> e.create(this))
.collect(Collectors.toList());
List<IExtensionInstance> list = new ArrayList<>();
for (IProgramExtension e : extensions) {
IExtensionInstance extension = e.create(this);
list.add(extension);
}
this.extensions = list;
} else {
this.extensions = Collections.emptyList();
}
@ -39,7 +44,7 @@ public class ExtensibleGlProgram extends GlProgram {
public void bind() {
super.bind();
extensions.forEach(IProgramExtension::bind);
extensions.forEach(IExtensionInstance::bind);
}
@Override
@ -57,7 +62,7 @@ public class ExtensibleGlProgram extends GlProgram {
public interface Factory<P extends GlProgram> {
@Nonnull
P create(Program program, @Nullable List<ProgramExtender> extensions);
P create(Program program, @Nullable List<IProgramExtension> extensions);
default P create(Program program) {
return create(program, null);

View file

@ -3,32 +3,14 @@ package com.jozufozu.flywheel.backend.core.shader;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.core.shader.extension.IExtensionInstance;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import net.minecraft.util.ResourceLocation;
public abstract class FogMode {
public static class None implements IProgramExtension {
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "fog_none");
public None(GlProgram program) {
}
@Override
public void bind() {
}
@Override
public ResourceLocation name() {
return NAME;
}
}
public static class Linear implements IProgramExtension {
public static class Linear implements IExtensionInstance {
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "fog_linear");
@ -52,7 +34,7 @@ public abstract class FogMode {
}
}
public static class Exp2 implements IProgramExtension {
public static class Exp2 implements IExtensionInstance {
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "fog_exp2");
@ -75,7 +57,4 @@ public abstract class FogMode {
return NAME;
}
}
public interface Factory extends IProgramExtension {
}
}

View file

@ -27,9 +27,9 @@ public class GlFog {
return GlStateManager.FOG.field_179045_d;
}
public static GlFogMode getFogMode() {
public static WorldFog getFogMode() {
if (!fogEnabled()) {
return GlFogMode.NONE;
return WorldFog.NONE;
}
int mode = getFogModeGlEnum();
@ -37,9 +37,9 @@ public class GlFog {
switch (mode) {
case GL11.GL_EXP2:
case GL11.GL_EXP:
return GlFogMode.EXP2;
return WorldFog.EXP2;
case GL11.GL_LINEAR:
return GlFogMode.LINEAR;
return WorldFog.LINEAR;
default:
throw new UnsupportedOperationException("Unknown fog mode: " + mode);
}

View file

@ -1,38 +0,0 @@
package com.jozufozu.flywheel.backend.core.shader;
import java.util.Collections;
import java.util.List;
import com.google.common.collect.Lists;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
public enum GlFogMode implements ProgramExtender {
NONE(FogMode.None::new),
LINEAR(FogMode.Linear::new, "USE_FOG_LINEAR"),
EXP2(FogMode.Exp2::new, "USE_FOG_EXP2"),
;
public static final String USE_FOG = "USE_FOG";
private final ProgramExtender fogFactory;
private final List<String> defines;
GlFogMode(ProgramExtender fogFactory) {
this.fogFactory = fogFactory;
this.defines = Collections.emptyList();
}
GlFogMode(ProgramExtender fogFactory, String name) {
this.fogFactory = fogFactory;
this.defines = Lists.newArrayList(USE_FOG, name);
}
public List<String> getDefines() {
return defines;
}
@Override
public IProgramExtension create(GlProgram program) {
return fogFactory.create(program);
}
}

View file

@ -1,4 +1,6 @@
package com.jozufozu.flywheel.backend.gl.shader;
package com.jozufozu.flywheel.backend.core.shader;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
/**
* Encapsulates any number of shader programs for use in similar contexts.

View file

@ -1,17 +0,0 @@
package com.jozufozu.flywheel.backend.core.shader;
import net.minecraft.util.ResourceLocation;
/**
* A program extension to be passed to
*/
public interface IProgramExtension {
/**
* Bind the extra program state. It is recommended to grab the state information from global variables,
* or local variables passed through a {@link ProgramExtender} closure.
*/
void bind();
ResourceLocation name();
}

View file

@ -1,18 +0,0 @@
package com.jozufozu.flywheel.backend.core.shader;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
/**
* A factory interface for creating {@link IProgramExtension}s. These are what end up being passed in
* during shader program construction.
*/
public interface ProgramExtender {
/**
* Construct the extension, binding any necessary information using the provided {@link GlProgram}.
*
* @param program The program being extended.
* @return An extension object, possibly initialized using the program.
*/
IProgramExtension create(GlProgram program);
}

View file

@ -1,22 +0,0 @@
package com.jozufozu.flywheel.backend.core.shader;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.util.ResourceLocation;
public class ProgramSpec {
public final ResourceLocation name;
public final ResourceLocation vert;
public final ResourceLocation frag;
public final List<ResourceLocation> debugModes = new ArrayList<>();
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag) {
this.name = name;
this.vert = vert;
this.frag = frag;
}
}

View file

@ -1,4 +1,6 @@
package com.jozufozu.flywheel.backend.gl.shader;
package com.jozufozu.flywheel.backend.core.shader;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
/**
* A Callback for when a shader is called. Used to define shader uniforms.

View file

@ -1,44 +0,0 @@
package com.jozufozu.flywheel.backend.core.shader;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.ShaderLoader;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.gl.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderSpecLoader;
import com.jozufozu.flywheel.backend.loading.Program;
public class SingleProgram<P extends GlProgram> implements IMultiProgram<P> {
final P program;
public SingleProgram(P program) {
this.program = program;
}
@Override
public P get() {
return program;
}
@Override
public void delete() {
program.delete();
}
public static class SpecLoader<P extends GlProgram> implements ShaderSpecLoader<P> {
final ProgramFactory<P> factory;
public SpecLoader(ProgramFactory<P> factory) {
this.factory = factory;
}
@Override
public IMultiProgram<P> create(ShaderLoader loader, ShaderContext<P> ctx, ProgramSpec spec) {
return new SingleProgram<>(factory.create(ctx.loadProgram(loader, spec, null)));
}
}
@FunctionalInterface
public interface ProgramFactory<P extends GlProgram> {
P create(Program program);
}
}

View file

@ -0,0 +1,54 @@
package com.jozufozu.flywheel.backend.core.shader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.ShaderLoader;
import com.jozufozu.flywheel.backend.core.shader.spec.IContextCondition;
import com.jozufozu.flywheel.backend.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.backend.core.shader.spec.ProgramState;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.jozufozu.flywheel.backend.loading.Program;
import com.jozufozu.flywheel.util.Pair;
public class StateSensitiveMultiProgram<P extends GlProgram> implements IMultiProgram<P> {
List<Pair<IContextCondition, P>> variants;
P fallback;
public StateSensitiveMultiProgram(ShaderLoader loader, ExtensibleGlProgram.Factory<P> factory, ShaderContext<P> context, ProgramSpec p) {
variants = new ArrayList<>(p.states.size());
for (ProgramState state : p.states) {
Program variant = context.loadProgram(loader, p, state.getDefines());
Pair<IContextCondition, P> pair = Pair.of(state.getContext(), factory.create(variant, state.getExtensions()));
variants.add(pair);
}
fallback = factory.create(context.loadProgram(loader, p, Collections.emptyList()));
}
@Override
public P get() {
for (Pair<IContextCondition, P> variant : variants) {
if (variant.getFirst().get())
return variant.getSecond();
}
return fallback;
}
@Override
public void delete() {
for (Pair<IContextCondition, P> variant : variants) {
variant.getSecond().delete();
}
fallback.delete();
}
}

View file

@ -0,0 +1,47 @@
package com.jozufozu.flywheel.backend.core.shader;
import java.util.function.Function;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.core.shader.extension.IExtensionInstance;
import com.jozufozu.flywheel.backend.core.shader.extension.IProgramExtension;
import com.jozufozu.flywheel.backend.core.shader.extension.UnitExtensionInstance;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import net.minecraft.util.ResourceLocation;
public enum WorldFog implements IProgramExtension {
NONE("none", UnitExtensionInstance::new),
LINEAR("linear", FogMode.Linear::new),
EXP2("exp2", FogMode.Exp2::new),
;
private final ResourceLocation id;
private final String name;
private final Function<GlProgram, IExtensionInstance> fogFactory;
WorldFog(String name, Function<GlProgram, IExtensionInstance> fogFactory) {
this.id = new ResourceLocation(Flywheel.ID, "fog_" + name);
this.name = name;
this.fogFactory = fogFactory;
}
public String getName() {
return name;
}
@Override
public IExtensionInstance create(GlProgram program) {
return fogFactory.apply(program);
}
@Override
public ResourceLocation getID() {
return id;
}
@Override
public String toString() {
return name;
}
}

View file

@ -5,6 +5,7 @@ import static org.lwjgl.opengl.GL20.glUniform3f;
import java.util.List;
import com.jozufozu.flywheel.backend.core.shader.extension.IProgramExtension;
import com.jozufozu.flywheel.backend.loading.Program;
import com.simibubi.create.foundation.utility.AnimationTickHolder;
@ -18,8 +19,8 @@ public class WorldProgram extends ExtensibleGlProgram {
protected int uBlockAtlas;
protected int uLightMap;
public WorldProgram(Program program, List<ProgramExtender> fogFactory) {
super(program, fogFactory);
public WorldProgram(Program program, List<IProgramExtension> extensions) {
super(program, extensions);
uTime = getUniformLocation("uTime");
uViewProjection = getUniformLocation("uViewProjection");
uCameraPos = getUniformLocation("uCameraPos");

View file

@ -0,0 +1,14 @@
package com.jozufozu.flywheel.backend.core.shader.extension;
import net.minecraft.util.ResourceLocation;
public interface IExtensionInstance {
/**
* Bind the extra program state. It is recommended to grab the state information from global variables,
* or local variables passed through a {@link IProgramExtension}.
*/
void bind();
ResourceLocation name();
}

View file

@ -0,0 +1,26 @@
package com.jozufozu.flywheel.backend.core.shader.extension;
import com.jozufozu.flywheel.backend.core.shader.spec.SpecMetaRegistry;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import com.mojang.serialization.Codec;
import net.minecraft.util.ResourceLocation;
/**
* A factory interface for creating {@link IExtensionInstance}s. These are what end up being passed in
* during shader program construction.
*/
public interface IProgramExtension {
Codec<IProgramExtension> CODEC = ResourceLocation.CODEC.xmap(SpecMetaRegistry::getExtension, IProgramExtension::getID);
/**
* Construct the extension, binding any necessary information using the provided {@link GlProgram}.
*
* @param program The program being extended.
* @return An extension object, possibly initialized using the program.
*/
IExtensionInstance create(GlProgram program);
ResourceLocation getID();
}

View file

@ -0,0 +1,23 @@
package com.jozufozu.flywheel.backend.core.shader.extension;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
import net.minecraft.util.ResourceLocation;
public class UnitExtensionInstance implements IExtensionInstance {
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "unit");
public UnitExtensionInstance(GlProgram program) { }
@Override
public void bind() {
}
@Override
public ResourceLocation name() {
return NAME;
}
}

View file

@ -0,0 +1,22 @@
package com.jozufozu.flywheel.backend.core.shader.gamestate;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.core.shader.GlFog;
import net.minecraft.util.ResourceLocation;
public class FogStateProvider implements IGameStateProvider {
public static final FogStateProvider INSTANCE = new FogStateProvider();
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "fog_mode");
@Override
public ResourceLocation getID() {
return NAME;
}
@Override
public Object getValue() {
return GlFog.getFogMode();
}
}

View file

@ -0,0 +1,15 @@
package com.jozufozu.flywheel.backend.core.shader.gamestate;
import com.jozufozu.flywheel.backend.core.shader.spec.SpecMetaRegistry;
import com.mojang.serialization.Codec;
import net.minecraft.util.ResourceLocation;
public interface IGameStateProvider {
Codec<IGameStateProvider> CODEC = ResourceLocation.CODEC.xmap(SpecMetaRegistry::getStateProvider, IGameStateProvider::getID);
ResourceLocation getID();
Object getValue();
}

View file

@ -0,0 +1,26 @@
package com.jozufozu.flywheel.backend.core.shader.gamestate;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.core.shader.spec.IBooleanStateProvider;
import net.minecraft.util.ResourceLocation;
public class NormalDebugStateProvider implements IBooleanStateProvider {
public static final NormalDebugStateProvider INSTANCE = new NormalDebugStateProvider();
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "normal_debug");
protected NormalDebugStateProvider() {
}
@Override
public boolean isTrue() {
return false;
}
@Override
public ResourceLocation getID() {
return NAME;
}
}

View file

@ -0,0 +1,27 @@
package com.jozufozu.flywheel.backend.core.shader.gamestate;
import com.jozufozu.flywheel.backend.core.shader.spec.IBooleanStateProvider;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.KineticDebugger;
import net.minecraft.util.ResourceLocation;
public class RainbowDebugStateProvider implements IBooleanStateProvider {
public static final RainbowDebugStateProvider INSTANCE = new RainbowDebugStateProvider();
public static final ResourceLocation NAME = new ResourceLocation(Create.ID, "rainbow_debug");
protected RainbowDebugStateProvider() {
}
@Override
public boolean isTrue() {
return KineticDebugger.isActive();
}
@Override
public ResourceLocation getID() {
return NAME;
}
}

View file

@ -0,0 +1,37 @@
package com.jozufozu.flywheel.backend.core.shader.spec;
import com.jozufozu.flywheel.backend.core.shader.gamestate.IGameStateProvider;
import com.mojang.serialization.Codec;
import net.minecraft.util.ResourceLocation;
public class BooleanContextCondition implements IContextCondition {
public static final Codec<BooleanContextCondition> BOOLEAN_SUGAR = IGameStateProvider.CODEC.xmap(gameContext -> {
if (gameContext instanceof IBooleanStateProvider) {
return new BooleanContextCondition(((IBooleanStateProvider) gameContext));
}
return null;
}, IContextCondition::contextProvider);
protected final IBooleanStateProvider context;
public BooleanContextCondition(IBooleanStateProvider context) {
this.context = context;
}
@Override
public ResourceLocation getID() {
return context.getID();
}
@Override
public IGameStateProvider contextProvider() {
return context;
}
@Override
public boolean get() {
return context.isTrue();
}
}

View file

@ -0,0 +1,13 @@
package com.jozufozu.flywheel.backend.core.shader.spec;
import com.jozufozu.flywheel.backend.core.shader.gamestate.IGameStateProvider;
public interface IBooleanStateProvider extends IGameStateProvider {
boolean isTrue();
@Override
default Boolean getValue() {
return isTrue();
}
}

View file

@ -0,0 +1,14 @@
package com.jozufozu.flywheel.backend.core.shader.spec;
import com.jozufozu.flywheel.backend.core.shader.gamestate.IGameStateProvider;
import net.minecraft.util.ResourceLocation;
public interface IContextCondition {
ResourceLocation getID();
IGameStateProvider contextProvider();
boolean get();
}

View file

@ -0,0 +1,61 @@
package com.jozufozu.flywheel.backend.core.shader.spec;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.util.ResourceLocation;
public class ProgramSpec {
// TODO: Block model style inheritance?
public static final Codec<ProgramSpec> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
ResourceLocation.CODEC.fieldOf("vert")
.forGetter(ProgramSpec::getVert),
ResourceLocation.CODEC.fieldOf("frag")
.forGetter(ProgramSpec::getFrag),
ProgramState.CODEC.listOf()
.optionalFieldOf("states", Collections.emptyList())
.forGetter(ProgramSpec::getStates)
).apply(instance, ProgramSpec::new));
public ResourceLocation name;
public final ResourceLocation vert;
public final ResourceLocation frag;
public final List<ProgramState> states;
public ProgramSpec(ResourceLocation vert, ResourceLocation frag, List<ProgramState> states) {
this.vert = vert;
this.frag = frag;
this.states = states;
}
public ProgramSpec(ResourceLocation name, ResourceLocation vert, ResourceLocation frag) {
this.name = name;
this.vert = vert;
this.frag = frag;
this.states = new ArrayList<>();
}
public void setName(ResourceLocation name) {
this.name = name;
}
public ResourceLocation getVert() {
return vert;
}
public ResourceLocation getFrag() {
return frag;
}
public List<ProgramState> getStates() {
return states;
}
}

View file

@ -0,0 +1,67 @@
package com.jozufozu.flywheel.backend.core.shader.spec;
import java.util.Collections;
import java.util.List;
import com.jozufozu.flywheel.backend.core.shader.extension.IProgramExtension;
import com.jozufozu.flywheel.util.CodecUtil;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.codecs.RecordCodecBuilder;
public class ProgramState {
// TODO: Use Codec.dispatch
private static final Codec<IContextCondition> WHEN = Codec.either(
BooleanContextCondition.BOOLEAN_SUGAR,
SpecificValueCondition.CODEC
).flatXmap(
either -> either.map(DataResult::success, DataResult::success),
any -> {
if (any instanceof BooleanContextCondition) {
return DataResult.success(Either.left((BooleanContextCondition) any));
}
if (any instanceof SpecificValueCondition) {
return DataResult.success(Either.right((SpecificValueCondition) any));
}
return DataResult.error("unknown context condition");
}
);
public static final Codec<ProgramState> CODEC = RecordCodecBuilder.create(state ->
state.group(
WHEN.fieldOf("when")
.forGetter(ProgramState::getContext),
CodecUtil.oneOrMore(Codec.STRING)
.optionalFieldOf("define", Collections.emptyList())
.forGetter(ProgramState::getDefines),
CodecUtil.oneOrMore(IProgramExtension.CODEC)
.optionalFieldOf("extend", Collections.emptyList())
.forGetter(ProgramState::getExtensions)
).apply(state, ProgramState::new));
private final IContextCondition context;
private final List<String> defines;
private final List<IProgramExtension> extensions;
public ProgramState(IContextCondition context, List<String> defines, List<IProgramExtension> extensions) {
this.context = context;
this.defines = defines;
this.extensions = extensions;
}
public IContextCondition getContext() {
return context;
}
public List<String> getDefines() {
return defines;
}
public List<IProgramExtension> getExtensions() {
return extensions;
}
}

View file

@ -0,0 +1,69 @@
package com.jozufozu.flywheel.backend.core.shader.spec;
import java.util.HashMap;
import java.util.Map;
import com.jozufozu.flywheel.backend.core.shader.WorldFog;
import com.jozufozu.flywheel.backend.core.shader.extension.IProgramExtension;
import com.jozufozu.flywheel.backend.core.shader.gamestate.FogStateProvider;
import com.jozufozu.flywheel.backend.core.shader.gamestate.IGameStateProvider;
import com.jozufozu.flywheel.backend.core.shader.gamestate.NormalDebugStateProvider;
import com.jozufozu.flywheel.backend.core.shader.gamestate.RainbowDebugStateProvider;
import net.minecraft.util.ResourceLocation;
public class SpecMetaRegistry {
private static final Map<ResourceLocation, IProgramExtension> registeredExtensions = new HashMap<>();
private static final Map<ResourceLocation, IGameStateProvider> registeredStateProviders = new HashMap<>();
// TODO: proper registration, don't call this from ShaderLoader
private static boolean initialized = false;
public static void init() {
if (initialized) return;
initialized = true;
register(FogStateProvider.INSTANCE);
register(RainbowDebugStateProvider.INSTANCE);
register(NormalDebugStateProvider.INSTANCE);
register(WorldFog.LINEAR);
register(WorldFog.EXP2);
}
public static IGameStateProvider getStateProvider(ResourceLocation location) {
IGameStateProvider out = registeredStateProviders.get(location);
if (out == null) {
throw new IllegalArgumentException("State provider '" + location + "' does not exist.");
}
return out;
}
public static IProgramExtension getExtension(ResourceLocation location) {
IProgramExtension out = registeredExtensions.get(location);
if (out == null) {
throw new IllegalArgumentException("Extension '" + location + "' does not exist.");
}
return out;
}
public static void register(IGameStateProvider context) {
if (registeredStateProviders.containsKey(context.getID())) {
throw new IllegalStateException("Duplicate game state provider: " + context.getID());
}
registeredStateProviders.put(context.getID(), context);
}
public static void register(IProgramExtension extender) {
if (registeredStateProviders.containsKey(extender.getID())) {
throw new IllegalStateException("Duplicate shader extension: " + extender.getID());
}
registeredExtensions.put(extender.getID(), extender);
}
}

View file

@ -0,0 +1,42 @@
package com.jozufozu.flywheel.backend.core.shader.spec;
import com.jozufozu.flywheel.backend.core.shader.gamestate.IGameStateProvider;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.util.ResourceLocation;
public class SpecificValueCondition implements IContextCondition {
public static final Codec<SpecificValueCondition> CODEC = RecordCodecBuilder.create(condition -> condition.group(
IGameStateProvider.CODEC.fieldOf("provider").forGetter(SpecificValueCondition::contextProvider),
Codec.STRING.fieldOf("value").forGetter(SpecificValueCondition::getValue)
).apply(condition, SpecificValueCondition::new));
private final String required;
private final IGameStateProvider context;
public SpecificValueCondition(IGameStateProvider context, String required) {
this.required = required;
this.context = context;
}
@Override
public ResourceLocation getID() {
return context.getID();
}
public String getValue() {
return required;
}
@Override
public IGameStateProvider contextProvider() {
return context;
}
@Override
public boolean get() {
return required.equals(context.getValue().toString());
}
}

View file

@ -1,45 +0,0 @@
package com.jozufozu.flywheel.backend.gl.shader;
import com.jozufozu.flywheel.backend.gl.GlPrimitiveType;
public class GLSLType {
public static final GLSLType FLOAT = new GLSLType("mat4", GlPrimitiveType.FLOAT, 16);
public static final GLSLType VEC2 = new GLSLType("vec4", GlPrimitiveType.FLOAT, 4);
public static final GLSLType VEC3 = new GLSLType("vec3", GlPrimitiveType.FLOAT, 3);
public static final GLSLType VEC4 = new GLSLType("vec2", GlPrimitiveType.FLOAT, 2);
public static final GLSLType MAT4 = new GLSLType("float", GlPrimitiveType.FLOAT, 1);
private final String symbol;
private final GlPrimitiveType base;
private final int count;
private final int size;
private final int attributeCount;
public GLSLType(String symbol, GlPrimitiveType base, int count) {
this.symbol = symbol;
this.base = base;
this.count = count;
this.size = base.getSize() * count;
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
}
public String getSymbol() {
return symbol;
}
public GlPrimitiveType getBase() {
return base;
}
public int getCount() {
return count;
}
public int getSize() {
return size;
}
public int getAttributeCount() {
return attributeCount;
}
}

View file

@ -1,10 +0,0 @@
package com.jozufozu.flywheel.backend.gl.shader;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.ShaderLoader;
import com.jozufozu.flywheel.backend.core.shader.ProgramSpec;
public interface ShaderSpecLoader<P extends GlProgram> {
IMultiProgram<P> create(ShaderLoader loader, ShaderContext<P> ctx, ProgramSpec spec);
}

View file

@ -11,8 +11,8 @@ import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.core.WorldContext;
import com.jozufozu.flywheel.backend.core.materials.ModelData;
import com.jozufozu.flywheel.backend.core.materials.OrientedData;
import com.jozufozu.flywheel.backend.core.shader.ShaderCallback;
import com.jozufozu.flywheel.backend.core.shader.WorldProgram;
import com.jozufozu.flywheel.backend.gl.shader.ShaderCallback;
import com.simibubi.create.foundation.render.AllMaterialSpecs;
import net.minecraft.client.Minecraft;

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.backend.core.shader.ProgramSpec;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import net.minecraft.util.ResourceLocation;
@ -9,12 +8,12 @@ public class MaterialSpec<D extends InstanceData> {
public final ResourceLocation name;
private final ProgramSpec programSpec;
private final ResourceLocation programSpec;
private final VertexFormat modelFormat;
private final VertexFormat instanceFormat;
private final InstanceFactory<D> instanceFactory;
public MaterialSpec(ResourceLocation name, ProgramSpec programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, InstanceFactory<D> instanceFactory) {
public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, InstanceFactory<D> instanceFactory) {
this.name = name;
this.programSpec = programSpec;
this.modelFormat = modelFormat;
@ -22,7 +21,7 @@ public class MaterialSpec<D extends InstanceData> {
this.instanceFactory = instanceFactory;
}
public ProgramSpec getProgramSpec() {
public ResourceLocation getProgramSpec() {
return programSpec;
}

View file

@ -13,9 +13,9 @@ import org.lwjgl.opengl.GL11;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.jozufozu.flywheel.backend.core.PartialModel;
import com.jozufozu.flywheel.backend.core.shader.ShaderCallback;
import com.jozufozu.flywheel.backend.core.shader.WorldProgram;
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
import com.jozufozu.flywheel.backend.gl.shader.ShaderCallback;
import com.jozufozu.flywheel.util.BufferBuilderReader;
import com.jozufozu.flywheel.util.RenderUtil;
import com.jozufozu.flywheel.util.VirtualEmptyModelData;

View file

@ -0,0 +1,26 @@
package com.jozufozu.flywheel.util;
import java.util.Collections;
import java.util.List;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
public class CodecUtil {
/**
* Creates a list codec that can be parsed from either a single element or a complete list.
*/
public static <T> Codec<List<T>> oneOrMore(Codec<T> codec) {
return Codec.either(codec.listOf(), codec)
.xmap(
either -> either.map(l -> l, Collections::singletonList),
list -> {
if (list.size() == 1) {
return Either.right(list.get(0));
} else {
return Either.left(list);
}
});
}
}

View file

@ -0,0 +1,68 @@
package com.jozufozu.flywheel.util;
import java.util.Objects;
public class Pair<F, S> {
F first;
S second;
protected Pair(F first, S second) {
this.first = first;
this.second = second;
}
public static <F, S> Pair<F, S> of(F first, S second) {
return new Pair<>(first, second);
}
public F getFirst() {
return first;
}
public S getSecond() {
return second;
}
public void setFirst(F first) {
this.first = first;
}
public void setSecond(S second) {
this.second = second;
}
public Pair<F, S> copy() {
return Pair.of(first, second);
}
@Override
public boolean equals(final Object obj) {
if (obj == this)
return true;
if (obj instanceof Pair) {
final Pair<?, ?> other = (Pair<?, ?>) obj;
return Objects.equals(first, other.first) && Objects.equals(second, other.second);
}
return false;
}
@Override
public int hashCode() {
return (nullHash(first) * 31) ^ nullHash(second);
}
int nullHash(Object o) {
return o == null ? 0 : o.hashCode();
}
@Override
public String toString() {
return "(" + first + ", " + second + ")";
}
public Pair<S, F> swap() {
return Pair.of(second, first);
}
}

View file

@ -27,7 +27,6 @@ import com.simibubi.create.foundation.item.CustomRenderedItems;
import com.simibubi.create.foundation.ponder.content.PonderIndex;
import com.simibubi.create.foundation.ponder.elements.WorldSectionElement;
import com.simibubi.create.foundation.render.AllMaterialSpecs;
import com.simibubi.create.foundation.render.AllProgramSpecs;
import com.simibubi.create.foundation.render.CreateFlywheelHandler;
import com.simibubi.create.foundation.render.SuperByteBufferCache;
import com.simibubi.create.foundation.utility.ghost.GhostBlocks;
@ -87,7 +86,6 @@ public class CreateClient {
}
public static void clientInit(FMLClientSetupEvent event) {
AllProgramSpecs.init();
AllMaterialSpecs.init();
schematicSender = new ClientSchematicLoader();

View file

@ -4,8 +4,8 @@ import java.util.List;
import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.backend.core.shader.ProgramExtender;
import com.jozufozu.flywheel.backend.core.shader.WorldProgram;
import com.jozufozu.flywheel.backend.core.shader.extension.IProgramExtension;
import com.jozufozu.flywheel.backend.loading.Program;
import net.minecraft.util.math.AxisAlignedBB;
@ -18,8 +18,8 @@ public class ContraptionProgram extends WorldProgram {
protected int uLightVolume;
public ContraptionProgram(Program program, List<ProgramExtender> fogFactory) {
super(program, fogFactory);
public ContraptionProgram(Program program, List<IProgramExtension> extensions) {
super(program, extensions);
uLightBoxSize = getUniformLocation("uLightBoxSize");
uLightBoxMin = getUniformLocation("uLightBoxMin");
uModel = getUniformLocation("uModel");

View file

@ -15,7 +15,6 @@ import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.Pair;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.core.FogMultiProgram;
import com.jozufozu.flywheel.backend.core.WorldContext;
import com.jozufozu.flywheel.backend.loading.ModelTemplate;
import com.mojang.blaze3d.matrix.MatrixStack;
@ -71,8 +70,10 @@ public class ContraptionRenderDispatcher {
public static final Compartment<Pair<Contraption, Integer>> CONTRAPTION = new Compartment<>();
private static final ResourceLocation ctxRoot = new ResourceLocation("create", "context/contraption");
public static final WorldContext<ContraptionProgram> STRUCTURE = new WorldContext<>(ctxRoot, new FogMultiProgram.SpecLoader<>(ContraptionProgram::new), () -> Stream.of(AllProgramSpecs.STRUCTURE), ModelTemplate::new);
public static final WorldContext<ContraptionProgram> TILES = new WorldContext<>(ctxRoot, new FogMultiProgram.SpecLoader<>(ContraptionProgram::new));
public static final WorldContext<ContraptionProgram> STRUCTURE = new WorldContext<>(ctxRoot, ContraptionProgram::new)
.setSpecStream(() -> Stream.of(AllProgramSpecs.STRUCTURE))
.setTemplateFactory(ModelTemplate::new);
public static final WorldContext<ContraptionProgram> TILES = new WorldContext<>(ctxRoot, ContraptionProgram::new);
public static void tick() {
if (Minecraft.getInstance().isGamePaused()) return;

View file

@ -24,10 +24,10 @@ public class AllMaterialSpecs {
.addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, CommonAttributes.UV)
.build();
public static final MaterialSpec<RotatingData> ROTATING = register(new MaterialSpec<>(Locations.ROTATING, AllProgramSpecs.ROTATING, UNLIT_MODEL, AllInstanceFormats.ROTATING, RotatingData::new));
public static final MaterialSpec<ModelData> TRANSFORMED = register(new MaterialSpec<>(Locations.MODEL, AllProgramSpecs.MODEL, UNLIT_MODEL, AllInstanceFormats.MODEL, ModelData::new));
public static final MaterialSpec<OrientedData> ORIENTED = register(new MaterialSpec<>(Locations.ORIENTED, AllProgramSpecs.ORIENTED, UNLIT_MODEL, AllInstanceFormats.ORIENTED, OrientedData::new));
public static final MaterialSpec<RotatingData> ROTATING = register(new MaterialSpec<>(Locations.ROTATING, AllProgramSpecs.ROTATING, UNLIT_MODEL, AllInstanceFormats.ROTATING, RotatingData::new));
public static final MaterialSpec<BeltData> BELTS = register(new MaterialSpec<>(Locations.BELTS, AllProgramSpecs.BELT, UNLIT_MODEL, AllInstanceFormats.BELT, BeltData::new));
public static final MaterialSpec<ActorData> ACTORS = register(new MaterialSpec<>(Locations.ACTORS, AllProgramSpecs.ACTOR, UNLIT_MODEL, AllInstanceFormats.ACTOR, ActorData::new));
public static final MaterialSpec<FlapData> FLAPS = register(new MaterialSpec<>(Locations.FLAPS, AllProgramSpecs.FLAPS, UNLIT_MODEL, AllInstanceFormats.FLAP, FlapData::new));

View file

@ -1,42 +1,21 @@
package com.simibubi.create.foundation.render;
import static com.jozufozu.flywheel.backend.Backend.register;
import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.core.shader.ProgramSpec;
import com.simibubi.create.Create;
import net.minecraft.util.ResourceLocation;
public class AllProgramSpecs {
public static void init() {
// noop, make sure the static field are loaded.
}
public static final ProgramSpec CHROMATIC = register(new ProgramSpec(loc("chromatic"), Locations.EFFECT_VERT, Locations.EFFECT_FRAG));
public static final ProgramSpec MODEL = register(new ProgramSpec(new ResourceLocation(Flywheel.ID, "model"), Locations.MODEL_VERT, Locations.BLOCK));
public static final ProgramSpec ORIENTED = register(new ProgramSpec(new ResourceLocation(Flywheel.ID, "oriented"), Locations.ORIENTED, Locations.BLOCK));
public static final ProgramSpec ROTATING = register(new ProgramSpec(loc("rotating"), Locations.ROTATING, Locations.BLOCK));
public static final ProgramSpec BELT = register(new ProgramSpec(loc("belt"), Locations.BELT, Locations.BLOCK));
public static final ProgramSpec FLAPS = register(new ProgramSpec(loc("flap"), Locations.FLAP, Locations.BLOCK));
public static final ProgramSpec STRUCTURE = register(new ProgramSpec(loc("contraption_structure"), Locations.CONTRAPTION_STRUCTURE, Locations.BLOCK));
public static final ProgramSpec ACTOR = register(new ProgramSpec(loc("contraption_actor"), Locations.CONTRAPTION_ACTOR, Locations.BLOCK));
public static final ResourceLocation MODEL = new ResourceLocation(Flywheel.ID, "model");//, Locations.MODEL_VERT, Locations.BLOCK);
public static final ResourceLocation ORIENTED = new ResourceLocation(Flywheel.ID, "oriented");//, Locations.ORIENTED, Locations.BLOCK);
public static class Locations {
public static final ResourceLocation BLOCK = new ResourceLocation(Flywheel.ID, "block.frag");
public static final ResourceLocation MODEL_VERT = new ResourceLocation(Flywheel.ID, "model.vert");
public static final ResourceLocation ORIENTED = new ResourceLocation(Flywheel.ID, "oriented.vert");
public static final ResourceLocation ROTATING = loc("rotating.vert");
public static final ResourceLocation BELT = loc("belt.vert");
public static final ResourceLocation FLAP = loc("flap.vert");
public static final ResourceLocation CONTRAPTION_STRUCTURE = loc("contraption_structure.vert");
public static final ResourceLocation CONTRAPTION_ACTOR = loc("contraption_actor.vert");
public static final ResourceLocation EFFECT_VERT = loc("area_effect.vert");
public static final ResourceLocation EFFECT_FRAG = loc("area_effect.frag");
}
public static final ResourceLocation ROTATING = loc("rotating");
public static final ResourceLocation CHROMATIC = loc("chromatic");
public static final ResourceLocation BELT = loc("belt");
public static final ResourceLocation FLAPS = loc("flap");
public static final ResourceLocation STRUCTURE = loc("contraption_structure");
public static final ResourceLocation ACTOR = loc("contraption_actor");
private static ResourceLocation loc(String name) {
return new ResourceLocation(Create.ID, name);

View file

@ -1,8 +1,12 @@
package com.simibubi.create.foundation.render.effects;
import java.util.Collections;
import com.jozufozu.flywheel.backend.Backend;
import com.jozufozu.flywheel.backend.ShaderContext;
import com.jozufozu.flywheel.backend.ShaderLoader;
import com.jozufozu.flywheel.backend.core.shader.SingleProgram;
import com.jozufozu.flywheel.backend.core.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.core.shader.spec.ProgramSpec;
import com.jozufozu.flywheel.backend.loading.ShaderTransformer;
import com.simibubi.create.foundation.render.AllProgramSpecs;
@ -11,13 +15,18 @@ public class EffectsContext extends ShaderContext<SphereFilterProgram> {
public static final EffectsContext INSTANCE = new EffectsContext();
public EffectsContext() {
super(new SingleProgram.SpecLoader<>(SphereFilterProgram::new));
super();
}
@Override
protected IMultiProgram<SphereFilterProgram> loadSpecInternal(ShaderLoader loader, ProgramSpec spec) {
return new SphereFilterProgram(loadProgram(loader, spec, Collections.emptyList()));
}
@Override
public void load(ShaderLoader loader) {
transformer = new ShaderTransformer()
.pushStage(loader::processIncludes);
loadProgramFromSpec(loader, AllProgramSpecs.CHROMATIC);
loadProgramFromSpec(loader, Backend.getSpec(AllProgramSpecs.CHROMATIC));
}
}

View file

@ -5,6 +5,7 @@ import java.util.ArrayList;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL31;
import com.jozufozu.flywheel.backend.core.shader.IMultiProgram;
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
@ -14,7 +15,7 @@ import com.jozufozu.flywheel.backend.loading.Program;
import net.minecraft.util.math.vector.Matrix4f;
import net.minecraft.util.math.vector.Vector3d;
public class SphereFilterProgram extends GlProgram {
public class SphereFilterProgram extends GlProgram implements IMultiProgram<SphereFilterProgram> {
protected static final int UBO_BINDING = 4;
@ -80,7 +81,7 @@ public class SphereFilterProgram extends GlProgram {
}
public void uploadFilters(ArrayList<FilterSphere> filters) {
effectsUBO.bind(GlBufferType.ARRAY_BUFFER);
effectsUBO.bind();
MappedBuffer buffer = effectsUBO.getBuffer(0, BUFFER_SIZE)
.putInt(filters.size())
.position(16);
@ -89,7 +90,7 @@ public class SphereFilterProgram extends GlProgram {
buffer.flush();
effectsUBO.unbind(GlBufferType.ARRAY_BUFFER);
effectsUBO.unbind();
}
public void bindInverseProjection(Matrix4f mat) {
@ -110,4 +111,8 @@ public class SphereFilterProgram extends GlProgram {
GL20.glBindTexture(GL20.GL_TEXTURE_2D, textureObject);
}
@Override
public SphereFilterProgram get() {
return this;
}
}

View file

@ -0,0 +1,33 @@
{
"vert": "create:belt.vert",
"frag": "flywheel:block.frag",
"states": [
{
"when": "create:rainbow_debug",
"define": "DEBUG_RAINBOW"
},
{
"when": {
"provider": "flywheel:normal_debug",
"value": "true"
},
"define": "DEBUG_NORMAL"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "linear"
},
"define": ["USE_FOG", "USE_FOG_LINEAR"],
"extend": "flywheel:fog_linear"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "exp2"
},
"define": ["USE_FOG", "USE_FOG_EXP2"],
"extend": "flywheel:fog_exp2"
}
]
}

View file

@ -0,0 +1,4 @@
{
"vert": "create:area_effect.vert",
"frag": "create:area_effect.frag"
}

View file

@ -0,0 +1,29 @@
{
"vert": "create:contraption_actor.vert",
"frag": "flywheel:block.frag",
"states": [
{
"when": {
"provider": "flywheel:normal_debug",
"value": "true"
},
"define": "DEBUG_NORMAL"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "linear"
},
"define": ["USE_FOG", "USE_FOG_LINEAR"],
"extend": "flywheel:fog_linear"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "exp2"
},
"define": ["USE_FOG", "USE_FOG_EXP2"],
"extend": "flywheel:fog_exp2"
}
]
}

View file

@ -0,0 +1,29 @@
{
"vert": "create:contraption_structure.vert",
"frag": "flywheel:block.frag",
"states": [
{
"when": {
"provider": "flywheel:normal_debug",
"value": "true"
},
"define": "DEBUG_NORMAL"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "linear"
},
"define": ["USE_FOG", "USE_FOG_LINEAR"],
"extend": "flywheel:fog_linear"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "exp2"
},
"define": ["USE_FOG", "USE_FOG_EXP2"],
"extend": "flywheel:fog_exp2"
}
]
}

View file

@ -0,0 +1,29 @@
{
"vert": "create:flap.vert",
"frag": "flywheel:block.frag",
"states": [
{
"when": {
"provider": "flywheel:normal_debug",
"value": "true"
},
"define": "DEBUG_NORMAL"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "linear"
},
"define": ["USE_FOG", "USE_FOG_LINEAR"],
"extend": "flywheel:fog_linear"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "exp2"
},
"define": ["USE_FOG", "USE_FOG_EXP2"],
"extend": "flywheel:fog_exp2"
}
]
}

View file

@ -0,0 +1,33 @@
{
"vert": "create:rotating.vert",
"frag": "flywheel:block.frag",
"states": [
{
"when": "create:rainbow_debug",
"define": "DEBUG_RAINBOW"
},
{
"when": {
"provider": "flywheel:normal_debug",
"value": "true"
},
"define": "DEBUG_NORMAL"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "linear"
},
"define": ["USE_FOG", "USE_FOG_LINEAR"],
"extend": "flywheel:fog_linear"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "exp2"
},
"define": ["USE_FOG", "USE_FOG_EXP2"],
"extend": "flywheel:fog_exp2"
}
]
}

View file

@ -39,9 +39,9 @@ BlockFrag FLWMain(Vertex v, Belt instance) {
b.texCoords = v.texCoords - instance.sourceTexture + instance.scrollTexture.xy + vec2(0, scroll);
b.light = instance.light;
#if defined(RAINBOW_DEBUG)
#if defined(DEBUG_RAINBOW)
b.color = instance.color;
#elif defined(NORMAL_DEBUG)
#elif defined(DEBUG_NORMAL)
b.color = vec4(norm, 1.);
#else
b.color = vec4(1.);

View file

@ -37,7 +37,7 @@ BlockFrag FLWMain(Vertex v, Actor instance) {
b.texCoords = v.texCoords;
b.light = instance.light;
#if defined(NORMAL_DEBUG)
#if defined(DEBUG_NORMAL)
b.color = vec4(norm, 1.);
#else
b.color = vec4(1.);

View file

@ -27,7 +27,7 @@ BlockFrag FLWMain(Vertex v) {
b.texCoords = v.texCoords;
b.light = v.modelLight;
#if defined(NORMAL_DEBUG)
#if defined(DEBUG_NORMAL)
b.color = vec4(norm, 1.);
#else
b.color = v.color / diffuse(v.normal);

View file

@ -57,7 +57,7 @@ BlockFrag FLWMain(Vertex v, Flap flap) {
b.diffuse = diffuse(norm);
b.texCoords = v.texCoords;
b.light = flap.light;
#if defined(NORMAL_DEBUG)
#if defined(DEBUG_NORMAL)
b.color = vec4(norm, 1.);
#else
b.color = vec4(1.);

View file

@ -40,9 +40,9 @@ BlockFrag FLWMain(Vertex v, Rotating instance) {
b.texCoords = v.texCoords;
b.light = instance.light;
#if defined(RAINBOW_DEBUG)
#if defined(DEBUG_RAINBOW)
b.color = instance.color;
#elif defined(NORMAL_DEBUG)
#elif defined(DEBUG_NORMAL)
b.color = vec4(norm, 1.);
#else
b.color = vec4(1.);

View file

@ -0,0 +1,29 @@
{
"vert": "flywheel:model.vert",
"frag": "flywheel:block.frag",
"states": [
{
"when": {
"provider": "flywheel:normal_debug",
"value": "true"
},
"define": "DEBUG_NORMAL"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "linear"
},
"define": ["USE_FOG", "USE_FOG_LINEAR"],
"extend": "flywheel:fog_linear"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "exp2"
},
"define": ["USE_FOG", "USE_FOG_EXP2"],
"extend": "flywheel:fog_exp2"
}
]
}

View file

@ -0,0 +1,29 @@
{
"vert": "flywheel:oriented.vert",
"frag": "flywheel:block.frag",
"states": [
{
"when": {
"provider": "flywheel:normal_debug",
"value": "true"
},
"define": "DEBUG_NORMAL"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "linear"
},
"define": ["USE_FOG", "USE_FOG_LINEAR"],
"extend": "flywheel:fog_linear"
},
{
"when": {
"provider": "flywheel:fog_mode",
"value": "exp2"
},
"define": ["USE_FOG", "USE_FOG_EXP2"],
"extend": "flywheel:fog_exp2"
}
]
}

View file

@ -26,7 +26,7 @@ BlockFrag FLWMain(Vertex v, Instance i) {
b.diffuse = diffuse(norm);
b.texCoords = v.texCoords;
b.light = i.light;
#if defined(NORMAL_DEBUG)
#if defined(DEBUG_NORMAL)
b.color = vec4(norm, 1.);
#else
b.color = i.color;

View file

@ -27,7 +27,7 @@ BlockFrag FLWMain(Vertex v, Oriented o) {
b.diffuse = diffuse(norm);
b.texCoords = v.texCoords;
b.light = o.light;
#if defined(NORMAL_DEBUG)
#if defined(DEBUG_NORMAL)
b.color = vec4(norm, 1.);
#else
b.color = o.color;