mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-12-15 10:43:42 +01:00
Yeet
This commit is contained in:
parent
d612cfdd34
commit
652111c233
184 changed files with 56 additions and 9533 deletions
12
build.gradle
12
build.gradle
|
@ -41,6 +41,7 @@ minecraft {
|
||||||
//jvmArgs '-XX:+UnlockCommercialFeatures' // uncomment for profiling
|
//jvmArgs '-XX:+UnlockCommercialFeatures' // uncomment for profiling
|
||||||
property 'forge.logging.console.level', 'info'
|
property 'forge.logging.console.level', 'info'
|
||||||
property 'fml.earlyprogresswindow', 'false'
|
property 'fml.earlyprogresswindow', 'false'
|
||||||
|
property 'mixin.env.disableRefMap', 'true'
|
||||||
mods {
|
mods {
|
||||||
create {
|
create {
|
||||||
source sourceSets.main
|
source sourceSets.main
|
||||||
|
@ -101,11 +102,14 @@ repositories {
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
//location of the maven for mixed mappings and registrate
|
//location of the maven for mixed mappings and registrate
|
||||||
name = "tterrag maven"
|
name "tterrag maven"
|
||||||
url = "https://maven.tterrag.com/"
|
url "https://maven.tterrag.com/"
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
url = "https://www.cursemaven.com"
|
url "https://www.cursemaven.com"
|
||||||
|
content {
|
||||||
|
includeGroup "curse.maven"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +129,8 @@ dependencies {
|
||||||
// at runtime, use the full JEI jar
|
// at runtime, use the full JEI jar
|
||||||
runtimeOnly fg.deobf("mezz.jei:jei-1.16.4:${jei_version}")
|
runtimeOnly fg.deobf("mezz.jei:jei-1.16.4:${jei_version}")
|
||||||
|
|
||||||
|
compile fg.deobf("curse.maven:flywheel-486392:3357100") // cursemaven for now
|
||||||
|
|
||||||
// implementation fg.deobf("curse.maven:druidcraft-340991:3101903")
|
// implementation fg.deobf("curse.maven:druidcraft-340991:3101903")
|
||||||
|
|
||||||
// i'll leave this here commented for easier testing
|
// i'll leave this here commented for easier testing
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.jozufozu.flywheel;
|
|
||||||
|
|
||||||
public class Flywheel {
|
|
||||||
|
|
||||||
public static final String ID = "flywheel";
|
|
||||||
}
|
|
|
@ -1,184 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.lwjgl.opengl.GL;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialSpec;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
|
||||||
import com.simibubi.create.foundation.config.AllConfigs;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
|
||||||
import net.minecraft.world.IWorld;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
public class Backend {
|
|
||||||
public static final Logger log = LogManager.getLogger(Backend.class);
|
|
||||||
|
|
||||||
protected static final Backend INSTANCE = new Backend();
|
|
||||||
|
|
||||||
public static Backend getInstance() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Minecraft minecraft;
|
|
||||||
public ShaderSources sources;
|
|
||||||
|
|
||||||
public GLCapabilities capabilities;
|
|
||||||
public GlCompat compat;
|
|
||||||
|
|
||||||
private Matrix4f projectionMatrix = new Matrix4f();
|
|
||||||
private boolean instancedArrays;
|
|
||||||
private boolean enabled;
|
|
||||||
|
|
||||||
private final List<IShaderContext<?>> contexts = new ArrayList<>();
|
|
||||||
private final Map<ResourceLocation, MaterialSpec<?>> materialRegistry = new HashMap<>();
|
|
||||||
private final Map<ResourceLocation, ProgramSpec> programSpecRegistry = new HashMap<>();
|
|
||||||
|
|
||||||
protected Backend() {
|
|
||||||
// Can be null when running datagenerators due to the unfortunate time we call this
|
|
||||||
minecraft = Minecraft.getInstance();
|
|
||||||
if (minecraft == null) return;
|
|
||||||
|
|
||||||
sources = new ShaderSources(this);
|
|
||||||
|
|
||||||
OptifineHandler.init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearContexts() {
|
|
||||||
SpecMetaRegistry.clear();
|
|
||||||
contexts.forEach(IShaderContext::delete);
|
|
||||||
materialRegistry.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a string describing the Flywheel backend. When there are eventually multiple backends
|
|
||||||
* (Meshlet, MDI, GL31 Draw Instanced are planned), this will name which one is in use.
|
|
||||||
*/
|
|
||||||
public String getBackendDescriptor() {
|
|
||||||
if (canUseInstancing()) {
|
|
||||||
return "GL33 Instanced Arrays";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (canUseVBOs()) {
|
|
||||||
return "VBOs";
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Disabled";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a shader program.
|
|
||||||
*/
|
|
||||||
public ProgramSpec register(ProgramSpec spec) {
|
|
||||||
ResourceLocation name = spec.name;
|
|
||||||
if (programSpecRegistry.containsKey(name)) {
|
|
||||||
throw new IllegalStateException("Program spec '" + name + "' already registered.");
|
|
||||||
}
|
|
||||||
programSpecRegistry.put(name, spec);
|
|
||||||
return spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a shader context.
|
|
||||||
*/
|
|
||||||
public <C extends ShaderContext<?>> C register(C spec) {
|
|
||||||
contexts.add(spec);
|
|
||||||
return spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register an instancing material.
|
|
||||||
*/
|
|
||||||
public <D extends InstanceData> MaterialSpec<D> register(MaterialSpec<D> spec) {
|
|
||||||
ResourceLocation name = spec.name;
|
|
||||||
if (materialRegistry.containsKey(name)) {
|
|
||||||
throw new IllegalStateException("Material spec '" + name + "' already registered.");
|
|
||||||
}
|
|
||||||
materialRegistry.put(name, spec);
|
|
||||||
return spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ProgramSpec getSpec(ResourceLocation name) {
|
|
||||||
return programSpecRegistry.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean available() {
|
|
||||||
return canUseVBOs();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canUseInstancing() {
|
|
||||||
return enabled && instancedArrays;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canUseVBOs() {
|
|
||||||
return enabled && gl20();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean gl33() {
|
|
||||||
return capabilities.OpenGL33;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean gl20() {
|
|
||||||
return capabilities.OpenGL20;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refresh() {
|
|
||||||
OptifineHandler.refresh();
|
|
||||||
capabilities = GL.createCapabilities();
|
|
||||||
|
|
||||||
compat = new GlCompat(capabilities);
|
|
||||||
|
|
||||||
instancedArrays = compat.vertexArrayObjectsSupported() &&
|
|
||||||
compat.drawInstancedSupported() &&
|
|
||||||
compat.instancedArraysSupported();
|
|
||||||
|
|
||||||
enabled = AllConfigs.CLIENT.experimentalRendering.get() && !OptifineHandler.usingShaders();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean canUseInstancing(World world) {
|
|
||||||
return canUseInstancing() && isFlywheelWorld(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<MaterialSpec<?>> allMaterials() {
|
|
||||||
return materialRegistry.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<ProgramSpec> allPrograms() {
|
|
||||||
return programSpecRegistry.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<IShaderContext<?>> allContexts() {
|
|
||||||
return contexts;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Matrix4f getProjectionMatrix() {
|
|
||||||
return projectionMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProjectionMatrix(Matrix4f projectionMatrix) {
|
|
||||||
this.projectionMatrix = projectionMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to avoid calling Flywheel functions on (fake) worlds that don't specifically support it.
|
|
||||||
*/
|
|
||||||
public static boolean isFlywheelWorld(IWorld world) {
|
|
||||||
return (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel()) || world == Minecraft.getInstance().world;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reloadWorldRenderers() {
|
|
||||||
RenderWork.enqueue(Minecraft.getInstance().worldRenderer::loadRenderers);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A marker interface custom worlds can override to indicate
|
|
||||||
* that tiles inside the world should render with Flywheel.
|
|
||||||
*
|
|
||||||
* <code>Minecraft.getInstance().world</code> is special cased and will support Flywheel by default.
|
|
||||||
*/
|
|
||||||
public interface IFlywheelWorld {
|
|
||||||
default boolean supportsFlywheel() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public interface IShaderContext<P extends GlProgram> {
|
|
||||||
|
|
||||||
default P getProgram(ResourceLocation loc) {
|
|
||||||
return this.getProgramSupplier(loc).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
Supplier<P> getProgramSupplier(ResourceLocation loc);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load all programs associated with this context. This might be just one, if the context is very specialized.
|
|
||||||
*/
|
|
||||||
void load();
|
|
||||||
|
|
||||||
void delete();
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
|
|
||||||
public class OptifineHandler {
|
|
||||||
public static final String OPTIFINE_ROOT_PACKAGE = "net.optifine";
|
|
||||||
public static final String SHADER_PACKAGE = "net.optifine.shaders";
|
|
||||||
|
|
||||||
private static Package optifine;
|
|
||||||
private static OptifineHandler handler;
|
|
||||||
|
|
||||||
public final boolean usingShaders;
|
|
||||||
|
|
||||||
public OptifineHandler(boolean usingShaders) {
|
|
||||||
this.usingShaders = usingShaders;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get information about the current Optifine configuration.
|
|
||||||
*
|
|
||||||
* @return {@link Optional#empty()} if Optifine is not installed.
|
|
||||||
*/
|
|
||||||
public static Optional<OptifineHandler> get() {
|
|
||||||
return Optional.ofNullable(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean optifineInstalled() {
|
|
||||||
return optifine != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean usingShaders() {
|
|
||||||
return OptifineHandler.get()
|
|
||||||
.map(OptifineHandler::isUsingShaders)
|
|
||||||
.orElse(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
optifine = Package.getPackage(OPTIFINE_ROOT_PACKAGE);
|
|
||||||
|
|
||||||
if (optifine == null) {
|
|
||||||
Backend.log.info("Optifine not detected.");
|
|
||||||
} else {
|
|
||||||
Backend.log.info("Optifine detected.");
|
|
||||||
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void refresh() {
|
|
||||||
if (optifine == null) return;
|
|
||||||
|
|
||||||
File dir = Minecraft.getInstance().gameDir;
|
|
||||||
|
|
||||||
File shaderOptions = new File(dir, "optionsshaders.txt");
|
|
||||||
|
|
||||||
boolean shadersOff = true;
|
|
||||||
try {
|
|
||||||
BufferedReader reader = new BufferedReader(new FileReader(shaderOptions));
|
|
||||||
|
|
||||||
shadersOff = reader.lines()
|
|
||||||
.anyMatch(it -> {
|
|
||||||
String line = it.replaceAll("\\s", "");
|
|
||||||
if (line.startsWith("shaderPack=")) {
|
|
||||||
String setting = line.substring("shaderPack=".length());
|
|
||||||
|
|
||||||
return setting.equals("OFF") || setting.equals("(internal)");
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
Backend.log.info("No shader config found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
handler = new OptifineHandler(!shadersOff);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUsingShaders() {
|
|
||||||
return usingShaders;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.client.event.RenderWorldLastEvent;
|
|
||||||
import net.minecraftforge.eventbus.api.EventPriority;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
|
|
||||||
@Mod.EventBusSubscriber(value = Dist.CLIENT)
|
|
||||||
public class RenderWork {
|
|
||||||
private static final Queue<Runnable> runs = new ConcurrentLinkedQueue<>();
|
|
||||||
|
|
||||||
|
|
||||||
@SubscribeEvent(priority = EventPriority.LOWEST)
|
|
||||||
public static void onRenderWorldLast(RenderWorldLastEvent event) {
|
|
||||||
while (!runs.isEmpty()) {
|
|
||||||
runs.remove().run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queue work to be executed at the end of a frame
|
|
||||||
*/
|
|
||||||
public static void enqueue(Runnable run) {
|
|
||||||
runs.add(run);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Program;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
|
||||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramState;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public abstract class ShaderContext<P extends GlProgram> implements IShaderContext<P> {
|
|
||||||
|
|
||||||
protected final Map<ResourceLocation, IMultiProgram<P>> programs = new HashMap<>();
|
|
||||||
|
|
||||||
public final Backend backend;
|
|
||||||
|
|
||||||
public ShaderContext(Backend backend) {
|
|
||||||
this.backend = backend;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Supplier<P> getProgramSupplier(ResourceLocation spec) {
|
|
||||||
return programs.get(spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Program loadAndLink(ProgramSpec spec, @Nullable ProgramState state) {
|
|
||||||
Shader vertexFile = getSource(ShaderType.VERTEX, spec.vert);
|
|
||||||
Shader fragmentFile = getSource(ShaderType.FRAGMENT, spec.frag);
|
|
||||||
|
|
||||||
if (state != null) {
|
|
||||||
vertexFile.defineAll(state.getDefines());
|
|
||||||
fragmentFile.defineAll(state.getDefines());
|
|
||||||
}
|
|
||||||
|
|
||||||
return link(buildProgram(spec.name, vertexFile, fragmentFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Shader getSource(ShaderType type, ResourceLocation name) {
|
|
||||||
return backend.sources.source(name, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Program link(Program program) {
|
|
||||||
return program.link();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete() {
|
|
||||||
programs.values().forEach(IMultiProgram::delete);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ingests the given shaders, compiling them and linking them together after applying the transformer to the source.
|
|
||||||
*
|
|
||||||
* @param name What should we call this program if something goes wrong?
|
|
||||||
* @param shaders What are the different shader stages that should be linked together?
|
|
||||||
* @return A program with all provided shaders attached
|
|
||||||
*/
|
|
||||||
protected static Program buildProgram(ResourceLocation name, Shader... shaders) {
|
|
||||||
List<GlShader> compiled = new ArrayList<>(shaders.length);
|
|
||||||
try {
|
|
||||||
Program builder = new Program(name);
|
|
||||||
|
|
||||||
for (Shader shader : shaders) {
|
|
||||||
GlShader sh = new GlShader(shader);
|
|
||||||
compiled.add(sh);
|
|
||||||
|
|
||||||
builder.attachShader(shader, sh);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
} finally {
|
|
||||||
compiled.forEach(GlObject::delete);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,212 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.channels.Channels;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
import java.nio.channels.ReadableByteChannel;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
|
|
||||||
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.gl.shader.ShaderType;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
|
|
||||||
import com.jozufozu.flywheel.core.shader.spec.ProgramSpec;
|
|
||||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
|
||||||
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.IReloadableResourceManager;
|
|
||||||
import net.minecraft.resources.IResource;
|
|
||||||
import net.minecraft.resources.IResourceManager;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraftforge.fml.ModLoader;
|
|
||||||
import net.minecraftforge.resource.IResourceType;
|
|
||||||
import net.minecraftforge.resource.ISelectiveResourceReloadListener;
|
|
||||||
import net.minecraftforge.resource.VanillaResourceType;
|
|
||||||
|
|
||||||
@ParametersAreNonnullByDefault
|
|
||||||
public class ShaderSources implements ISelectiveResourceReloadListener {
|
|
||||||
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");
|
|
||||||
private static final Gson GSON = new GsonBuilder().create();
|
|
||||||
|
|
||||||
private final Map<ResourceLocation, String> shaderSource = new HashMap<>();
|
|
||||||
|
|
||||||
private boolean shouldCrash;
|
|
||||||
private final Backend backend;
|
|
||||||
|
|
||||||
public ShaderSources(Backend backend) {
|
|
||||||
this.backend = backend;
|
|
||||||
IResourceManager manager = backend.minecraft.getResourceManager();
|
|
||||||
if (manager instanceof IReloadableResourceManager) {
|
|
||||||
((IReloadableResourceManager) manager).addReloadListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
|
|
||||||
if (predicate.test(VanillaResourceType.SHADERS)) {
|
|
||||||
backend.refresh();
|
|
||||||
|
|
||||||
if (backend.gl20()) {
|
|
||||||
shaderSource.clear();
|
|
||||||
|
|
||||||
shouldCrash = false;
|
|
||||||
|
|
||||||
backend.clearContexts();
|
|
||||||
ModLoader.get().postEvent(new GatherContextEvent(backend));
|
|
||||||
|
|
||||||
loadProgramSpecs(manager);
|
|
||||||
loadShaderSources(manager);
|
|
||||||
|
|
||||||
for (IShaderContext<?> context : backend.allContexts()) {
|
|
||||||
context.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shouldCrash) {
|
|
||||||
throw new ShaderLoadingException("Could not load all shaders, see log for details");
|
|
||||||
}
|
|
||||||
|
|
||||||
Backend.log.info("Loaded all shader programs.");
|
|
||||||
|
|
||||||
// no need to hog all that memory
|
|
||||||
shaderSource.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public String getShaderSource(ResourceLocation loc) {
|
|
||||||
String source = shaderSource.get(loc);
|
|
||||||
|
|
||||||
if (source == null) {
|
|
||||||
throw new ShaderLoadingException(String.format("shader '%s' does not exist", loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadShaderSources(IResourceManager manager) {
|
|
||||||
Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> {
|
|
||||||
for (String ext : EXTENSIONS) {
|
|
||||||
if (s.endsWith(ext)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (ResourceLocation location : allShaders) {
|
|
||||||
try {
|
|
||||||
IResource resource = manager.getResource(location);
|
|
||||||
|
|
||||||
String file = readToString(resource.getInputStream());
|
|
||||||
|
|
||||||
ResourceLocation name = ResourceUtil.removePrefixUnchecked(location, SHADER_DIR);
|
|
||||||
|
|
||||||
shaderSource.put(name, file);
|
|
||||||
} catch (IOException e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Shader source(ResourceLocation name, ShaderType type) {
|
|
||||||
return new Shader(this, type, name, getShaderSource(name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Stream<String> lines(String s) {
|
|
||||||
return new BufferedReader(new StringReader(s)).lines();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String readToString(InputStream is) {
|
|
||||||
RenderSystem.assertThread(RenderSystem::isOnRenderThread);
|
|
||||||
ByteBuffer bytebuffer = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
bytebuffer = readToBuffer(is);
|
|
||||||
int i = bytebuffer.position();
|
|
||||||
bytebuffer.rewind();
|
|
||||||
return MemoryUtil.memASCII(bytebuffer, i);
|
|
||||||
} catch (IOException e) {
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
if (bytebuffer != null) {
|
|
||||||
MemoryUtil.memFree(bytebuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer readToBuffer(InputStream is) throws IOException {
|
|
||||||
ByteBuffer bytebuffer;
|
|
||||||
if (is instanceof FileInputStream) {
|
|
||||||
FileInputStream fileinputstream = (FileInputStream) is;
|
|
||||||
FileChannel filechannel = fileinputstream.getChannel();
|
|
||||||
bytebuffer = MemoryUtil.memAlloc((int) filechannel.size() + 1);
|
|
||||||
|
|
||||||
while (filechannel.read(bytebuffer) != -1) {
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bytebuffer = MemoryUtil.memAlloc(8192);
|
|
||||||
ReadableByteChannel readablebytechannel = Channels.newChannel(is);
|
|
||||||
|
|
||||||
while (readablebytechannel.read(bytebuffer) != -1) {
|
|
||||||
if (bytebuffer.remaining() == 0) {
|
|
||||||
bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytebuffer;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.shader.extension.IProgramExtension;
|
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.IGameStateProvider;
|
|
||||||
|
|
||||||
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<>();
|
|
||||||
|
|
||||||
static void clear() {
|
|
||||||
registeredExtensions.clear();
|
|
||||||
registeredStateProviders.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL11;
|
|
||||||
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
|
||||||
public enum GlNumericType {
|
|
||||||
FLOAT(4, "float", GL11.GL_FLOAT),
|
|
||||||
UBYTE(1, "ubyte", GL11.GL_UNSIGNED_BYTE),
|
|
||||||
BYTE(1, "byte", GL11.GL_BYTE),
|
|
||||||
USHORT(2, "ushort", GL11.GL_UNSIGNED_SHORT),
|
|
||||||
SHORT(2, "short", GL11.GL_SHORT),
|
|
||||||
UINT(4, "uint", GL11.GL_UNSIGNED_INT),
|
|
||||||
INT(4, "int", GL11.GL_INT),
|
|
||||||
;
|
|
||||||
|
|
||||||
private static final GlNumericType[] VALUES = values();
|
|
||||||
private static final Map<String, GlNumericType> NAME_LOOKUP = Arrays.stream(VALUES)
|
|
||||||
.collect(Collectors.toMap(GlNumericType::getDisplayName, type -> type));
|
|
||||||
|
|
||||||
private final int byteWidth;
|
|
||||||
private final String displayName;
|
|
||||||
private final int glEnum;
|
|
||||||
|
|
||||||
GlNumericType(int bytes, String name, int glEnum) {
|
|
||||||
this.byteWidth = bytes;
|
|
||||||
this.displayName = name;
|
|
||||||
this.glEnum = glEnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getByteWidth() {
|
|
||||||
return this.byteWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return this.displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGlEnum() {
|
|
||||||
return this.glEnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void castAndBuffer(ByteBuffer buf, int val) {
|
|
||||||
if (this == UBYTE || this == BYTE) {
|
|
||||||
buf.put((byte) val);
|
|
||||||
} else if (this == USHORT || this == SHORT) {
|
|
||||||
buf.putShort((short) val);
|
|
||||||
} else if (this == UINT || this == INT) {
|
|
||||||
buf.putInt(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static GlNumericType byName(String name) {
|
|
||||||
return name == null ? null : NAME_LOOKUP.get(name.toLowerCase(Locale.ROOT));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl;
|
|
||||||
|
|
||||||
// Utility class for safely dealing with gl object handles.
|
|
||||||
public abstract class GlObject {
|
|
||||||
private static final int INVALID_HANDLE = Integer.MIN_VALUE;
|
|
||||||
|
|
||||||
private int handle = INVALID_HANDLE;
|
|
||||||
|
|
||||||
protected final void setHandle(int handle) {
|
|
||||||
this.handle = handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final int handle() {
|
|
||||||
this.checkHandle();
|
|
||||||
|
|
||||||
return this.handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void checkHandle() {
|
|
||||||
if (!this.isHandleValid()) {
|
|
||||||
throw new IllegalStateException("Handle is not valid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final boolean isHandleValid() {
|
|
||||||
return this.handle != INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void invalidateHandle() {
|
|
||||||
this.handle = INVALID_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (!isHandleValid()) {
|
|
||||||
throw new IllegalStateException("Handle already deleted.");
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteInternal(handle);
|
|
||||||
invalidateHandle();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void deleteInternal(int handle);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL11;
|
|
||||||
|
|
||||||
public enum GlPrimitive {
|
|
||||||
POINTS(GL11.GL_POINTS),
|
|
||||||
LINES(GL11.GL_LINES),
|
|
||||||
LINE_LOOP(GL11.GL_LINE_LOOP),
|
|
||||||
LINE_STRIP(GL11.GL_LINE_STRIP),
|
|
||||||
TRIANGLES(GL11.GL_TRIANGLES),
|
|
||||||
TRIANGLE_STRIP(GL11.GL_TRIANGLE_STRIP),
|
|
||||||
TRIANGLE_FAN(GL11.GL_TRIANGLE_FAN),
|
|
||||||
QUADS(GL11.GL_QUADS),
|
|
||||||
QUAD_STRIP(GL11.GL_QUAD_STRIP),
|
|
||||||
POLYGON(GL11.GL_POLYGON),
|
|
||||||
;
|
|
||||||
|
|
||||||
public final int glEnum;
|
|
||||||
|
|
||||||
GlPrimitive(int glEnum) {
|
|
||||||
this.glEnum = glEnum;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
public class GlTexture extends GlObject {
|
|
||||||
private final int textureType;
|
|
||||||
|
|
||||||
public GlTexture(int textureType) {
|
|
||||||
this.textureType = textureType;
|
|
||||||
setHandle(GL20.glGenTextures());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void deleteInternal(int handle) {
|
|
||||||
GL20.glDeleteTextures(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind() {
|
|
||||||
GL20.glBindTexture(textureType, handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unbind() {
|
|
||||||
GL20.glBindTexture(textureType, 0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
|
|
||||||
public class GlVertexArray extends GlObject {
|
|
||||||
public GlVertexArray() {
|
|
||||||
setHandle(Backend.getInstance().compat.vao.genVertexArrays());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind() {
|
|
||||||
Backend.getInstance().compat.vao.bindVertexArray(handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unbind() {
|
|
||||||
Backend.getInstance().compat.vao.bindVertexArray(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void deleteInternal(int handle) {
|
|
||||||
Backend.getInstance().compat.vao.deleteVertexArrays(handle);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.attrib;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
|
|
||||||
public class CommonAttributes {
|
|
||||||
|
|
||||||
public static final VertexAttribSpec VEC4 = new VertexAttribSpec(GlNumericType.FLOAT, 4);
|
|
||||||
public static final VertexAttribSpec VEC3 = new VertexAttribSpec(GlNumericType.FLOAT, 3);
|
|
||||||
public static final VertexAttribSpec VEC2 = new VertexAttribSpec(GlNumericType.FLOAT, 2);
|
|
||||||
public static final VertexAttribSpec FLOAT = new VertexAttribSpec(GlNumericType.FLOAT, 1);
|
|
||||||
|
|
||||||
public static final VertexAttribSpec QUATERNION = new VertexAttribSpec(GlNumericType.FLOAT, 4);
|
|
||||||
public static final VertexAttribSpec NORMAL = new VertexAttribSpec(GlNumericType.BYTE, 3, true);
|
|
||||||
public static final VertexAttribSpec UV = new VertexAttribSpec(GlNumericType.FLOAT, 2);
|
|
||||||
|
|
||||||
public static final VertexAttribSpec RGBA = new VertexAttribSpec(GlNumericType.UBYTE, 4, true);
|
|
||||||
public static final VertexAttribSpec RGB = new VertexAttribSpec(GlNumericType.UBYTE, 3, true);
|
|
||||||
public static final VertexAttribSpec LIGHT = new VertexAttribSpec(GlNumericType.UBYTE, 2, true);
|
|
||||||
|
|
||||||
public static final VertexAttribSpec NORMALIZED_BYTE = new VertexAttribSpec(GlNumericType.BYTE, 1, true);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.attrib;
|
|
||||||
|
|
||||||
public interface IAttribSpec {
|
|
||||||
|
|
||||||
void vertexAttribPointer(int stride, int index, int pointer);
|
|
||||||
|
|
||||||
int getSize();
|
|
||||||
|
|
||||||
int getAttributeCount();
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.attrib;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
|
|
||||||
public enum MatrixAttributes implements IAttribSpec {
|
|
||||||
MAT3(3, 3),
|
|
||||||
MAT4(4, 4),
|
|
||||||
;
|
|
||||||
|
|
||||||
private final int rows;
|
|
||||||
private final int cols;
|
|
||||||
|
|
||||||
MatrixAttributes(int rows, int cols) {
|
|
||||||
this.rows = rows;
|
|
||||||
this.cols = cols;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void vertexAttribPointer(int stride, int index, int pointer) {
|
|
||||||
for (int i = 0; i < rows; i++) {
|
|
||||||
long attribPointer = pointer + (long) i * cols * GlNumericType.FLOAT.getByteWidth();
|
|
||||||
GL20.glVertexAttribPointer(index + i, cols, GlNumericType.FLOAT.getGlEnum(), false, stride, attribPointer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return GlNumericType.FLOAT.getByteWidth() * rows * cols;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAttributeCount() {
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.attrib;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
|
|
||||||
public class VertexAttribSpec implements IAttribSpec {
|
|
||||||
|
|
||||||
private final GlNumericType type;
|
|
||||||
private final int count;
|
|
||||||
private final int size;
|
|
||||||
private final int attributeCount;
|
|
||||||
private final boolean normalized;
|
|
||||||
|
|
||||||
public VertexAttribSpec(GlNumericType type, int count) {
|
|
||||||
this(type, count, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexAttribSpec(GlNumericType type, int count, boolean normalized) {
|
|
||||||
this.type = type;
|
|
||||||
this.count = count;
|
|
||||||
this.size = type.getByteWidth() * count;
|
|
||||||
this.attributeCount = (this.size + 15) / 16; // ceiling division. GLSL vertex attributes can only be 16 bytes wide
|
|
||||||
this.normalized = normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void vertexAttribPointer(int stride, int index, int pointer) {
|
|
||||||
GL20.glVertexAttribPointer(index, count, type.getGlEnum(), normalized, stride, pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getAttributeCount() {
|
|
||||||
return attributeCount;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.attrib;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
public class VertexFormat {
|
|
||||||
|
|
||||||
private final ArrayList<IAttribSpec> allAttributes;
|
|
||||||
|
|
||||||
private final int numAttributes;
|
|
||||||
private final int stride;
|
|
||||||
|
|
||||||
public VertexFormat(ArrayList<IAttribSpec> allAttributes) {
|
|
||||||
this.allAttributes = allAttributes;
|
|
||||||
|
|
||||||
int numAttributes = 0, stride = 0;
|
|
||||||
for (IAttribSpec spec : allAttributes) {
|
|
||||||
numAttributes += spec.getAttributeCount();
|
|
||||||
stride += spec.getSize();
|
|
||||||
}
|
|
||||||
this.numAttributes = numAttributes;
|
|
||||||
this.stride = stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAttributeCount() {
|
|
||||||
return numAttributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getStride() {
|
|
||||||
return stride;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void vertexAttribPointers(int index) {
|
|
||||||
int offset = 0;
|
|
||||||
for (IAttribSpec spec : this.allAttributes) {
|
|
||||||
spec.vertexAttribPointer(stride, index, offset);
|
|
||||||
index += spec.getAttributeCount();
|
|
||||||
offset += spec.getSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Builder builder() {
|
|
||||||
return new Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Builder {
|
|
||||||
private final ArrayList<IAttribSpec> allAttributes = new ArrayList<>();
|
|
||||||
|
|
||||||
public Builder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder addAttributes(IAttribSpec... attributes) {
|
|
||||||
Collections.addAll(allAttributes, attributes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexFormat build() {
|
|
||||||
return new VertexFormat(allAttributes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL15;
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.MapBufferRange;
|
|
||||||
|
|
||||||
public class GlBuffer extends GlObject {
|
|
||||||
|
|
||||||
protected final GlBufferType type;
|
|
||||||
protected final GlBufferUsage usage;
|
|
||||||
|
|
||||||
public GlBuffer(GlBufferType type) {
|
|
||||||
this(type, GlBufferUsage.STATIC_DRAW);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlBuffer(GlBufferType type, GlBufferUsage usage) {
|
|
||||||
setHandle(GL20.glGenBuffers());
|
|
||||||
this.type = type;
|
|
||||||
this.usage = usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlBufferType getBufferTarget() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind() {
|
|
||||||
bind(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind(GlBufferType type) {
|
|
||||||
GL20.glBindBuffer(type.glEnum, handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unbind() {
|
|
||||||
unbind(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unbind(GlBufferType bufferType) {
|
|
||||||
GL20.glBindBuffer(bufferType.glEnum, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void alloc(int size) {
|
|
||||||
GL15.glBufferData(type.glEnum, size, usage.glEnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void upload(ByteBuffer directBuffer) {
|
|
||||||
GL15.glBufferData(type.glEnum, directBuffer, usage.glEnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer getBuffer(int offset, int length) {
|
|
||||||
if (Backend.getInstance().compat.mapBufferRange != MapBufferRange.UNSUPPORTED) {
|
|
||||||
return new MappedBufferRange(this, offset, length, GL30.GL_MAP_WRITE_BIT);
|
|
||||||
} else {
|
|
||||||
MappedFullBuffer fullBuffer = new MappedFullBuffer(this, MappedBufferUsage.WRITE_ONLY);
|
|
||||||
fullBuffer.position(offset);
|
|
||||||
return fullBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void deleteInternal(int handle) {
|
|
||||||
GL20.glDeleteBuffers(handle);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL15C;
|
|
||||||
import org.lwjgl.opengl.GL21;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
import org.lwjgl.opengl.GL31;
|
|
||||||
import org.lwjgl.opengl.GL40;
|
|
||||||
import org.lwjgl.opengl.GL42;
|
|
||||||
import org.lwjgl.opengl.GL43;
|
|
||||||
|
|
||||||
public enum GlBufferType {
|
|
||||||
ARRAY_BUFFER(GL15C.GL_ARRAY_BUFFER),
|
|
||||||
ELEMENT_ARRAY_BUFFER(GL15C.GL_ELEMENT_ARRAY_BUFFER),
|
|
||||||
PIXEL_PACK_BUFFER(GL21.GL_PIXEL_PACK_BUFFER),
|
|
||||||
PIXEL_UNPACK_BUFFER(GL21.GL_PIXEL_UNPACK_BUFFER),
|
|
||||||
TRANSFORM_FEEDBACK_BUFFER(GL30.GL_TRANSFORM_FEEDBACK_BUFFER),
|
|
||||||
UNIFORM_BUFFER(GL31.GL_UNIFORM_BUFFER),
|
|
||||||
TEXTURE_BUFFER(GL31.GL_TEXTURE_BUFFER),
|
|
||||||
COPY_READ_BUFFER(GL31.GL_COPY_READ_BUFFER),
|
|
||||||
COPY_WRITE_BUFFER(GL31.GL_COPY_WRITE_BUFFER),
|
|
||||||
DRAW_INDIRECT_BUFFER(GL40.GL_DRAW_INDIRECT_BUFFER),
|
|
||||||
ATOMIC_COUNTER_BUFFER(GL42.GL_ATOMIC_COUNTER_BUFFER),
|
|
||||||
DISPATCH_INDIRECT_BUFFER(GL43.GL_DISPATCH_INDIRECT_BUFFER),
|
|
||||||
SHADER_STORAGE_BUFFER(GL43.GL_SHADER_STORAGE_BUFFER),
|
|
||||||
;
|
|
||||||
|
|
||||||
public final int glEnum;
|
|
||||||
|
|
||||||
GlBufferType(int glEnum) {
|
|
||||||
this.glEnum = glEnum;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL15;
|
|
||||||
|
|
||||||
public enum GlBufferUsage {
|
|
||||||
STREAM_DRAW(GL15.GL_STREAM_DRAW),
|
|
||||||
STREAM_READ(GL15.GL_STREAM_READ),
|
|
||||||
STREAM_COPY(GL15.GL_STREAM_COPY),
|
|
||||||
STATIC_DRAW(GL15.GL_STATIC_DRAW),
|
|
||||||
STATIC_READ(GL15.GL_STATIC_READ),
|
|
||||||
STATIC_COPY(GL15.GL_STATIC_COPY),
|
|
||||||
DYNAMIC_DRAW(GL15.GL_DYNAMIC_DRAW),
|
|
||||||
DYNAMIC_READ(GL15.GL_DYNAMIC_READ),
|
|
||||||
DYNAMIC_COPY(GL15.GL_DYNAMIC_COPY),
|
|
||||||
;
|
|
||||||
|
|
||||||
public final int glEnum;
|
|
||||||
|
|
||||||
GlBufferUsage(int glEnum) {
|
|
||||||
this.glEnum = glEnum;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL15;
|
|
||||||
|
|
||||||
public abstract class MappedBuffer extends VecBuffer implements AutoCloseable {
|
|
||||||
|
|
||||||
protected boolean mapped;
|
|
||||||
protected final GlBuffer owner;
|
|
||||||
|
|
||||||
public MappedBuffer(GlBuffer owner) {
|
|
||||||
this.owner = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void checkAndMap();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make the changes in client memory available to the GPU.
|
|
||||||
*/
|
|
||||||
public void flush() {
|
|
||||||
if (mapped) {
|
|
||||||
GL15.glUnmapBuffer(owner.type.glEnum);
|
|
||||||
mapped = false;
|
|
||||||
setInternal(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws Exception {
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putFloatArray(float[] floats) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putFloatArray(floats);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putByteArray(byte[] bytes) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putByteArray(bytes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position this buffer relative to the 0-index in GPU memory.
|
|
||||||
*
|
|
||||||
* @return This buffer.
|
|
||||||
*/
|
|
||||||
public MappedBuffer position(int p) {
|
|
||||||
checkAndMap();
|
|
||||||
super.position(p);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putFloat(float f) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putFloat(f);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putInt(int i) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putInt(i);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer put(byte b) {
|
|
||||||
checkAndMap();
|
|
||||||
super.put(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer put(ByteBuffer b) {
|
|
||||||
checkAndMap();
|
|
||||||
super.put(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putVec4(float x, float y, float z, float w) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putVec4(x, y, z, w);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putVec3(float x, float y, float z) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putVec3(x, y, z);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putVec2(float x, float y) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putVec2(x, y);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putVec3(byte x, byte y, byte z) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putVec3(x, y, z);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappedBuffer putVec2(byte x, byte y) {
|
|
||||||
checkAndMap();
|
|
||||||
super.putVec2(x, y);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
|
|
||||||
public class MappedBufferRange extends MappedBuffer {
|
|
||||||
|
|
||||||
long offset, length;
|
|
||||||
int access;
|
|
||||||
|
|
||||||
public MappedBufferRange(GlBuffer buffer, long offset, long length, int access) {
|
|
||||||
super(buffer);
|
|
||||||
this.offset = offset;
|
|
||||||
this.length = length;
|
|
||||||
this.access = access;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MappedBuffer position(int p) {
|
|
||||||
if (p < offset || p >= offset + length) {
|
|
||||||
throw new IndexOutOfBoundsException("Index " + p + " is not mapped");
|
|
||||||
}
|
|
||||||
return super.position(p - (int) offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void checkAndMap() {
|
|
||||||
if (!mapped) {
|
|
||||||
setInternal(Backend.getInstance().compat.mapBufferRange.mapBuffer(owner.type, offset, length, access));
|
|
||||||
mapped = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL15C;
|
|
||||||
|
|
||||||
public enum MappedBufferUsage {
|
|
||||||
READ_ONLY(GL15C.GL_READ_ONLY),
|
|
||||||
WRITE_ONLY(GL15C.GL_WRITE_ONLY),
|
|
||||||
READ_WRITE(GL15C.GL_READ_WRITE),
|
|
||||||
;
|
|
||||||
|
|
||||||
int glEnum;
|
|
||||||
|
|
||||||
MappedBufferUsage(int glEnum) {
|
|
||||||
this.glEnum = glEnum;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL15;
|
|
||||||
|
|
||||||
public class MappedFullBuffer extends MappedBuffer {
|
|
||||||
|
|
||||||
MappedBufferUsage usage;
|
|
||||||
|
|
||||||
public MappedFullBuffer(GlBuffer buffer, MappedBufferUsage usage) {
|
|
||||||
super(buffer);
|
|
||||||
this.usage = usage;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void checkAndMap() {
|
|
||||||
if (!mapped) {
|
|
||||||
setInternal(GL15.glMapBuffer(owner.type.glEnum, usage.glEnum));
|
|
||||||
mapped = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.buffer;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
public class VecBuffer {
|
|
||||||
|
|
||||||
protected ByteBuffer internal;
|
|
||||||
|
|
||||||
public VecBuffer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer(ByteBuffer internal) {
|
|
||||||
this.internal = internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VecBuffer allocate(int bytes) {
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(bytes);
|
|
||||||
buffer.order(ByteOrder.nativeOrder());
|
|
||||||
return new VecBuffer(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setInternal(ByteBuffer internal) {
|
|
||||||
this.internal = internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteBuffer unwrap() {
|
|
||||||
return internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer rewind() {
|
|
||||||
internal.rewind();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putFloatArray(float[] floats) {
|
|
||||||
|
|
||||||
for (float f : floats) {
|
|
||||||
internal.putFloat(f);
|
|
||||||
}
|
|
||||||
// internal.asFloatBuffer().put(floats);
|
|
||||||
// internal.position(internal.position() + floats.length * 4);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putByteArray(byte[] bytes) {
|
|
||||||
internal.put(bytes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Position this buffer relative to the 0-index in GPU memory.
|
|
||||||
*
|
|
||||||
* @return This buffer.
|
|
||||||
*/
|
|
||||||
public VecBuffer position(int p) {
|
|
||||||
internal.position(p);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putFloat(float f) {
|
|
||||||
internal.putFloat(f);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putInt(int i) {
|
|
||||||
internal.putInt(i);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer put(byte b) {
|
|
||||||
internal.put(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer put(ByteBuffer b) {
|
|
||||||
internal.put(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putVec4(float x, float y, float z, float w) {
|
|
||||||
internal.putFloat(x);
|
|
||||||
internal.putFloat(y);
|
|
||||||
internal.putFloat(z);
|
|
||||||
internal.putFloat(w);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putVec3(float x, float y, float z) {
|
|
||||||
internal.putFloat(x);
|
|
||||||
internal.putFloat(y);
|
|
||||||
internal.putFloat(z);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putVec2(float x, float y) {
|
|
||||||
internal.putFloat(x);
|
|
||||||
internal.putFloat(y);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putVec3(byte x, byte y, byte z) {
|
|
||||||
internal.put(x);
|
|
||||||
internal.put(y);
|
|
||||||
internal.put(z);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VecBuffer putVec2(byte x, byte y) {
|
|
||||||
internal.put(x);
|
|
||||||
internal.put(y);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.shader;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL20.glDeleteProgram;
|
|
||||||
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
|
|
||||||
import static org.lwjgl.opengl.GL20.glUniform1i;
|
|
||||||
import static org.lwjgl.opengl.GL20.glUniformMatrix4fv;
|
|
||||||
import static org.lwjgl.opengl.GL20.glUseProgram;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Program;
|
|
||||||
import com.jozufozu.flywheel.util.RenderUtil;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
|
||||||
|
|
||||||
public abstract class GlProgram extends GlObject {
|
|
||||||
|
|
||||||
public final ResourceLocation name;
|
|
||||||
|
|
||||||
protected GlProgram(Program program) {
|
|
||||||
setHandle(program.program);
|
|
||||||
this.name = program.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind() {
|
|
||||||
glUseProgram(handle());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unbind() {
|
|
||||||
glUseProgram(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the index of the uniform with the given name.
|
|
||||||
*
|
|
||||||
* @param uniform The name of the uniform to find the index of
|
|
||||||
* @return The uniform's index
|
|
||||||
*/
|
|
||||||
public int getUniformLocation(String uniform) {
|
|
||||||
int index = glGetUniformLocation(this.handle(), uniform);
|
|
||||||
|
|
||||||
if (index < 0) {
|
|
||||||
Backend.log.debug("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds a sampler uniform to the given texture unit.
|
|
||||||
*
|
|
||||||
* @param name The name of the sampler uniform.
|
|
||||||
* @param binding The index of the texture unit.
|
|
||||||
* @return The sampler uniform's index.
|
|
||||||
* @throws NullPointerException If no uniform exists with the given name.
|
|
||||||
*/
|
|
||||||
public int setSamplerBinding(String name, int binding) {
|
|
||||||
int samplerUniform = getUniformLocation(name);
|
|
||||||
|
|
||||||
if (samplerUniform >= 0) {
|
|
||||||
glUniform1i(samplerUniform, binding);
|
|
||||||
}
|
|
||||||
|
|
||||||
return samplerUniform;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
|
|
||||||
glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void deleteInternal(int handle) {
|
|
||||||
glDeleteProgram(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.shader;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlObject;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlCompat;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class GlShader extends GlObject {
|
|
||||||
|
|
||||||
public final ResourceLocation name;
|
|
||||||
public final ShaderType type;
|
|
||||||
|
|
||||||
public GlShader(Shader shader) {
|
|
||||||
this(shader.type, shader.name, shader.getSource());
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlShader(ShaderType type, ResourceLocation name, String source) {
|
|
||||||
this.type = type;
|
|
||||||
this.name = name;
|
|
||||||
int handle = GL20.glCreateShader(type.glEnum);
|
|
||||||
|
|
||||||
GlCompat.safeShaderSource(handle, source);
|
|
||||||
GL20.glCompileShader(handle);
|
|
||||||
|
|
||||||
String log = GL20.glGetShaderInfoLog(handle);
|
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
|
||||||
Backend.log.error("Shader compilation log for " + name + ": " + log);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) {
|
|
||||||
throw new RuntimeException("Could not compile " + name + ". See log for details.");
|
|
||||||
}
|
|
||||||
|
|
||||||
setHandle(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void deleteInternal(int handle) {
|
|
||||||
GL20.glDeleteShader(handle);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.shader;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
public enum ShaderType {
|
|
||||||
VERTEX("vertex", GL20.GL_VERTEX_SHADER),
|
|
||||||
FRAGMENT("fragment", GL20.GL_FRAGMENT_SHADER),
|
|
||||||
;
|
|
||||||
|
|
||||||
public final String name;
|
|
||||||
public final int glEnum;
|
|
||||||
|
|
||||||
ShaderType(String name, int glEnum) {
|
|
||||||
this.name = name;
|
|
||||||
this.glEnum = glEnum;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,113 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import org.lwjgl.PointerBuffer;
|
|
||||||
import org.lwjgl.opengl.GL20C;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
import org.lwjgl.system.MemoryStack;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Blit;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.framebuffer.Framebuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.instancing.DrawInstanced;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.instancing.InstancedArrays;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.instancing.VertexArrayObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An instance of this class stores information about what OpenGL features are available.
|
|
||||||
* <br>
|
|
||||||
* Each field stores an enum variant that provides access to the most appropriate version of a feature for the current
|
|
||||||
* system.
|
|
||||||
*/
|
|
||||||
public class GlCompat {
|
|
||||||
public final MapBufferRange mapBufferRange;
|
|
||||||
|
|
||||||
public final VertexArrayObject vao;
|
|
||||||
public final InstancedArrays instancedArrays;
|
|
||||||
public final DrawInstanced drawInstanced;
|
|
||||||
public final Blit blit;
|
|
||||||
public final Framebuffer fbo;
|
|
||||||
|
|
||||||
public final RGPixelFormat pixelFormat;
|
|
||||||
|
|
||||||
public GlCompat(GLCapabilities caps) {
|
|
||||||
mapBufferRange = getLatest(MapBufferRange.class, caps);
|
|
||||||
|
|
||||||
vao = getLatest(VertexArrayObject.class, caps);
|
|
||||||
instancedArrays = getLatest(InstancedArrays.class, caps);
|
|
||||||
drawInstanced = getLatest(DrawInstanced.class, caps);
|
|
||||||
blit = getLatest(Blit.class, caps);
|
|
||||||
fbo = getLatest(Framebuffer.class, caps);
|
|
||||||
|
|
||||||
pixelFormat = getLatest(RGPixelFormat.class, caps);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean vertexArrayObjectsSupported() {
|
|
||||||
return vao != VertexArrayObject.UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean instancedArraysSupported() {
|
|
||||||
return instancedArrays != InstancedArrays.UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean drawInstancedSupported() {
|
|
||||||
return drawInstanced != DrawInstanced.UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean fbosSupported() {
|
|
||||||
return fbo != Framebuffer.UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean blitSupported() {
|
|
||||||
return blit != Blit.UNSUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order.
|
|
||||||
*
|
|
||||||
* @param clazz The class of the versioning enum.
|
|
||||||
* @param caps The current system's supported features.
|
|
||||||
* @param <V> The type of the versioning enum.
|
|
||||||
* @return The first defined enum variant to return true.
|
|
||||||
*/
|
|
||||||
public static <V extends Enum<V> & GlVersioned> V getLatest(Class<V> clazz, GLCapabilities caps) {
|
|
||||||
V[] constants = clazz.getEnumConstants();
|
|
||||||
V last = constants[constants.length - 1];
|
|
||||||
if (!last.supported(caps)) {
|
|
||||||
throw new IllegalStateException("");
|
|
||||||
}
|
|
||||||
|
|
||||||
return Arrays.stream(constants).filter(it -> it.supported(caps)).findFirst().get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copied from:
|
|
||||||
* <br> https://github.com/grondag/canvas/commit/820bf754092ccaf8d0c169620c2ff575722d7d96
|
|
||||||
*
|
|
||||||
* <p>Identical in function to {@link GL20C#glShaderSource(int, CharSequence)} but
|
|
||||||
* passes a null pointer for string length to force the driver to rely on the null
|
|
||||||
* terminator for string length. This is a workaround for an apparent flaw with some
|
|
||||||
* AMD drivers that don't receive or interpret the length correctly, resulting in
|
|
||||||
* an access violation when the driver tries to read past the string memory.
|
|
||||||
*
|
|
||||||
* <p>Hat tip to fewizz for the find and the fix.
|
|
||||||
*/
|
|
||||||
public static void safeShaderSource(int glId, CharSequence source) {
|
|
||||||
final MemoryStack stack = MemoryStack.stackGet();
|
|
||||||
final int stackPointer = stack.getPointer();
|
|
||||||
|
|
||||||
try {
|
|
||||||
final ByteBuffer sourceBuffer = MemoryUtil.memUTF8(source, true);
|
|
||||||
final PointerBuffer pointers = stack.mallocPointer(1);
|
|
||||||
pointers.put(sourceBuffer);
|
|
||||||
|
|
||||||
GL20C.nglShaderSource(glId, 1, pointers.address0(), 0);
|
|
||||||
org.lwjgl.system.APIUtil.apiArrayFree(pointers.address0(), 1);
|
|
||||||
} finally {
|
|
||||||
stack.setPointer(stackPointer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface should be implemented by enums such that the
|
|
||||||
* last defined variant <em>always</em> returns <code>true</code>.
|
|
||||||
*/
|
|
||||||
public interface GlVersioned {
|
|
||||||
/**
|
|
||||||
* Queries whether this variant is supported by the current system.
|
|
||||||
*
|
|
||||||
* @param caps The {@link GLCapabilities} reported by the current system.
|
|
||||||
* @return <code>true</code> if this variant is supported, or if this is the last defined variant.
|
|
||||||
*/
|
|
||||||
boolean supported(GLCapabilities caps);
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.ARBMapBufferRange;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
|
||||||
|
|
||||||
public enum MapBufferRange implements GlVersioned {
|
|
||||||
|
|
||||||
GL30_RANGE {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL30;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access) {
|
|
||||||
return GL30.glMapBufferRange(target.glEnum, offset, length, access);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ARB_RANGE {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_ARB_map_buffer_range;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access) {
|
|
||||||
return ARBMapBufferRange.glMapBufferRange(target.glEnum, offset, length, access);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UNSUPPORTED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access) {
|
|
||||||
throw new UnsupportedOperationException("glMapBuffer not supported");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public abstract ByteBuffer mapBuffer(GlBufferType target, long offset, long length, int access);
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL11;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
public enum RGPixelFormat implements GlVersioned {
|
|
||||||
GL30_RG {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL30;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int internalFormat() {
|
|
||||||
return GL30.GL_RG8;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int format() {
|
|
||||||
return GL30.GL_RG;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int byteCount() {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
GL11_RGB {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL11;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int internalFormat() {
|
|
||||||
return GL11.GL_RGB8;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int format() {
|
|
||||||
return GL11.GL_RGB;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int byteCount() {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UNSUPPORTED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int internalFormat() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int format() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int byteCount() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public abstract int internalFormat();
|
|
||||||
|
|
||||||
public abstract int format();
|
|
||||||
|
|
||||||
public abstract int byteCount();
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned.framebuffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.EXTFramebufferBlit;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
|
|
||||||
|
|
||||||
public enum Blit implements GlVersioned {
|
|
||||||
CORE {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL30;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
|
||||||
GL30.glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
EXT {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_EXT_framebuffer_blit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
|
||||||
EXTFramebufferBlit.glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UNSUPPORTED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) {
|
|
||||||
throw new UnsupportedOperationException("Framebuffer blitting not supported.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public abstract void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter);
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned.framebuffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.ARBFramebufferObject;
|
|
||||||
import org.lwjgl.opengl.EXTFramebufferObject;
|
|
||||||
import org.lwjgl.opengl.GL30C;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
|
|
||||||
|
|
||||||
public enum Framebuffer implements GlVersioned {
|
|
||||||
CORE {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL30;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindFramebuffer(int target, int framebuffer) {
|
|
||||||
GL30C.glBindFramebuffer(target, framebuffer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ARB {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_ARB_framebuffer_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindFramebuffer(int target, int framebuffer) {
|
|
||||||
ARBFramebufferObject.glBindFramebuffer(target, framebuffer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
EXT {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_EXT_framebuffer_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindFramebuffer(int target, int framebuffer) {
|
|
||||||
EXTFramebufferObject.glBindFramebufferEXT(target, framebuffer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UNSUPPORTED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindFramebuffer(int target, int framebuffer) {
|
|
||||||
throw new UnsupportedOperationException("Framebuffers not supported");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public abstract void bindFramebuffer(int target, int framebuffer);
|
|
||||||
}
|
|
|
@ -1,76 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.ARBDrawInstanced;
|
|
||||||
import org.lwjgl.opengl.EXTDrawInstanced;
|
|
||||||
import org.lwjgl.opengl.GL31;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
|
|
||||||
|
|
||||||
public enum DrawInstanced implements GlVersioned {
|
|
||||||
GL31_DRAW_INSTANCED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL31;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
|
|
||||||
GL31.glDrawArraysInstanced(mode.glEnum, first, count, primcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
|
|
||||||
GL31.glDrawElementsInstanced(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ARB_DRAW_INSTANCED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_ARB_draw_instanced;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
|
|
||||||
ARBDrawInstanced.glDrawArraysInstancedARB(mode.glEnum, first, count, primcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
|
|
||||||
ARBDrawInstanced.glDrawElementsInstancedARB(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
EXT_DRAW_INSTANCED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_EXT_draw_instanced;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
|
|
||||||
EXTDrawInstanced.glDrawArraysInstancedEXT(mode.glEnum, first, count, primcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
|
|
||||||
EXTDrawInstanced.glDrawElementsInstancedEXT(mode.glEnum, elementCount, type.getGlEnum(), indices, primcount);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UNSUPPORTED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public void drawArraysInstanced(GlPrimitive mode, int first, int count, int primcount) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawElementsInstanced(GlPrimitive mode, int elementCount, GlNumericType type, long indices, int primcount) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.ARBInstancedArrays;
|
|
||||||
import org.lwjgl.opengl.GL33;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
|
|
||||||
|
|
||||||
public enum InstancedArrays implements GlVersioned {
|
|
||||||
GL33_INSTANCED_ARRAYS {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL33;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
|
||||||
GL33.glVertexAttribDivisor(index, divisor);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ARB_INSTANCED_ARRAYS {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_ARB_instanced_arrays;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
|
||||||
ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UNSUPPORTED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void vertexAttribDivisor(int index, int divisor) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public abstract void vertexAttribDivisor(int index, int divisor);
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.gl.versioned.instancing;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.ARBVertexArrayObject;
|
|
||||||
import org.lwjgl.opengl.GL30;
|
|
||||||
import org.lwjgl.opengl.GLCapabilities;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.versioned.GlVersioned;
|
|
||||||
|
|
||||||
public enum VertexArrayObject implements GlVersioned {
|
|
||||||
GL30_VAO {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.OpenGL30;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int genVertexArrays() {
|
|
||||||
return GL30.glGenVertexArrays();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindVertexArray(int array) {
|
|
||||||
GL30.glBindVertexArray(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteVertexArrays(int array) {
|
|
||||||
GL30.glDeleteVertexArrays(array);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ARB_VAO {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return caps.GL_ARB_vertex_array_object;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int genVertexArrays() {
|
|
||||||
return ARBVertexArrayObject.glGenVertexArrays();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindVertexArray(int array) {
|
|
||||||
ARBVertexArrayObject.glBindVertexArray(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteVertexArrays(int array) {
|
|
||||||
ARBVertexArrayObject.glDeleteVertexArrays(array);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UNSUPPORTED {
|
|
||||||
@Override
|
|
||||||
public boolean supported(GLCapabilities caps) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int genVertexArrays() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindVertexArray(int array) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteVertexArrays(int array) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public abstract int genVertexArrays();
|
|
||||||
|
|
||||||
public abstract void bindVertexArray(int array);
|
|
||||||
|
|
||||||
public abstract void deleteVertexArrays(int array);
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface giving {@link TileEntityInstance}s a hook to have a function called at
|
|
||||||
* the start of a frame. By implementing {@link IDynamicInstance}, a {@link TileEntityInstance}
|
|
||||||
* can animate its models in ways that could not be easily achieved by shader attribute
|
|
||||||
* parameterization.
|
|
||||||
*
|
|
||||||
* <br><br> If your goal is offloading work to shaders, but you're unsure exactly how you need
|
|
||||||
* to parameterize the instances, you're encouraged to implement this for prototyping.
|
|
||||||
*/
|
|
||||||
public interface IDynamicInstance extends IInstance {
|
|
||||||
/**
|
|
||||||
* Called every frame.
|
|
||||||
*/
|
|
||||||
void beginFrame();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* As a further optimization, dynamic instances that are far away are ticked less often.
|
|
||||||
* This behavior can be disabled by returning false.
|
|
||||||
*
|
|
||||||
* <br> You might want to opt out of this if you want your animations to remain smooth
|
|
||||||
* even when far away from the camera. It is recommended to keep this as is, however.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if your instance should be slow ticked.
|
|
||||||
*/
|
|
||||||
default boolean decreaseFramerateWithDistance() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
|
||||||
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
|
|
||||||
* Right now, that's only {@link TileInstanceManager}, but there could be an entity equivalent in the future.
|
|
||||||
*/
|
|
||||||
public interface IInstance {
|
|
||||||
|
|
||||||
BlockPos getWorldPosition();
|
|
||||||
|
|
||||||
void updateLight();
|
|
||||||
|
|
||||||
void remove();
|
|
||||||
|
|
||||||
boolean shouldReset();
|
|
||||||
|
|
||||||
void update();
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
public interface IInstanceFactory<D extends InstanceData> {
|
|
||||||
D create(Instancer<? super D> owner);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Something (a TileEntity or Entity) that can be rendered using the instancing API.
|
|
||||||
*/
|
|
||||||
public interface IInstanceRendered {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if there are parts of the renderer that cannot be implemented with Flywheel.
|
|
||||||
*/
|
|
||||||
default boolean shouldRenderNormally() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
World getWorld();
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface giving {@link TileEntityInstance}s a hook to have a function called at
|
|
||||||
* the end of every tick. By implementing {@link ITickableInstance}, a {@link TileEntityInstance}
|
|
||||||
* can update frequently, but not every frame.
|
|
||||||
* <br> There are a few cases in which this should be considered over {@link IDynamicInstance}:
|
|
||||||
* <ul>
|
|
||||||
* <li>
|
|
||||||
* You'd like to change something about the instance every now and then.
|
|
||||||
* eg. adding or removing parts, snapping to a different rotation, etc.
|
|
||||||
* </li>
|
|
||||||
* <li>
|
|
||||||
* Your TileEntity does animate, but the animation doesn't have
|
|
||||||
* to be smooth, in which case this could be an optimization.
|
|
||||||
* </li>
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
public interface ITickableInstance extends IInstance {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called every tick.
|
|
||||||
*/
|
|
||||||
void tick();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* As a further optimization, tickable instances that are far away are ticked less often.
|
|
||||||
* This behavior can be disabled by returning false.
|
|
||||||
*
|
|
||||||
* <br> You might want to opt out of this if you want your animations to remain smooth
|
|
||||||
* even when far away from the camera. It is recommended to keep this as is, however.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if your instance should be slow ticked.
|
|
||||||
*/
|
|
||||||
default boolean decreaseTickRateWithDistance() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
|
||||||
|
|
||||||
public abstract class InstanceData {
|
|
||||||
|
|
||||||
protected final Instancer<?> owner;
|
|
||||||
|
|
||||||
boolean dirty;
|
|
||||||
boolean removed;
|
|
||||||
|
|
||||||
protected InstanceData(Instancer<?> owner) {
|
|
||||||
this.owner = owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void write(MappedBuffer buf);
|
|
||||||
|
|
||||||
public void markDirty() {
|
|
||||||
owner.anyToUpdate = true;
|
|
||||||
dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
owner.anyToRemove = true;
|
|
||||||
removed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,245 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
||||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.vector.Vector3f;
|
|
||||||
|
|
||||||
public abstract class InstanceManager<T> implements MaterialManager.OriginShiftListener {
|
|
||||||
|
|
||||||
public final MaterialManager<?> materialManager;
|
|
||||||
|
|
||||||
protected final ArrayList<T> queuedAdditions;
|
|
||||||
protected final ConcurrentHashMap.KeySetView<T, Boolean> queuedUpdates;
|
|
||||||
|
|
||||||
protected final Map<T, IInstance> instances;
|
|
||||||
protected final Object2ObjectOpenHashMap<T, ITickableInstance> tickableInstances;
|
|
||||||
protected final Object2ObjectOpenHashMap<T, IDynamicInstance> dynamicInstances;
|
|
||||||
|
|
||||||
protected int frame;
|
|
||||||
protected int tick;
|
|
||||||
|
|
||||||
public InstanceManager(MaterialManager<?> materialManager) {
|
|
||||||
this.materialManager = materialManager;
|
|
||||||
this.queuedUpdates = ConcurrentHashMap.newKeySet(64);
|
|
||||||
this.queuedAdditions = new ArrayList<>(64);
|
|
||||||
this.instances = new HashMap<>();
|
|
||||||
|
|
||||||
this.dynamicInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
this.tickableInstances = new Object2ObjectOpenHashMap<>();
|
|
||||||
|
|
||||||
materialManager.onOriginShift(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tick(double cameraX, double cameraY, double cameraZ) {
|
|
||||||
tick++;
|
|
||||||
|
|
||||||
// integer camera pos
|
|
||||||
int cX = (int) cameraX;
|
|
||||||
int cY = (int) cameraY;
|
|
||||||
int cZ = (int) cameraZ;
|
|
||||||
|
|
||||||
if (tickableInstances.size() > 0) {
|
|
||||||
for (ITickableInstance instance : tickableInstances.values()) {
|
|
||||||
if (!instance.decreaseTickRateWithDistance()) {
|
|
||||||
instance.tick();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockPos pos = instance.getWorldPosition();
|
|
||||||
|
|
||||||
int dX = pos.getX() - cX;
|
|
||||||
int dY = pos.getY() - cY;
|
|
||||||
int dZ = pos.getZ() - cZ;
|
|
||||||
|
|
||||||
if ((tick % getUpdateDivisor(dX, dY, dZ)) == 0)
|
|
||||||
instance.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
queuedUpdates.forEach(te -> {
|
|
||||||
queuedUpdates.remove(te);
|
|
||||||
|
|
||||||
update(te);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beginFrame(ActiveRenderInfo info) {
|
|
||||||
frame++;
|
|
||||||
processQueuedAdditions();
|
|
||||||
|
|
||||||
Vector3f look = info.getHorizontalPlane();
|
|
||||||
float lookX = look.getX();
|
|
||||||
float lookY = look.getY();
|
|
||||||
float lookZ = look.getZ();
|
|
||||||
|
|
||||||
// integer camera pos
|
|
||||||
int cX = (int) info.getProjectedView().x;
|
|
||||||
int cY = (int) info.getProjectedView().y;
|
|
||||||
int cZ = (int) info.getProjectedView().z;
|
|
||||||
|
|
||||||
if (dynamicInstances.size() > 0) {
|
|
||||||
dynamicInstances.object2ObjectEntrySet().fastForEach(e -> {
|
|
||||||
IDynamicInstance dyn = e.getValue();
|
|
||||||
if (!dyn.decreaseFramerateWithDistance() || shouldFrameUpdate(dyn.getWorldPosition(), lookX, lookY, lookZ, cX, cY, cZ))
|
|
||||||
dyn.beginFrame();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(T obj) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (obj instanceof IInstanceRendered) {
|
|
||||||
addInternal(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void queueAdd(T obj) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
queuedAdditions.add(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(T obj) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (obj instanceof IInstanceRendered) {
|
|
||||||
IInstance instance = getInstance(obj, false);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
|
|
||||||
if (instance.shouldReset()) {
|
|
||||||
removeInternal(obj, instance);
|
|
||||||
|
|
||||||
createInternal(obj);
|
|
||||||
} else {
|
|
||||||
instance.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void queueUpdate(T obj) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
queuedUpdates.add(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onLightUpdate(T obj) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (obj instanceof IInstanceRendered) {
|
|
||||||
IInstance instance = getInstance(obj, false);
|
|
||||||
|
|
||||||
if (instance != null)
|
|
||||||
instance.updateLight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(T obj) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return;
|
|
||||||
|
|
||||||
if (obj instanceof IInstanceRendered) {
|
|
||||||
IInstance instance = getInstance(obj, false);
|
|
||||||
if (instance != null)
|
|
||||||
removeInternal(obj, instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidate() {
|
|
||||||
instances.clear();
|
|
||||||
dynamicInstances.clear();
|
|
||||||
tickableInstances.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
|
||||||
protected <I extends T> IInstance getInstance(I obj, boolean create) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing()) return null;
|
|
||||||
|
|
||||||
IInstance instance = instances.get(obj);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
return instance;
|
|
||||||
} else if (create && canCreateInstance(obj)) {
|
|
||||||
return createInternal(obj);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected synchronized void processQueuedAdditions() {
|
|
||||||
if (queuedAdditions.size() > 0) {
|
|
||||||
queuedAdditions.forEach(this::addInternal);
|
|
||||||
queuedAdditions.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean shouldFrameUpdate(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
|
|
||||||
int dX = worldPos.getX() - cX;
|
|
||||||
int dY = worldPos.getY() - cY;
|
|
||||||
int dZ = worldPos.getZ() - cZ;
|
|
||||||
|
|
||||||
// is it more than 2 blocks behind the camera?
|
|
||||||
int dist = 2;
|
|
||||||
float dot = (dX + lookX * dist) * lookX + (dY + lookY * dist) * lookY + (dZ + lookZ * dist) * lookZ;
|
|
||||||
if (dot < 0) return false;
|
|
||||||
|
|
||||||
return (frame % getUpdateDivisor(dX, dY, dZ)) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getUpdateDivisor(int dX, int dY, int dZ) {
|
|
||||||
int dSq = dX * dX + dY * dY + dZ * dZ;
|
|
||||||
|
|
||||||
return (dSq / 1024) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addInternal(T tile) {
|
|
||||||
getInstance(tile, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void removeInternal(T obj, IInstance instance) {
|
|
||||||
instance.remove();
|
|
||||||
instances.remove(obj);
|
|
||||||
dynamicInstances.remove(obj);
|
|
||||||
tickableInstances.remove(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IInstance createInternal(T obj) {
|
|
||||||
IInstance renderer = createRaw(obj);
|
|
||||||
|
|
||||||
if (renderer != null) {
|
|
||||||
renderer.updateLight();
|
|
||||||
instances.put(obj, renderer);
|
|
||||||
|
|
||||||
if (renderer instanceof IDynamicInstance)
|
|
||||||
dynamicInstances.put(obj, (IDynamicInstance) renderer);
|
|
||||||
|
|
||||||
if (renderer instanceof ITickableInstance)
|
|
||||||
tickableInstances.put(obj, ((ITickableInstance) renderer));
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onOriginShift() {
|
|
||||||
ArrayList<T> instancedTiles = new ArrayList<>(instances.keySet());
|
|
||||||
invalidate();
|
|
||||||
instancedTiles.forEach(this::add);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract IInstance createRaw(T obj);
|
|
||||||
|
|
||||||
protected abstract boolean canCreateInstance(T entity);
|
|
||||||
}
|
|
|
@ -1,170 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
|
||||||
import org.lwjgl.opengl.GL11;
|
|
||||||
|
|
||||||
import com.google.common.cache.Cache;
|
|
||||||
import com.google.common.cache.CacheBuilder;
|
|
||||||
import com.jozufozu.flywheel.backend.RenderWork;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|
||||||
import com.jozufozu.flywheel.backend.model.BufferedModel;
|
|
||||||
import com.jozufozu.flywheel.backend.model.IndexedModel;
|
|
||||||
import com.jozufozu.flywheel.core.PartialModel;
|
|
||||||
import com.jozufozu.flywheel.util.BufferBuilderReader;
|
|
||||||
import com.jozufozu.flywheel.util.RenderUtil;
|
|
||||||
import com.jozufozu.flywheel.util.VirtualEmptyModelData;
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.renderer.BlockModelRenderer;
|
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
|
||||||
import net.minecraft.client.renderer.BufferBuilder;
|
|
||||||
import net.minecraft.client.renderer.model.IBakedModel;
|
|
||||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
|
||||||
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
|
|
||||||
import net.minecraft.util.Direction;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.vector.Vector3i;
|
|
||||||
|
|
||||||
public class InstanceMaterial<D extends InstanceData> {
|
|
||||||
|
|
||||||
protected final Supplier<Vector3i> originCoordinate;
|
|
||||||
protected final Cache<Object, Instancer<D>> models;
|
|
||||||
protected final MaterialSpec<D> spec;
|
|
||||||
private final VertexFormat modelFormat;
|
|
||||||
|
|
||||||
public InstanceMaterial(Supplier<Vector3i> renderer, MaterialSpec<D> spec) {
|
|
||||||
this.originCoordinate = renderer;
|
|
||||||
this.spec = spec;
|
|
||||||
|
|
||||||
this.models = CacheBuilder.newBuilder()
|
|
||||||
.removalListener(notification -> {
|
|
||||||
Instancer<?> instancer = (Instancer<?>) notification.getValue();
|
|
||||||
RenderWork.enqueue(instancer::delete);
|
|
||||||
})
|
|
||||||
.build();
|
|
||||||
modelFormat = this.spec.getModelFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean nothingToRender() {
|
|
||||||
return models.size() > 0 && models.asMap().values().stream().allMatch(Instancer::empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
models.invalidateAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all instance data without freeing resources.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
models.asMap().values().forEach(Instancer::clear);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void forEachInstancer(Consumer<Instancer<D>> f) {
|
|
||||||
for (Instancer<D> model : models.asMap().values()) {
|
|
||||||
f.accept(model);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState) {
|
|
||||||
return get(partial, () -> buildModel(partial.get(), referenceState));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir) {
|
|
||||||
return getModel(partial, referenceState, dir, RenderUtil.rotateToFace(dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instancer<D> getModel(PartialModel partial, BlockState referenceState, Direction dir, Supplier<MatrixStack> modelTransform) {
|
|
||||||
return get(Pair.of(dir, partial),
|
|
||||||
() -> buildModel(partial.get(), referenceState, modelTransform.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instancer<D> getModel(BlockState toRender) {
|
|
||||||
return get(toRender, () -> buildModel(toRender));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Instancer<D> get(Object key, Supplier<BufferedModel> supplier) {
|
|
||||||
try {
|
|
||||||
return models.get(key, () -> new Instancer<>(supplier.get(), originCoordinate, spec));
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BufferedModel buildModel(BlockState renderedState) {
|
|
||||||
BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher();
|
|
||||||
return buildModel(dispatcher.getModelForState(renderedState), renderedState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private BufferedModel buildModel(IBakedModel model, BlockState renderedState) {
|
|
||||||
return buildModel(model, renderedState, new MatrixStack());
|
|
||||||
}
|
|
||||||
|
|
||||||
private BufferedModel buildModel(IBakedModel model, BlockState referenceState, MatrixStack ms) {
|
|
||||||
BufferBuilderReader reader = new BufferBuilderReader(getBufferBuilder(model, referenceState, ms));
|
|
||||||
|
|
||||||
int vertexCount = reader.getVertexCount();
|
|
||||||
|
|
||||||
ByteBuffer vertices = ByteBuffer.allocate(vertexCount * modelFormat.getStride());
|
|
||||||
vertices.order(ByteOrder.nativeOrder());
|
|
||||||
|
|
||||||
for (int i = 0; i < vertexCount; i++) {
|
|
||||||
vertices.putFloat(reader.getX(i));
|
|
||||||
vertices.putFloat(reader.getY(i));
|
|
||||||
vertices.putFloat(reader.getZ(i));
|
|
||||||
|
|
||||||
vertices.put(reader.getNX(i));
|
|
||||||
vertices.put(reader.getNY(i));
|
|
||||||
vertices.put(reader.getNZ(i));
|
|
||||||
|
|
||||||
vertices.putFloat(reader.getU(i));
|
|
||||||
vertices.putFloat(reader.getV(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices.rewind();
|
|
||||||
|
|
||||||
// return new BufferedModel(GlPrimitive.QUADS, format, vertices, vertexCount);
|
|
||||||
|
|
||||||
return IndexedModel.fromSequentialQuads(modelFormat, vertices, vertexCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DOWN, UP, NORTH, SOUTH, WEST, EAST, null
|
|
||||||
private static final Direction[] dirs;
|
|
||||||
|
|
||||||
static {
|
|
||||||
Direction[] directions = Direction.values();
|
|
||||||
|
|
||||||
dirs = Arrays.copyOf(directions, directions.length + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BufferBuilder getBufferBuilder(IBakedModel model, BlockState referenceState, MatrixStack ms) {
|
|
||||||
Minecraft mc = Minecraft.getInstance();
|
|
||||||
BlockRendererDispatcher dispatcher = mc.getBlockRendererDispatcher();
|
|
||||||
BlockModelRenderer blockRenderer = dispatcher.getBlockModelRenderer();
|
|
||||||
BufferBuilder builder = new BufferBuilder(512);
|
|
||||||
|
|
||||||
// BakedQuadWrapper quadReader = new BakedQuadWrapper();
|
|
||||||
//
|
|
||||||
// IModelData modelData = model.getModelData(mc.world, BlockPos.ZERO.up(255), referenceState, VirtualEmptyModelData.INSTANCE);
|
|
||||||
// List<BakedQuad> quads = Arrays.stream(dirs)
|
|
||||||
// .flatMap(dir -> model.getQuads(referenceState, dir, mc.world.rand, modelData).stream())
|
|
||||||
// .collect(Collectors.toList());
|
|
||||||
|
|
||||||
builder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
|
|
||||||
blockRenderer.renderModel(mc.world, model, referenceState, BlockPos.ZERO.up(255), ms, builder, true,
|
|
||||||
mc.world.rand, 42, OverlayTexture.DEFAULT_UV, VirtualEmptyModelData.INSTANCE);
|
|
||||||
builder.finishDrawing();
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
|
|
||||||
import static org.lwjgl.opengl.GL11.glBindTexture;
|
|
||||||
import static org.lwjgl.opengl.GL13.GL_TEXTURE0;
|
|
||||||
import static org.lwjgl.opengl.GL13.GL_TEXTURE4;
|
|
||||||
import static org.lwjgl.opengl.GL13.glActiveTexture;
|
|
||||||
|
|
||||||
import java.util.BitSet;
|
|
||||||
import java.util.SortedSet;
|
|
||||||
import java.util.Vector;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstanceManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
|
||||||
import com.jozufozu.flywheel.core.Contexts;
|
|
||||||
import com.jozufozu.flywheel.core.CrumblingInstanceManager;
|
|
||||||
import com.jozufozu.flywheel.event.BeginFrameEvent;
|
|
||||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
|
||||||
import com.jozufozu.flywheel.event.RenderLayerEvent;
|
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
|
||||||
import net.minecraft.client.renderer.DestroyBlockProgress;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.renderer.WorldRenderer;
|
|
||||||
import net.minecraft.client.renderer.model.ModelBakery;
|
|
||||||
import net.minecraft.client.renderer.texture.Texture;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureManager;
|
|
||||||
import net.minecraft.client.world.ClientWorld;
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.inventory.container.PlayerContainer;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
import net.minecraft.util.LazyValue;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
|
||||||
import net.minecraft.world.IWorld;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
|
|
||||||
@Mod.EventBusSubscriber(value = Dist.CLIENT)
|
|
||||||
public class InstancedRenderDispatcher {
|
|
||||||
|
|
||||||
private static final WorldAttached<EntityInstanceManager> entityInstanceManager = new WorldAttached<>(world -> new EntityInstanceManager(Contexts.WORLD.getMaterialManager(world)));
|
|
||||||
private static final WorldAttached<TileInstanceManager> tileInstanceManager = new WorldAttached<>(world -> new TileInstanceManager(Contexts.WORLD.getMaterialManager(world)));
|
|
||||||
|
|
||||||
private static final LazyValue<Vector<CrumblingInstanceManager>> blockBreaking = new LazyValue<>(() -> {
|
|
||||||
Vector<CrumblingInstanceManager> renderers = new Vector<>(10);
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
renderers.add(new CrumblingInstanceManager());
|
|
||||||
}
|
|
||||||
return renderers;
|
|
||||||
});
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static TileInstanceManager getTiles(IWorld world) {
|
|
||||||
return tileInstanceManager.get(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static EntityInstanceManager getEntities(IWorld world) {
|
|
||||||
return entityInstanceManager.get(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void tick() {
|
|
||||||
Minecraft mc = Minecraft.getInstance();
|
|
||||||
ClientWorld world = mc.world;
|
|
||||||
|
|
||||||
Entity renderViewEntity = mc.renderViewEntity != null ? mc.renderViewEntity : mc.player;
|
|
||||||
|
|
||||||
if (renderViewEntity == null) return;
|
|
||||||
|
|
||||||
getTiles(world).tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
|
||||||
getEntities(world).tick(renderViewEntity.getX(), renderViewEntity.getY(), renderViewEntity.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void enqueueUpdate(TileEntity te) {
|
|
||||||
getTiles(te.getWorld()).queueUpdate(te);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void enqueueUpdate(Entity entity) {
|
|
||||||
getEntities(entity.world).queueUpdate(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void onBeginFrame(BeginFrameEvent event) {
|
|
||||||
Contexts.WORLD.getMaterialManager(event.getWorld())
|
|
||||||
.checkAndShiftOrigin(event.getInfo());
|
|
||||||
|
|
||||||
getTiles(event.getWorld()).beginFrame(event.getInfo());
|
|
||||||
getEntities(event.getWorld()).beginFrame(event.getInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void renderLayer(RenderLayerEvent event) {
|
|
||||||
ClientWorld world = event.getWorld();
|
|
||||||
if (!Backend.getInstance().canUseInstancing(world)) return;
|
|
||||||
|
|
||||||
event.type.startDrawing();
|
|
||||||
|
|
||||||
Contexts.WORLD.getMaterialManager(world)
|
|
||||||
.render(event.type, event.viewProjection, event.camX, event.camY, event.camZ);
|
|
||||||
|
|
||||||
event.type.endDrawing();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
|
||||||
ClientWorld world = event.getWorld();
|
|
||||||
if (Backend.getInstance().canUseInstancing() && world != null) {
|
|
||||||
Contexts.WORLD.getMaterialManager(world).delete();
|
|
||||||
|
|
||||||
TileInstanceManager tiles = getTiles(world);
|
|
||||||
tiles.invalidate();
|
|
||||||
world.loadedTileEntityList.forEach(tiles::add);
|
|
||||||
|
|
||||||
EntityInstanceManager entities = getEntities(world);
|
|
||||||
entities.invalidate();
|
|
||||||
world.getAllEntities().forEach(entities::add);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final RenderType crumblingLayer = ModelBakery.BLOCK_DESTRUCTION_RENDER_LAYERS.get(0);
|
|
||||||
|
|
||||||
public static void renderBreaking(ClientWorld world, Matrix4f viewProjection, double cameraX, double cameraY, double cameraZ) {
|
|
||||||
if (!Backend.getInstance().canUseInstancing(world)) return;
|
|
||||||
|
|
||||||
WorldRenderer worldRenderer = Minecraft.getInstance().worldRenderer;
|
|
||||||
Long2ObjectMap<SortedSet<DestroyBlockProgress>> breakingProgressions = worldRenderer.blockBreakingProgressions;
|
|
||||||
|
|
||||||
if (breakingProgressions.isEmpty()) return;
|
|
||||||
Vector<CrumblingInstanceManager> renderers = blockBreaking.getValue();
|
|
||||||
|
|
||||||
BitSet bitSet = new BitSet(10);
|
|
||||||
|
|
||||||
for (Long2ObjectMap.Entry<SortedSet<DestroyBlockProgress>> entry : breakingProgressions.long2ObjectEntrySet()) {
|
|
||||||
BlockPos breakingPos = BlockPos.fromLong(entry.getLongKey());
|
|
||||||
|
|
||||||
SortedSet<DestroyBlockProgress> progresses = entry.getValue();
|
|
||||||
if (progresses != null && !progresses.isEmpty()) {
|
|
||||||
int blockDamage = progresses.last().getPartialBlockDamage();
|
|
||||||
bitSet.set(blockDamage);
|
|
||||||
renderers.get(blockDamage).add(world.getTileEntity(breakingPos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TextureManager textureManager = Minecraft.getInstance().textureManager;
|
|
||||||
ActiveRenderInfo info = Minecraft.getInstance().gameRenderer.getActiveRenderInfo();
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureManager.getTexture(PlayerContainer.BLOCK_ATLAS_TEXTURE).getGlTextureId());
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE4);
|
|
||||||
|
|
||||||
crumblingLayer.startDrawing();
|
|
||||||
bitSet.stream().forEach(i -> {
|
|
||||||
Texture breaking = textureManager.getTexture(ModelBakery.BLOCK_DESTRUCTION_STAGE_TEXTURES.get(i));
|
|
||||||
CrumblingInstanceManager renderer = renderers.get(i);
|
|
||||||
renderer.beginFrame(info);
|
|
||||||
|
|
||||||
if (breaking != null) {
|
|
||||||
glBindTexture(GL_TEXTURE_2D, breaking.getGlTextureId());
|
|
||||||
renderer.materialManager.render(RenderType.getCutoutMipped(), viewProjection, cameraX, cameraY, cameraZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer.invalidate();
|
|
||||||
});
|
|
||||||
crumblingLayer.endDrawing();
|
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
Texture breaking = textureManager.getTexture(ModelBakery.BLOCK_DESTRUCTION_STAGE_TEXTURES.get(0));
|
|
||||||
if (breaking != null)
|
|
||||||
glBindTexture(GL_TEXTURE_2D, breaking.getGlTextureId());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.entity.IEntityInstanceFactory;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.ITileInstanceFactory;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileEntityInstance;
|
|
||||||
import com.simibubi.create.AllEntityTypes;
|
|
||||||
import com.simibubi.create.content.contraptions.components.structureMovement.glue.GlueInstance;
|
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.entity.EntityType;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
import net.minecraft.tileentity.TileEntityType;
|
|
||||||
|
|
||||||
public class InstancedRenderRegistry {
|
|
||||||
private static final InstancedRenderRegistry INSTANCE = new InstancedRenderRegistry();
|
|
||||||
|
|
||||||
public static InstancedRenderRegistry getInstance() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<TileEntityType<?>, ITileInstanceFactory<?>> tiles = Maps.newHashMap();
|
|
||||||
private final Map<EntityType<?>, IEntityInstanceFactory<?>> entities = Maps.newHashMap();
|
|
||||||
|
|
||||||
public <T extends TileEntity> void register(TileEntityType<? extends T> type, ITileInstanceFactory<? super T> rendererFactory) {
|
|
||||||
this.tiles.put(type, rendererFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Entity> void register(EntityType<? extends T> type, IEntityInstanceFactory<? super T> rendererFactory) {
|
|
||||||
this.entities.put(type, rendererFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
INSTANCE.register(AllEntityTypes.SUPER_GLUE.get(), GlueInstance::new);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
|
||||||
public <T extends TileEntity> TileEntityInstance<? super T> create(MaterialManager<?> manager, T tile) {
|
|
||||||
TileEntityType<?> type = tile.getType();
|
|
||||||
ITileInstanceFactory<? super T> factory = (ITileInstanceFactory<? super T>) this.tiles.get(type);
|
|
||||||
|
|
||||||
if (factory == null) return null;
|
|
||||||
else return factory.create(manager, tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
|
||||||
public <T extends Entity> EntityInstance<? super T> create(MaterialManager<?> manager, T tile) {
|
|
||||||
EntityType<?> type = tile.getType();
|
|
||||||
IEntityInstanceFactory<? super T> factory = (IEntityInstanceFactory<? super T>) this.entities.get(type);
|
|
||||||
|
|
||||||
if (factory == null) return null;
|
|
||||||
else return factory.create(manager, tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,252 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.BitSet;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.model.BufferedModel;
|
|
||||||
import com.jozufozu.flywheel.util.AttribUtil;
|
|
||||||
|
|
||||||
import net.minecraft.util.math.vector.Vector3i;
|
|
||||||
|
|
||||||
public class Instancer<D extends InstanceData> {
|
|
||||||
|
|
||||||
public final Supplier<Vector3i> originCoordinate;
|
|
||||||
|
|
||||||
protected final BufferedModel model;
|
|
||||||
|
|
||||||
protected final VertexFormat instanceFormat;
|
|
||||||
protected final IInstanceFactory<D> factory;
|
|
||||||
protected GlVertexArray vao;
|
|
||||||
protected GlBuffer instanceVBO;
|
|
||||||
protected int glBufferSize = -1;
|
|
||||||
protected int glInstanceCount = 0;
|
|
||||||
private boolean deleted;
|
|
||||||
|
|
||||||
protected final ArrayList<D> data = new ArrayList<>();
|
|
||||||
|
|
||||||
boolean anyToRemove;
|
|
||||||
boolean anyToUpdate;
|
|
||||||
|
|
||||||
public Instancer(BufferedModel model, Supplier<Vector3i> originCoordinate, MaterialSpec<D> spec) {
|
|
||||||
this.model = model;
|
|
||||||
this.factory = spec.getInstanceFactory();
|
|
||||||
this.instanceFormat = spec.getInstanceFormat();
|
|
||||||
this.originCoordinate = originCoordinate;
|
|
||||||
|
|
||||||
if (model.getVertexCount() <= 0)
|
|
||||||
throw new IllegalArgumentException("Refusing to instance a model with no vertices.");
|
|
||||||
|
|
||||||
vao = new GlVertexArray();
|
|
||||||
instanceVBO = new GlBuffer(GlBufferType.ARRAY_BUFFER);
|
|
||||||
|
|
||||||
vao.bind();
|
|
||||||
|
|
||||||
// bind the model's vbo to our vao
|
|
||||||
model.setupState();
|
|
||||||
|
|
||||||
AttribUtil.enableArrays(model.getAttributeCount() + instanceFormat.getAttributeCount());
|
|
||||||
|
|
||||||
vao.unbind();
|
|
||||||
|
|
||||||
model.clearState();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void render() {
|
|
||||||
if (deleted) return;
|
|
||||||
|
|
||||||
vao.bind();
|
|
||||||
renderSetup();
|
|
||||||
|
|
||||||
if (glInstanceCount > 0)
|
|
||||||
model.drawInstances(glInstanceCount);
|
|
||||||
|
|
||||||
vao.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
public D createInstance() {
|
|
||||||
D instanceData = factory.create(this);
|
|
||||||
instanceData.dirty = true;
|
|
||||||
anyToUpdate = true;
|
|
||||||
data.add(instanceData);
|
|
||||||
|
|
||||||
return instanceData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean empty() {
|
|
||||||
return !anyToUpdate && !anyToRemove && glInstanceCount == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear all instance data without freeing resources.
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
data.clear();
|
|
||||||
anyToRemove = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free acquired resources. Attempting to use this after calling delete is undefined behavior.
|
|
||||||
*/
|
|
||||||
public void delete() {
|
|
||||||
if (deleted) return;
|
|
||||||
|
|
||||||
deleted = true;
|
|
||||||
|
|
||||||
model.delete();
|
|
||||||
|
|
||||||
instanceVBO.delete();
|
|
||||||
vao.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void renderSetup() {
|
|
||||||
if (anyToRemove) {
|
|
||||||
removeDeletedInstances();
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceVBO.bind();
|
|
||||||
if (!realloc()) {
|
|
||||||
|
|
||||||
if (anyToRemove) {
|
|
||||||
clearBufferTail();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (anyToUpdate) {
|
|
||||||
updateBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
glInstanceCount = data.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceVBO.unbind();
|
|
||||||
|
|
||||||
anyToRemove = anyToUpdate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearBufferTail() {
|
|
||||||
int size = data.size();
|
|
||||||
final int offset = size * instanceFormat.getStride();
|
|
||||||
final int length = glBufferSize - offset;
|
|
||||||
if (length > 0) {
|
|
||||||
instanceVBO.getBuffer(offset, length)
|
|
||||||
.putByteArray(new byte[length])
|
|
||||||
.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateBuffer() {
|
|
||||||
final int size = data.size();
|
|
||||||
|
|
||||||
if (size <= 0) return;
|
|
||||||
|
|
||||||
final int stride = instanceFormat.getStride();
|
|
||||||
final BitSet dirtySet = getDirtyBitSet();
|
|
||||||
|
|
||||||
if (dirtySet.isEmpty()) return;
|
|
||||||
|
|
||||||
final int firstDirty = dirtySet.nextSetBit(0);
|
|
||||||
final int lastDirty = dirtySet.previousSetBit(size);
|
|
||||||
|
|
||||||
final int offset = firstDirty * stride;
|
|
||||||
final int length = (1 + lastDirty - firstDirty) * stride;
|
|
||||||
|
|
||||||
if (length > 0) {
|
|
||||||
MappedBuffer mapped = instanceVBO.getBuffer(offset, length);
|
|
||||||
|
|
||||||
dirtySet.stream().forEach(i -> {
|
|
||||||
final D d = data.get(i);
|
|
||||||
|
|
||||||
mapped.position(i * stride);
|
|
||||||
d.write(mapped);
|
|
||||||
});
|
|
||||||
mapped.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BitSet getDirtyBitSet() {
|
|
||||||
final int size = data.size();
|
|
||||||
final BitSet dirtySet = new BitSet(size);
|
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
D element = data.get(i);
|
|
||||||
if (element.dirty) {
|
|
||||||
dirtySet.set(i);
|
|
||||||
|
|
||||||
element.dirty = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dirtySet;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean realloc() {
|
|
||||||
int size = this.data.size();
|
|
||||||
int stride = instanceFormat.getStride();
|
|
||||||
int requiredSize = size * stride;
|
|
||||||
if (requiredSize > glBufferSize) {
|
|
||||||
glBufferSize = requiredSize + stride * 16;
|
|
||||||
instanceVBO.alloc(glBufferSize);
|
|
||||||
|
|
||||||
MappedBuffer buffer = instanceVBO.getBuffer(0, glBufferSize);
|
|
||||||
for (D datum : data) {
|
|
||||||
datum.write(buffer);
|
|
||||||
}
|
|
||||||
buffer.flush();
|
|
||||||
|
|
||||||
glInstanceCount = size;
|
|
||||||
|
|
||||||
informAttribDivisors();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeDeletedInstances() {
|
|
||||||
// Figure out which elements are to be removed.
|
|
||||||
final int oldSize = this.data.size();
|
|
||||||
int removeCount = 0;
|
|
||||||
final BitSet removeSet = new BitSet(oldSize);
|
|
||||||
for (int i = 0; i < oldSize; i++) {
|
|
||||||
final D element = this.data.get(i);
|
|
||||||
if (element.removed) {
|
|
||||||
removeSet.set(i);
|
|
||||||
removeCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final int newSize = oldSize - removeCount;
|
|
||||||
|
|
||||||
// shift surviving elements left over the spaces left by removed elements
|
|
||||||
for (int i = 0, j = 0; (i < oldSize) && (j < newSize); i++, j++) {
|
|
||||||
i = removeSet.nextClearBit(i);
|
|
||||||
|
|
||||||
if (i != j) {
|
|
||||||
D element = data.get(i);
|
|
||||||
data.set(j, element);
|
|
||||||
element.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
anyToUpdate = true;
|
|
||||||
|
|
||||||
data.subList(newSize, oldSize).clear();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void informAttribDivisors() {
|
|
||||||
int staticAttributes = model.getAttributeCount();
|
|
||||||
instanceFormat.vertexAttribPointers(staticAttributes);
|
|
||||||
|
|
||||||
for (int i = 0; i < instanceFormat.getAttributeCount(); i++) {
|
|
||||||
Backend.getInstance().compat.instancedArrays.vertexAttribDivisor(i + staticAttributes, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,161 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.core.Materials;
|
|
||||||
import com.jozufozu.flywheel.core.WorldContext;
|
|
||||||
import com.jozufozu.flywheel.core.materials.ModelData;
|
|
||||||
import com.jozufozu.flywheel.core.materials.OrientedData;
|
|
||||||
import com.jozufozu.flywheel.core.shader.IProgramCallback;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
import com.jozufozu.flywheel.util.WeakHashSet;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.renderer.ActiveRenderInfo;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.MathHelper;
|
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
|
||||||
import net.minecraft.util.math.vector.Vector3i;
|
|
||||||
|
|
||||||
public class MaterialManager<P extends WorldProgram> {
|
|
||||||
|
|
||||||
public static int MAX_ORIGIN_DISTANCE = 100;
|
|
||||||
|
|
||||||
protected final WorldContext<P> context;
|
|
||||||
|
|
||||||
protected final Map<MaterialSpec<?>, InstanceMaterial<?>> atlasMaterials;
|
|
||||||
protected final ArrayList<MaterialRenderer<P>> atlasRenderers;
|
|
||||||
|
|
||||||
protected final Map<ResourceLocation, ArrayList<MaterialRenderer<P>>> renderers;
|
|
||||||
protected final Map<ResourceLocation, Map<MaterialSpec<?>, InstanceMaterial<?>>> materials;
|
|
||||||
|
|
||||||
private BlockPos originCoordinate = BlockPos.ZERO;
|
|
||||||
|
|
||||||
private final WeakHashSet<OriginShiftListener> listeners;
|
|
||||||
|
|
||||||
public MaterialManager(WorldContext<P> context) {
|
|
||||||
this.context = context;
|
|
||||||
|
|
||||||
this.atlasMaterials = new HashMap<>();
|
|
||||||
this.atlasRenderers = new ArrayList<>(Backend.getInstance().allMaterials().size());
|
|
||||||
|
|
||||||
this.materials = new HashMap<>();
|
|
||||||
this.renderers = new HashMap<>();
|
|
||||||
|
|
||||||
this.listeners = new WeakHashSet<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render every model for every material.
|
|
||||||
*
|
|
||||||
* @param layer Which vanilla {@link RenderType} is being drawn?
|
|
||||||
* @param viewProjection How do we get from camera space to clip space?
|
|
||||||
*/
|
|
||||||
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ) {
|
|
||||||
render(layer, viewProjection, camX, camY, camZ, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render every model for every material.
|
|
||||||
*
|
|
||||||
* @param layer Which vanilla {@link RenderType} is being drawn?
|
|
||||||
* @param viewProjection How do we get from camera space to clip space?
|
|
||||||
* @param callback Provide additional uniforms or state here.
|
|
||||||
*/
|
|
||||||
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, IProgramCallback<P> callback) {
|
|
||||||
camX -= originCoordinate.getX();
|
|
||||||
camY -= originCoordinate.getY();
|
|
||||||
camZ -= originCoordinate.getZ();
|
|
||||||
|
|
||||||
Matrix4f translate = Matrix4f.translate((float) -camX, (float) -camY, (float) -camZ);
|
|
||||||
|
|
||||||
translate.multiplyBackward(viewProjection);
|
|
||||||
|
|
||||||
for (MaterialRenderer<P> material : atlasRenderers) {
|
|
||||||
material.render(layer, translate, camX, camY, camZ, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<ResourceLocation, ArrayList<MaterialRenderer<P>>> entry : renderers.entrySet()) {
|
|
||||||
Minecraft.getInstance().textureManager.bindTexture(entry.getKey());
|
|
||||||
|
|
||||||
for (MaterialRenderer<P> materialRenderer : entry.getValue()) {
|
|
||||||
materialRenderer.render(layer, translate, camX, camY, camZ, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
atlasMaterials.values().forEach(InstanceMaterial::delete);
|
|
||||||
|
|
||||||
materials.values().stream().flatMap(m -> m.values().stream()).forEach(InstanceMaterial::delete);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <D extends InstanceData> InstanceMaterial<D> getMaterial(MaterialSpec<D> materialType) {
|
|
||||||
return (InstanceMaterial<D>) this.atlasMaterials.computeIfAbsent(materialType, type -> {
|
|
||||||
InstanceMaterial<?> material = new InstanceMaterial<>(this::getOriginCoordinate, type);
|
|
||||||
|
|
||||||
this.atlasRenderers.add(new MaterialRenderer<>(context.getProgramSupplier(type.getProgramName()), material));
|
|
||||||
|
|
||||||
return material;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public <D extends InstanceData> InstanceMaterial<D> getMaterial(MaterialSpec<D> materialType, ResourceLocation texture) {
|
|
||||||
return (InstanceMaterial<D>) materials.computeIfAbsent(texture, $ -> new HashMap<>())
|
|
||||||
.computeIfAbsent(materialType, type -> {
|
|
||||||
InstanceMaterial<?> material = new InstanceMaterial<>(this::getOriginCoordinate, type);
|
|
||||||
|
|
||||||
this.renderers.computeIfAbsent(texture, $ -> new ArrayList<>())
|
|
||||||
.add(new MaterialRenderer<>(context.getProgramSupplier(type.getProgramName()), material));
|
|
||||||
|
|
||||||
return material;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public InstanceMaterial<ModelData> getTransformMaterial() {
|
|
||||||
return getMaterial(Materials.TRANSFORMED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public InstanceMaterial<OrientedData> getOrientedMaterial() {
|
|
||||||
return getMaterial(Materials.ORIENTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3i getOriginCoordinate() {
|
|
||||||
return originCoordinate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onOriginShift(OriginShiftListener listener) {
|
|
||||||
listeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkAndShiftOrigin(ActiveRenderInfo info) {
|
|
||||||
int cX = MathHelper.floor(info.getProjectedView().x);
|
|
||||||
int cY = MathHelper.floor(info.getProjectedView().y);
|
|
||||||
int cZ = MathHelper.floor(info.getProjectedView().z);
|
|
||||||
|
|
||||||
int dX = cX - originCoordinate.getX();
|
|
||||||
int dY = cY - originCoordinate.getY();
|
|
||||||
int dZ = cZ - originCoordinate.getZ();
|
|
||||||
|
|
||||||
if (Math.abs(dX) > MAX_ORIGIN_DISTANCE || Math.abs(dY) > MAX_ORIGIN_DISTANCE || Math.abs(dZ) > MAX_ORIGIN_DISTANCE) {
|
|
||||||
|
|
||||||
originCoordinate = new BlockPos(cX, cY, cZ);
|
|
||||||
|
|
||||||
materials.values().stream().flatMap(m -> m.values().stream()).forEach(InstanceMaterial::clear);
|
|
||||||
atlasMaterials.values().forEach(InstanceMaterial::clear);
|
|
||||||
listeners.forEach(OriginShiftListener::onOriginShift);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface OriginShiftListener {
|
|
||||||
void onOriginShift();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.core.shader.IProgramCallback;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.util.math.vector.Matrix4f;
|
|
||||||
|
|
||||||
public class MaterialRenderer<P extends WorldProgram> {
|
|
||||||
|
|
||||||
private final Supplier<P> program;
|
|
||||||
private final InstanceMaterial<?> material;
|
|
||||||
|
|
||||||
public MaterialRenderer(Supplier<P> programSupplier, InstanceMaterial<?> material) {
|
|
||||||
this.program = programSupplier;
|
|
||||||
this.material = material;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void render(RenderType layer, Matrix4f viewProjection, double camX, double camY, double camZ, IProgramCallback<P> setup) {
|
|
||||||
if (!(layer == RenderType.getCutoutMipped())) return;
|
|
||||||
|
|
||||||
if (material.nothingToRender()) return;
|
|
||||||
|
|
||||||
P program = this.program.get();
|
|
||||||
|
|
||||||
program.bind();
|
|
||||||
program.uploadViewProjection(viewProjection);
|
|
||||||
program.uploadCameraPos(camX, camY, camZ);
|
|
||||||
|
|
||||||
if (setup != null) setup.call(program);
|
|
||||||
|
|
||||||
makeRenderCalls();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void makeRenderCalls() {
|
|
||||||
material.forEachInstancer(Instancer::render);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|
||||||
|
|
||||||
import net.minecraft.inventory.container.PlayerContainer;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class MaterialSpec<D extends InstanceData> {
|
|
||||||
|
|
||||||
public final ResourceLocation name;
|
|
||||||
|
|
||||||
private final ResourceLocation programSpec;
|
|
||||||
private final VertexFormat modelFormat;
|
|
||||||
private final VertexFormat instanceFormat;
|
|
||||||
private final IInstanceFactory<D> instanceFactory;
|
|
||||||
private final ResourceLocation texture;
|
|
||||||
|
|
||||||
public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, IInstanceFactory<D> instanceFactory) {
|
|
||||||
this(name, programSpec, modelFormat, instanceFormat, PlayerContainer.BLOCK_ATLAS_TEXTURE, instanceFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MaterialSpec(ResourceLocation name, ResourceLocation programSpec, VertexFormat modelFormat, VertexFormat instanceFormat, ResourceLocation texture, IInstanceFactory<D> instanceFactory) {
|
|
||||||
this.name = name;
|
|
||||||
this.programSpec = programSpec;
|
|
||||||
this.modelFormat = modelFormat;
|
|
||||||
this.instanceFormat = instanceFormat;
|
|
||||||
this.instanceFactory = instanceFactory;
|
|
||||||
this.texture = texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceLocation getProgramName() {
|
|
||||||
return programSpec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexFormat getModelFormat() {
|
|
||||||
return modelFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexFormat getInstanceFormat() {
|
|
||||||
return instanceFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IInstanceFactory<D> getInstanceFactory() {
|
|
||||||
return instanceFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.entity;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceMaterial;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
|
||||||
import com.jozufozu.flywheel.core.materials.IFlatLight;
|
|
||||||
import com.jozufozu.flywheel.core.materials.ModelData;
|
|
||||||
import com.jozufozu.flywheel.core.materials.OrientedData;
|
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.vector.Vector3d;
|
|
||||||
import net.minecraft.util.math.vector.Vector3f;
|
|
||||||
import net.minecraft.util.math.vector.Vector3i;
|
|
||||||
import net.minecraft.world.LightType;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The layer between a {@link TileEntity} and the Flywheel backend.
|
|
||||||
**
|
|
||||||
* <br><br> There are a few additional features that overriding classes can opt in to:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link IDynamicInstance}</li>
|
|
||||||
* <li>{@link ITickableInstance}</li>
|
|
||||||
* </ul>
|
|
||||||
* See the interfaces' documentation for more information about each one.
|
|
||||||
*
|
|
||||||
* <br> Implementing one or more of these will give a {@link EntityInstance} access
|
|
||||||
* to more interesting and regular points within a tick or a frame.
|
|
||||||
*
|
|
||||||
* @param <E> The type of {@link Entity} your class is an instance of.
|
|
||||||
*/
|
|
||||||
public abstract class EntityInstance<E extends Entity> implements IInstance {
|
|
||||||
|
|
||||||
protected final MaterialManager<?> materialManager;
|
|
||||||
protected final E entity;
|
|
||||||
protected final World world;
|
|
||||||
|
|
||||||
public EntityInstance(MaterialManager<?> materialManager, E entity) {
|
|
||||||
this.materialManager = materialManager;
|
|
||||||
this.entity = entity;
|
|
||||||
this.world = entity.world;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free any acquired resources.
|
|
||||||
*/
|
|
||||||
public abstract void remove();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
|
||||||
* Don't query lighting data here, that's handled separately in {@link #updateLight()}.
|
|
||||||
*
|
|
||||||
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
|
|
||||||
*/
|
|
||||||
public void update() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after construction and when a light update occurs in the world.
|
|
||||||
*
|
|
||||||
* <br> If your model needs it, update light here.
|
|
||||||
*/
|
|
||||||
public void updateLight() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
|
|
||||||
* If this function returns <code>true</code>, then this instance will be {@link #remove removed},
|
|
||||||
* and another instance will be constructed to replace it. This allows for more sane resource
|
|
||||||
* acquisition compared to trying to update everything within the lifetime of an instance.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this instance should be discarded and refreshed.
|
|
||||||
*/
|
|
||||||
public boolean shouldReset() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In order to accommodate for floating point precision errors at high coordinates,
|
|
||||||
* {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and
|
|
||||||
* shift the world matrix provided as a shader uniform accordingly.
|
|
||||||
*
|
|
||||||
* @return The {@link BlockPos position} of the {@link Entity} this instance
|
|
||||||
* represents should be rendered at to appear in the correct location.
|
|
||||||
*/
|
|
||||||
public Vector3f getInstancePosition() {
|
|
||||||
Vector3d pos = entity.getPositionVec();
|
|
||||||
Vector3i origin = materialManager.getOriginCoordinate();
|
|
||||||
return new Vector3f(
|
|
||||||
(float) (pos.x - origin.getX()),
|
|
||||||
(float) (pos.y - origin.getY()),
|
|
||||||
(float) (pos.z - origin.getZ())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockPos getWorldPosition() {
|
|
||||||
return entity.getBlockPos();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void relight(BlockPos pos, IFlatLight<?>... models) {
|
|
||||||
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
|
|
||||||
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void relight(int block, int sky, IFlatLight<?>... models) {
|
|
||||||
relight(block, sky, Arrays.stream(models));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
|
|
||||||
models.forEach(model -> model.setBlockLight(block).setSkyLight(sky));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InstanceMaterial<ModelData> getTransformMaterial() {
|
|
||||||
return materialManager.getTransformMaterial();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InstanceMaterial<OrientedData> getOrientedMaterial() {
|
|
||||||
return materialManager.getOrientedMaterial();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.entity;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.IBlockReader;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
public class EntityInstanceManager extends InstanceManager<Entity> {
|
|
||||||
|
|
||||||
public EntityInstanceManager(MaterialManager<?> materialManager) {
|
|
||||||
super(materialManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected IInstance createRaw(Entity obj) {
|
|
||||||
return InstancedRenderRegistry.getInstance().create(materialManager, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean canCreateInstance(Entity entity) {
|
|
||||||
if (!entity.isAlive()) return false;
|
|
||||||
|
|
||||||
World world = entity.world;
|
|
||||||
|
|
||||||
if (world == null) return false;
|
|
||||||
|
|
||||||
if (Backend.isFlywheelWorld(world)) {
|
|
||||||
BlockPos pos = entity.getBlockPos();
|
|
||||||
|
|
||||||
IBlockReader existingChunk = world.getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4);
|
|
||||||
|
|
||||||
return existingChunk != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.entity;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
|
|
||||||
import net.minecraft.entity.Entity;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface IEntityInstanceFactory<E extends Entity> {
|
|
||||||
EntityInstance<? super E> create(MaterialManager<?> manager, E te);
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.tile;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface ITileInstanceFactory<T extends TileEntity> {
|
|
||||||
TileEntityInstance<? super T> create(MaterialManager<?> manager, T te);
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.tile;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.IDynamicInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.ITickableInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceMaterial;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
import com.jozufozu.flywheel.core.materials.IFlatLight;
|
|
||||||
import com.jozufozu.flywheel.core.materials.ModelData;
|
|
||||||
import com.jozufozu.flywheel.core.materials.OrientedData;
|
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.LightType;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The layer between a {@link TileEntity} and the Flywheel backend.
|
|
||||||
*
|
|
||||||
* <br><br> {@link #updateLight()} is called after construction.
|
|
||||||
*
|
|
||||||
* <br><br> There are a few additional features that overriding classes can opt in to:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link IDynamicInstance}</li>
|
|
||||||
* <li>{@link ITickableInstance}</li>
|
|
||||||
* </ul>
|
|
||||||
* See the interfaces' documentation for more information about each one.
|
|
||||||
*
|
|
||||||
* <br> Implementing one or more of these will give a {@link TileEntityInstance} access
|
|
||||||
* to more interesting and regular points within a tick or a frame.
|
|
||||||
*
|
|
||||||
* @param <T> The type of {@link TileEntity} your class is an instance of.
|
|
||||||
*/
|
|
||||||
public abstract class TileEntityInstance<T extends TileEntity> implements IInstance {
|
|
||||||
|
|
||||||
protected final MaterialManager<?> materialManager;
|
|
||||||
protected final T tile;
|
|
||||||
protected final World world;
|
|
||||||
protected final BlockPos pos;
|
|
||||||
protected final BlockPos instancePos;
|
|
||||||
protected final BlockState blockState;
|
|
||||||
|
|
||||||
public TileEntityInstance(MaterialManager<?> materialManager, T tile) {
|
|
||||||
this.materialManager = materialManager;
|
|
||||||
this.tile = tile;
|
|
||||||
this.world = tile.getWorld();
|
|
||||||
this.pos = tile.getPos();
|
|
||||||
this.blockState = tile.getBlockState();
|
|
||||||
this.instancePos = pos.subtract(materialManager.getOriginCoordinate());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
|
||||||
* Don't query lighting data here, that's handled separately in {@link #updateLight()}.
|
|
||||||
*
|
|
||||||
* <br><br> If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
|
|
||||||
*/
|
|
||||||
public void update() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after construction and when a light update occurs in the world.
|
|
||||||
*
|
|
||||||
* <br> If your model needs it, update light here.
|
|
||||||
*/
|
|
||||||
public void updateLight() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free any acquired resources.
|
|
||||||
*/
|
|
||||||
public abstract void remove();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Just before {@link #update()} would be called, <code>shouldReset()</code> is checked.
|
|
||||||
* If this function returns <code>true</code>, then this instance will be {@link #remove removed},
|
|
||||||
* and another instance will be constructed to replace it. This allows for more sane resource
|
|
||||||
* acquisition compared to trying to update everything within the lifetime of an instance.
|
|
||||||
*
|
|
||||||
* @return <code>true</code> if this instance should be discarded and refreshed.
|
|
||||||
*/
|
|
||||||
public boolean shouldReset() {
|
|
||||||
return tile.getBlockState() != blockState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In order to accommodate for floating point precision errors at high coordinates,
|
|
||||||
* {@link TileInstanceManager}s are allowed to arbitrarily adjust the origin, and
|
|
||||||
* shift the world matrix provided as a shader uniform accordingly.
|
|
||||||
*
|
|
||||||
* @return The {@link BlockPos position} of the {@link TileEntity} this instance
|
|
||||||
* represents should be rendered at to appear in the correct location.
|
|
||||||
*/
|
|
||||||
public BlockPos getInstancePosition() {
|
|
||||||
return pos.subtract(materialManager.getOriginCoordinate());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockPos getWorldPosition() {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void relight(BlockPos pos, IFlatLight<?>... models) {
|
|
||||||
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <L extends IFlatLight<?>> void relight(BlockPos pos, Stream<L> models) {
|
|
||||||
relight(world.getLightLevel(LightType.BLOCK, pos), world.getLightLevel(LightType.SKY, pos), models);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void relight(int block, int sky, IFlatLight<?>... models) {
|
|
||||||
relight(block, sky, Arrays.stream(models));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <L extends IFlatLight<?>> void relight(int block, int sky, Stream<L> models) {
|
|
||||||
models.forEach(model -> model.setBlockLight(block).setSkyLight(sky));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InstanceMaterial<ModelData> getTransformMaterial() {
|
|
||||||
return materialManager.getTransformMaterial();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected InstanceMaterial<OrientedData> getOrientedMaterial() {
|
|
||||||
return materialManager.getOrientedMaterial();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.tile;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.IInstance;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderRegistry;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
|
|
||||||
import net.minecraft.tileentity.TileEntity;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.world.IBlockReader;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
|
|
||||||
public class TileInstanceManager extends InstanceManager<TileEntity> {
|
|
||||||
|
|
||||||
public TileInstanceManager(MaterialManager<?> materialManager) {
|
|
||||||
super(materialManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected IInstance createRaw(TileEntity obj) {
|
|
||||||
return InstancedRenderRegistry.getInstance().create(materialManager, obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean canCreateInstance(TileEntity tile) {
|
|
||||||
if (tile.isRemoved()) return false;
|
|
||||||
|
|
||||||
World world = tile.getWorld();
|
|
||||||
|
|
||||||
if (world == null) return false;
|
|
||||||
|
|
||||||
if (world.isAirBlock(tile.getPos())) return false;
|
|
||||||
|
|
||||||
if (Backend.isFlywheelWorld(world)) {
|
|
||||||
BlockPos pos = tile.getPos();
|
|
||||||
|
|
||||||
IBlockReader existingChunk = world.getExistingChunk(pos.getX() >> 4, pos.getZ() >> 4);
|
|
||||||
|
|
||||||
return existingChunk != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface IProcessingStage {
|
|
||||||
|
|
||||||
void process(Shader shader);
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class InstancedArraysTemplate extends ProgramTemplate {
|
|
||||||
|
|
||||||
public static final String vertexData = "VertexData";
|
|
||||||
public static final String instanceData = "InstanceData";
|
|
||||||
public static final String fragment = "Fragment";
|
|
||||||
|
|
||||||
public static final String vertexPrefix = "a_v_";
|
|
||||||
public static final String instancePrefix = "a_i_";
|
|
||||||
|
|
||||||
public static final String[] requiredVert = new String[]{instanceData, vertexData, fragment};
|
|
||||||
|
|
||||||
public static final String[] requiredFrag = {fragment};
|
|
||||||
|
|
||||||
public static final ResourceLocation vert = new ResourceLocation(Flywheel.ID, "template/instanced/instanced.vert");
|
|
||||||
public static final ResourceLocation frag = new ResourceLocation(Flywheel.ID, "template/instanced/instanced.frag");
|
|
||||||
|
|
||||||
public InstancedArraysTemplate(ShaderSources loader) {
|
|
||||||
super(loader);
|
|
||||||
|
|
||||||
templates.put(ShaderType.VERTEX, new ShaderTemplate(requiredVert, loader.getShaderSource(vert)));
|
|
||||||
templates.put(ShaderType.FRAGMENT, new ShaderTemplate(requiredFrag, loader.getShaderSource(frag)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attachAttributes(Program builder) {
|
|
||||||
Shader shader = builder.attached.get(ShaderType.VERTEX);
|
|
||||||
|
|
||||||
shader.getTag(vertexData).addPrefixedAttributes(builder, vertexPrefix);
|
|
||||||
shader.getTag(instanceData).addPrefixedAttributes(builder, instancePrefix);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
|
|
||||||
public class LayoutTag {
|
|
||||||
|
|
||||||
public static final Pattern pattern = Pattern.compile("Layout\\((\\w+)(?:\\s*,\\s*(\\w*))?\\)");
|
|
||||||
|
|
||||||
final GlNumericType type;
|
|
||||||
final boolean normalized;
|
|
||||||
|
|
||||||
public LayoutTag(Matcher matcher) {
|
|
||||||
type = GlNumericType.byName(matcher.group(1));
|
|
||||||
normalized = Boolean.parseBoolean(matcher.group(2));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class ModelTemplate extends ProgramTemplate {
|
|
||||||
public static final String vertexData = "VertexData";
|
|
||||||
public static final String fragment = "Fragment";
|
|
||||||
|
|
||||||
public static final String vertexPrefix = "a_v_";
|
|
||||||
|
|
||||||
public static final String[] requiredVert = new String[]{vertexData, fragment};
|
|
||||||
|
|
||||||
public static final String[] requiredFrag = {fragment};
|
|
||||||
|
|
||||||
public static final ResourceLocation vert = new ResourceLocation(Flywheel.ID, "template/model/model.vert");
|
|
||||||
public static final ResourceLocation frag = new ResourceLocation(Flywheel.ID, "template/model/model.frag");
|
|
||||||
|
|
||||||
public ModelTemplate(ShaderSources loader) {
|
|
||||||
super(loader);
|
|
||||||
|
|
||||||
templates.put(ShaderType.VERTEX, new ShaderTemplate(requiredVert, loader.getShaderSource(vert)));
|
|
||||||
templates.put(ShaderType.FRAGMENT, new ShaderTemplate(requiredFrag, loader.getShaderSource(frag)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attachAttributes(Program builder) {
|
|
||||||
Shader shader = builder.attached.get(ShaderType.VERTEX);
|
|
||||||
|
|
||||||
shader.getTag(vertexData).addPrefixedAttributes(builder, vertexPrefix);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL20.GL_LINK_STATUS;
|
|
||||||
import static org.lwjgl.opengl.GL20.GL_TRUE;
|
|
||||||
import static org.lwjgl.opengl.GL20.glAttachShader;
|
|
||||||
import static org.lwjgl.opengl.GL20.glBindAttribLocation;
|
|
||||||
import static org.lwjgl.opengl.GL20.glCreateProgram;
|
|
||||||
import static org.lwjgl.opengl.GL20.glGetProgramInfoLog;
|
|
||||||
import static org.lwjgl.opengl.GL20.glGetProgrami;
|
|
||||||
import static org.lwjgl.opengl.GL20.glLinkProgram;
|
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlShader;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class Program {
|
|
||||||
public final ResourceLocation name;
|
|
||||||
public final int program;
|
|
||||||
|
|
||||||
private int attributeIndex;
|
|
||||||
|
|
||||||
public final Map<ShaderType, Shader> attached;
|
|
||||||
|
|
||||||
public Program(ResourceLocation name) {
|
|
||||||
this.name = name;
|
|
||||||
this.program = glCreateProgram();
|
|
||||||
attached = new EnumMap<>(ShaderType.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Program attachShader(Shader shader, GlShader glShader) {
|
|
||||||
glAttachShader(this.program, glShader.handle());
|
|
||||||
|
|
||||||
attached.put(shader.type, shader);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Program addAttribute(String name, int attributeCount) {
|
|
||||||
glBindAttribLocation(this.program, attributeIndex, name);
|
|
||||||
attributeIndex += attributeCount;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Links the attached shaders to this program.
|
|
||||||
*/
|
|
||||||
public Program link() {
|
|
||||||
glLinkProgram(this.program);
|
|
||||||
|
|
||||||
String log = glGetProgramInfoLog(this.program);
|
|
||||||
|
|
||||||
if (!log.isEmpty()) {
|
|
||||||
Backend.log.debug("Program link log for " + this.name + ": " + log);
|
|
||||||
}
|
|
||||||
|
|
||||||
int result = glGetProgrami(this.program, GL_LINK_STATUS);
|
|
||||||
|
|
||||||
if (result != GL_TRUE) {
|
|
||||||
throw new RuntimeException("Shader program linking failed, see log for details");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
|
|
||||||
public abstract class ProgramTemplate implements IProcessingStage {
|
|
||||||
|
|
||||||
protected final ShaderSources loader;
|
|
||||||
protected Map<ShaderType, ShaderTemplate> templates = new EnumMap<>(ShaderType.class);
|
|
||||||
|
|
||||||
public ProgramTemplate(ShaderSources loader) {
|
|
||||||
this.loader = loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void process(Shader shader) {
|
|
||||||
ShaderTemplate template = templates.get(shader.type);
|
|
||||||
|
|
||||||
if (template == null) return;
|
|
||||||
|
|
||||||
shader.setSource(template.apply(shader));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void attachAttributes(Program builder) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,149 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.StringReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class Shader {
|
|
||||||
// #flwinclude <"valid_namespace:valid/path_to_file.glsl">
|
|
||||||
private static final Pattern includePattern = Pattern.compile("#flwinclude <\"([\\w\\d_]+:[\\w\\d_./]+)\">");
|
|
||||||
|
|
||||||
public static final Pattern versionDetector = Pattern.compile("#version[^\\n]*");
|
|
||||||
private static final Pattern decorator = Pattern.compile("#\\[([\\w_]*)]");
|
|
||||||
|
|
||||||
public final ResourceLocation name;
|
|
||||||
public ShaderType type;
|
|
||||||
private String source;
|
|
||||||
private final ShaderSources loader;
|
|
||||||
|
|
||||||
private boolean parsed = false;
|
|
||||||
final List<TaggedStruct> structs = new ArrayList<>(3);
|
|
||||||
final Map<String, TaggedStruct> tag2Struct = new HashMap<>();
|
|
||||||
final Map<String, TaggedStruct> name2Struct = new HashMap<>();
|
|
||||||
|
|
||||||
public Shader(ShaderSources loader, ShaderType type, ResourceLocation name, String source) {
|
|
||||||
this.loader = loader;
|
|
||||||
this.type = type;
|
|
||||||
this.name = name;
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSource() {
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSource(String source) {
|
|
||||||
this.source = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaggedStruct getTag(String tag) {
|
|
||||||
checkAndParse();
|
|
||||||
return tag2Struct.get(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaggedStruct getStruct(String name) {
|
|
||||||
checkAndParse();
|
|
||||||
return name2Struct.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkAndParse() {
|
|
||||||
if (!parsed) {
|
|
||||||
parsed = true;
|
|
||||||
parseStructs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void defineAll(Collection<String> defines) {
|
|
||||||
Matcher matcher = versionDetector.matcher(source);
|
|
||||||
|
|
||||||
if (matcher.find()) {
|
|
||||||
StringBuffer sourceWithDefines = new StringBuffer();
|
|
||||||
String lines = defines.stream().map(it -> "#define " + it).collect(Collectors.joining("\n"));
|
|
||||||
|
|
||||||
matcher.appendReplacement(sourceWithDefines, matcher.group() + '\n' + lines);
|
|
||||||
|
|
||||||
matcher.appendTail(sourceWithDefines);
|
|
||||||
|
|
||||||
source = sourceWithDefines.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void parseStructs() {
|
|
||||||
Matcher structMatcher = TaggedStruct.taggedStruct.matcher(source);
|
|
||||||
|
|
||||||
StringBuffer strippedSrc = new StringBuffer();
|
|
||||||
|
|
||||||
while (structMatcher.find()) {
|
|
||||||
TaggedStruct struct = new TaggedStruct(structMatcher);
|
|
||||||
|
|
||||||
structs.add(struct);
|
|
||||||
|
|
||||||
structMatcher.appendReplacement(strippedSrc, decorator.matcher(struct.source).replaceFirst(""));
|
|
||||||
|
|
||||||
tag2Struct.put(struct.tag, struct);
|
|
||||||
name2Struct.put(struct.name, struct);
|
|
||||||
}
|
|
||||||
structMatcher.appendTail(strippedSrc);
|
|
||||||
|
|
||||||
this.source = strippedSrc.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void processIncludes() {
|
|
||||||
HashSet<ResourceLocation> seen = new HashSet<>();
|
|
||||||
seen.add(name);
|
|
||||||
|
|
||||||
source = includeRecursive(source, seen).collect(Collectors.joining("\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private Stream<String> includeRecursive(String source, Set<ResourceLocation> seen) {
|
|
||||||
return lines(source).flatMap(line -> {
|
|
||||||
|
|
||||||
Matcher matcher = includePattern.matcher(line);
|
|
||||||
|
|
||||||
if (matcher.find()) {
|
|
||||||
String includeName = matcher.group(1);
|
|
||||||
|
|
||||||
ResourceLocation include = new ResourceLocation(includeName);
|
|
||||||
|
|
||||||
if (seen.add(include)) {
|
|
||||||
try {
|
|
||||||
return includeRecursive(loader.getShaderSource(include), seen);
|
|
||||||
} catch (ShaderLoadingException e) {
|
|
||||||
throw new ShaderLoadingException("could not resolve import: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return Stream.of(line);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void printSource() {
|
|
||||||
Backend.log.debug("Source for shader '" + name + "':");
|
|
||||||
int i = 1;
|
|
||||||
for (String s : source.split("\n")) {
|
|
||||||
Backend.log.debug(String.format("%1$4s: ", i++) + s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Stream<String> lines(String s) {
|
|
||||||
return new BufferedReader(new StringReader(s)).lines();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
public class ShaderLoadingException extends RuntimeException {
|
|
||||||
public ShaderLoadingException() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderLoadingException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderLoadingException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderLoadingException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderLoadingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class ShaderTemplate {
|
|
||||||
|
|
||||||
private static final String delimiter = "#flwbeginbody";
|
|
||||||
private static final Pattern headerFinder = Pattern.compile(delimiter);
|
|
||||||
|
|
||||||
private static final Pattern prefixer = Pattern.compile("#FLWPrefixFields\\((\\w+),\\s*(\\w+),\\s*([\\w\\d]+)\\)");
|
|
||||||
private static final Pattern assigner = Pattern.compile("#FLWAssignFields\\(([\\w\\d_]+),\\s*([\\w\\d_.]+),\\s*([\\w\\d_.]+)\\)");
|
|
||||||
|
|
||||||
final String[] requiredStructs;
|
|
||||||
|
|
||||||
final String header;
|
|
||||||
final String body;
|
|
||||||
|
|
||||||
public ShaderTemplate(String[] requiredStructs, String templateSrc) {
|
|
||||||
this.requiredStructs = requiredStructs;
|
|
||||||
Matcher matcher = headerFinder.matcher(templateSrc);
|
|
||||||
|
|
||||||
if (!matcher.find()) {
|
|
||||||
throw new RuntimeException("Shader template must have a header and footer delimited by '" + delimiter + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.header = templateSrc.substring(0, matcher.start());
|
|
||||||
this.body = templateSrc.substring(matcher.end());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public String apply(Shader shader) {
|
|
||||||
|
|
||||||
return header +
|
|
||||||
shader.getSource() +
|
|
||||||
processBody(shader);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String processBody(Shader shader) {
|
|
||||||
String s = body;
|
|
||||||
|
|
||||||
List<String> missing = new ArrayList<>();
|
|
||||||
|
|
||||||
for (String name : requiredStructs) {
|
|
||||||
TaggedStruct struct = shader.getTag(name);
|
|
||||||
|
|
||||||
if (struct != null) {
|
|
||||||
s = s.replace(name, struct.name);
|
|
||||||
} else {
|
|
||||||
missing.add(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!missing.isEmpty()) {
|
|
||||||
String err = shader.name + " is missing: " + String.join(", ", missing);
|
|
||||||
throw new RuntimeException(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
s = fillPrefixes(shader, s);
|
|
||||||
s = fillAssigns(shader, s);
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String fillPrefixes(Shader shader, String s) {
|
|
||||||
Matcher prefixMatches = prefixer.matcher(s);
|
|
||||||
|
|
||||||
StringBuffer out = new StringBuffer();
|
|
||||||
while (prefixMatches.find()) {
|
|
||||||
String structName = prefixMatches.group(1);
|
|
||||||
String modifier = prefixMatches.group(2);
|
|
||||||
String prefix = prefixMatches.group(3);
|
|
||||||
|
|
||||||
TaggedStruct struct = shader.getStruct(structName);
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (TaggedField field : struct.fields) {
|
|
||||||
builder.append(modifier);
|
|
||||||
builder.append(' ');
|
|
||||||
builder.append(field.getType());
|
|
||||||
builder.append(' ');
|
|
||||||
builder.append(prefix);
|
|
||||||
builder.append(field.getName());
|
|
||||||
builder.append(";\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
prefixMatches.appendReplacement(out, builder.toString());
|
|
||||||
}
|
|
||||||
prefixMatches.appendTail(out);
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String fillAssigns(Shader shader, String s) {
|
|
||||||
Matcher assignMatches = assigner.matcher(s);
|
|
||||||
|
|
||||||
StringBuffer out = new StringBuffer();
|
|
||||||
while (assignMatches.find()) {
|
|
||||||
String structName = assignMatches.group(1);
|
|
||||||
String lhs = assignMatches.group(2);
|
|
||||||
String rhs = assignMatches.group(3);
|
|
||||||
|
|
||||||
TaggedStruct struct = shader.getStruct(structName);
|
|
||||||
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
for (TaggedField field : struct.fields) {
|
|
||||||
builder.append(lhs);
|
|
||||||
builder.append(field.getName());
|
|
||||||
builder.append(" = ");
|
|
||||||
builder.append(rhs);
|
|
||||||
builder.append(field.getName());
|
|
||||||
builder.append(";\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
assignMatches.appendReplacement(out, builder.toString());
|
|
||||||
}
|
|
||||||
assignMatches.appendTail(out);
|
|
||||||
return out.toString();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
public class ShaderTransformer {
|
|
||||||
|
|
||||||
private final LinkedList<IProcessingStage> stages = new LinkedList<>();
|
|
||||||
|
|
||||||
public ShaderTransformer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderTransformer pushStage(IProcessingStage stage) {
|
|
||||||
if (stage != null) {
|
|
||||||
stages.addLast(stage);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderTransformer popStage() {
|
|
||||||
stages.removeLast();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ShaderTransformer prependStage(IProcessingStage stage) {
|
|
||||||
if (stage != null) {
|
|
||||||
stages.addFirst(stage);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void transformSource(Shader shader) {
|
|
||||||
|
|
||||||
for (IProcessingStage stage : this.stages) {
|
|
||||||
stage.process(shader);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class TaggedField {
|
|
||||||
public static final Pattern fieldPattern = Pattern.compile("(?:#\\[([^\\n]*)]\\s*)?(\\S+)\\s*(\\S+);");
|
|
||||||
|
|
||||||
public String annotation;
|
|
||||||
public String name;
|
|
||||||
public String type;
|
|
||||||
public LayoutTag layout;
|
|
||||||
|
|
||||||
public TaggedField(Matcher fieldMatcher) {
|
|
||||||
annotation = fieldMatcher.group(1);
|
|
||||||
type = fieldMatcher.group(2);
|
|
||||||
name = fieldMatcher.group(3);
|
|
||||||
|
|
||||||
if (annotation != null) {
|
|
||||||
Matcher matcher = LayoutTag.pattern.matcher(annotation);
|
|
||||||
|
|
||||||
if (matcher.find()) {
|
|
||||||
layout = new LayoutTag(matcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAnnotation() {
|
|
||||||
return annotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "TaggedField{" +
|
|
||||||
"name='" + name + '\'' +
|
|
||||||
", type='" + type + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class TaggedStruct {
|
|
||||||
|
|
||||||
// https://regexr.com/5t207
|
|
||||||
static final Pattern taggedStruct = Pattern.compile("#\\[(\\w*)]\\s*struct\\s+([\\w\\d]*)\\s*\\{([\\w\\d \\t#\\[\\](),;\\n]*)}\\s*;");
|
|
||||||
|
|
||||||
int srcStart, srcEnd;
|
|
||||||
String source;
|
|
||||||
String tag;
|
|
||||||
String name;
|
|
||||||
String body;
|
|
||||||
|
|
||||||
List<TaggedField> fields = new ArrayList<>(4);
|
|
||||||
Map<String, String> fields2Types = new HashMap<>();
|
|
||||||
|
|
||||||
public TaggedStruct(Matcher foundMatcher) {
|
|
||||||
this.source = foundMatcher.group();
|
|
||||||
|
|
||||||
srcStart = foundMatcher.start();
|
|
||||||
srcEnd = foundMatcher.end();
|
|
||||||
|
|
||||||
tag = foundMatcher.group(1);
|
|
||||||
name = foundMatcher.group(2);
|
|
||||||
body = foundMatcher.group(3);
|
|
||||||
|
|
||||||
Matcher fielder = TaggedField.fieldPattern.matcher(body);
|
|
||||||
|
|
||||||
while (fielder.find()) {
|
|
||||||
fields.add(new TaggedField(fielder));
|
|
||||||
fields2Types.put(fielder.group(2), fielder.group(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addPrefixedAttributes(Program builder, String prefix) {
|
|
||||||
for (TaggedField field : fields) {
|
|
||||||
int attributeCount = TypeHelper.getAttributeCount(field.type);
|
|
||||||
|
|
||||||
builder.addAttribute(prefix + field.name, attributeCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.loading;
|
|
||||||
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class TypeHelper {
|
|
||||||
|
|
||||||
public static final Pattern vecType = Pattern.compile("^[biud]?vec([234])$");
|
|
||||||
public static final Pattern matType = Pattern.compile("^mat([234])(?:x([234]))?$");
|
|
||||||
|
|
||||||
public static int getElementCount(String type) {
|
|
||||||
Matcher vec = vecType.matcher(type);
|
|
||||||
if (vec.find()) return Integer.parseInt(vec.group(1));
|
|
||||||
|
|
||||||
Matcher mat = matType.matcher(type);
|
|
||||||
if (mat.find()) {
|
|
||||||
int n = Integer.parseInt(mat.group(1));
|
|
||||||
|
|
||||||
String m = mat.group(2);
|
|
||||||
|
|
||||||
if (m != null) return Integer.parseInt(m) * n;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getAttributeCount(String type) {
|
|
||||||
Matcher mat = matType.matcher(type);
|
|
||||||
if (mat.find()) {
|
|
||||||
return Integer.parseInt(mat.group(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
|
||||||
|
|
||||||
public class ArrayModelRenderer extends ModelRenderer {
|
|
||||||
|
|
||||||
protected GlVertexArray vao;
|
|
||||||
|
|
||||||
public ArrayModelRenderer(BufferedModel model) {
|
|
||||||
super(model);
|
|
||||||
vao = new GlVertexArray();
|
|
||||||
|
|
||||||
vao.bind();
|
|
||||||
model.setupState();
|
|
||||||
vao.unbind();
|
|
||||||
model.clearState();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void draw() {
|
|
||||||
if (!model.valid()) return;
|
|
||||||
|
|
||||||
vao.bind();
|
|
||||||
|
|
||||||
model.drawCall();
|
|
||||||
|
|
||||||
vao.unbind();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,93 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL20.glDrawArrays;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
|
||||||
import com.jozufozu.flywheel.util.AttribUtil;
|
|
||||||
|
|
||||||
public class BufferedModel {
|
|
||||||
|
|
||||||
protected final GlPrimitive primitiveMode;
|
|
||||||
protected final ByteBuffer data;
|
|
||||||
protected final VertexFormat format;
|
|
||||||
protected final int vertexCount;
|
|
||||||
protected GlBuffer vbo;
|
|
||||||
protected boolean deleted;
|
|
||||||
|
|
||||||
public BufferedModel(GlPrimitive primitiveMode, VertexFormat format, ByteBuffer data, int vertices) {
|
|
||||||
this.primitiveMode = primitiveMode;
|
|
||||||
this.data = data;
|
|
||||||
this.format = format;
|
|
||||||
this.vertexCount = vertices;
|
|
||||||
|
|
||||||
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
|
|
||||||
|
|
||||||
vbo.bind();
|
|
||||||
// allocate the buffer on the gpu
|
|
||||||
vbo.alloc(this.data.capacity());
|
|
||||||
|
|
||||||
// mirror it in system memory so we can write to it, and upload our model.
|
|
||||||
vbo.getBuffer(0, this.data.capacity())
|
|
||||||
.put(this.data)
|
|
||||||
.flush();
|
|
||||||
vbo.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
public VertexFormat getFormat() {
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getVertexCount() {
|
|
||||||
return vertexCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean valid() {
|
|
||||||
return vertexCount > 0 && !deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The VBO/VAO should be bound externally.
|
|
||||||
*/
|
|
||||||
public void setupState() {
|
|
||||||
vbo.bind();
|
|
||||||
AttribUtil.enableArrays(getAttributeCount());
|
|
||||||
format.vertexAttribPointers(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearState() {
|
|
||||||
AttribUtil.disableArrays(getAttributeCount());
|
|
||||||
vbo.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawCall() {
|
|
||||||
glDrawArrays(primitiveMode.glEnum, 0, vertexCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws many instances of this model, assuming the appropriate state is already bound.
|
|
||||||
*/
|
|
||||||
public void drawInstances(int instanceCount) {
|
|
||||||
if (!valid()) return;
|
|
||||||
|
|
||||||
Backend.getInstance().compat.drawInstanced.drawArraysInstanced(primitiveMode, 0, vertexCount, instanceCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (deleted) return;
|
|
||||||
|
|
||||||
deleted = true;
|
|
||||||
vbo.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getAttributeCount() {
|
|
||||||
return format.getAttributeCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
|
||||||
|
|
||||||
public class ElementBuffer {
|
|
||||||
|
|
||||||
private final GlBuffer buffer;
|
|
||||||
public final int elementCount;
|
|
||||||
public final GlNumericType eboIndexType;
|
|
||||||
|
|
||||||
public ElementBuffer(GlBuffer backing, int elementCount, GlNumericType indexType) {
|
|
||||||
this.buffer = backing;
|
|
||||||
this.eboIndexType = indexType;
|
|
||||||
this.elementCount = elementCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void bind() {
|
|
||||||
buffer.bind();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unbind() {
|
|
||||||
buffer.unbind();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlPrimitive;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|
||||||
import com.jozufozu.flywheel.core.QuadConverter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An indexed triangle model. Just what the driver ordered.
|
|
||||||
*
|
|
||||||
* <br><em>This should be favored over a normal BufferedModel.</em>
|
|
||||||
*/
|
|
||||||
public class IndexedModel extends BufferedModel {
|
|
||||||
|
|
||||||
protected ElementBuffer ebo;
|
|
||||||
|
|
||||||
public IndexedModel(VertexFormat modelFormat, ByteBuffer buf, int vertices, ElementBuffer ebo) {
|
|
||||||
super(GlPrimitive.TRIANGLES, modelFormat, buf, vertices);
|
|
||||||
|
|
||||||
this.ebo = ebo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IndexedModel fromSequentialQuads(VertexFormat modelFormat, ByteBuffer quads, int vertices) {
|
|
||||||
return new IndexedModel(modelFormat, quads, vertices, QuadConverter.getInstance().quads2Tris(vertices / 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupState() {
|
|
||||||
super.setupState();
|
|
||||||
ebo.bind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearState() {
|
|
||||||
super.clearState();
|
|
||||||
ebo.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawCall() {
|
|
||||||
GL20.glDrawElements(primitiveMode.glEnum, ebo.elementCount, ebo.eboIndexType.getGlEnum(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void drawInstances(int instanceCount) {
|
|
||||||
if (vertexCount <= 0 || deleted) return;
|
|
||||||
|
|
||||||
Backend.getInstance().compat.drawInstanced.drawElementsInstanced(primitiveMode, ebo.elementCount, ebo.eboIndexType, 0, instanceCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete() {
|
|
||||||
super.delete();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package com.jozufozu.flywheel.backend.model;
|
|
||||||
|
|
||||||
public class ModelRenderer {
|
|
||||||
|
|
||||||
protected BufferedModel model;
|
|
||||||
|
|
||||||
public ModelRenderer(BufferedModel model) {
|
|
||||||
this.model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders this model, checking first if there is anything to render.
|
|
||||||
*/
|
|
||||||
public void draw() {
|
|
||||||
if (!model.valid()) return;
|
|
||||||
|
|
||||||
model.setupState();
|
|
||||||
model.drawCall();
|
|
||||||
model.clearState();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
model.delete();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.minecraft.inventory.container.PlayerContainer;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is primarily for hacking entity textures into the block atlas.
|
|
||||||
*/
|
|
||||||
public class AtlasStitcher {
|
|
||||||
protected static final AtlasStitcher INSTANCE = new AtlasStitcher();
|
|
||||||
|
|
||||||
public static AtlasStitcher getInstance() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<StitchedSprite> sprites = new ArrayList<>();
|
|
||||||
|
|
||||||
public StitchedSprite get(ResourceLocation loc) {
|
|
||||||
StitchedSprite sprite = new StitchedSprite(loc);
|
|
||||||
|
|
||||||
sprites.add(sprite);
|
|
||||||
|
|
||||||
return sprite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTextureStitch(TextureStitchEvent.Pre event) {
|
|
||||||
if (!event.getMap()
|
|
||||||
.getId()
|
|
||||||
.equals(PlayerContainer.BLOCK_ATLAS_TEXTURE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
sprites.stream()
|
|
||||||
.map(StitchedSprite::getLoc)
|
|
||||||
.forEach(event::addSprite);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.SpecMetaRegistry;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldFog;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.FogStateProvider;
|
|
||||||
import com.jozufozu.flywheel.core.shader.gamestate.NormalDebugStateProvider;
|
|
||||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
|
|
||||||
@Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
|
|
||||||
public class Contexts {
|
|
||||||
|
|
||||||
public static WorldContext<WorldProgram> WORLD;
|
|
||||||
public static WorldContext<CrumblingProgram> CRUMBLING;
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void flwInit(GatherContextEvent event) {
|
|
||||||
Backend backend = event.getBackend();
|
|
||||||
|
|
||||||
SpecMetaRegistry.register(FogStateProvider.INSTANCE);
|
|
||||||
SpecMetaRegistry.register(NormalDebugStateProvider.INSTANCE);
|
|
||||||
|
|
||||||
SpecMetaRegistry.register(WorldFog.LINEAR);
|
|
||||||
SpecMetaRegistry.register(WorldFog.EXP2);
|
|
||||||
|
|
||||||
CRUMBLING = backend.register(new WorldContext<>(backend, CrumblingProgram::new)
|
|
||||||
.withName(Names.CRUMBLING)
|
|
||||||
.withBuiltin(ShaderType.FRAGMENT, Names.CRUMBLING, "/builtin.frag")
|
|
||||||
.withBuiltin(ShaderType.VERTEX, Names.CRUMBLING, "/builtin.vert"));
|
|
||||||
|
|
||||||
WORLD = backend.register(new WorldContext<>(backend, WorldProgram::new)
|
|
||||||
.withName(Names.WORLD)
|
|
||||||
.withBuiltin(ShaderType.FRAGMENT, Names.WORLD, "/builtin.frag")
|
|
||||||
.withBuiltin(ShaderType.VERTEX, Names.WORLD, "/builtin.vert"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Names {
|
|
||||||
public static final ResourceLocation CRUMBLING = new ResourceLocation(Flywheel.ID, "context/crumbling");
|
|
||||||
public static final ResourceLocation WORLD = new ResourceLocation(Flywheel.ID, "context/world");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
|
|
||||||
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
|
|
||||||
public class CrumblingInstanceManager extends TileInstanceManager {
|
|
||||||
public CrumblingInstanceManager() {
|
|
||||||
super(new MaterialManager<>(Contexts.CRUMBLING));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean shouldFrameUpdate(BlockPos worldPos, float lookX, float lookY, float lookZ, int cX, int cY, int cZ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL20.glUniform2f;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Program;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.extension.IProgramExtension;
|
|
||||||
|
|
||||||
public class CrumblingProgram extends WorldProgram {
|
|
||||||
protected final int uTextureScale;
|
|
||||||
protected int uCrumbling;
|
|
||||||
|
|
||||||
public CrumblingProgram(Program program, List<IProgramExtension> extensions) {
|
|
||||||
super(program, extensions);
|
|
||||||
|
|
||||||
uTextureScale = getUniformLocation("uTextureScale");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void registerSamplers() {
|
|
||||||
super.registerSamplers();
|
|
||||||
uCrumbling = setSamplerBinding("uCrumbling", 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextureScale(float x, float y) {
|
|
||||||
glUniform2f(uTextureScale, x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.CommonAttributes;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.MatrixAttributes;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.attrib.VertexFormat;
|
|
||||||
|
|
||||||
public class Formats {
|
|
||||||
public static final VertexFormat UNLIT_MODEL = VertexFormat.builder()
|
|
||||||
.addAttributes(CommonAttributes.VEC3, CommonAttributes.NORMAL, CommonAttributes.UV)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static final VertexFormat TRANSFORMED = litInstance()
|
|
||||||
.addAttributes(MatrixAttributes.MAT4,
|
|
||||||
MatrixAttributes.MAT3)
|
|
||||||
.build();
|
|
||||||
public static final VertexFormat ORIENTED = litInstance()
|
|
||||||
.addAttributes(CommonAttributes.VEC3, CommonAttributes.VEC3, CommonAttributes.QUATERNION)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
public static VertexFormat.Builder litInstance() {
|
|
||||||
return VertexFormat.builder()
|
|
||||||
.addAttributes(CommonAttributes.LIGHT, CommonAttributes.RGBA);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlVertexArray;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
|
||||||
|
|
||||||
import net.minecraftforge.common.util.Lazy;
|
|
||||||
|
|
||||||
public class FullscreenQuad {
|
|
||||||
|
|
||||||
public static final Lazy<FullscreenQuad> INSTANCE = Lazy.of(FullscreenQuad::new);
|
|
||||||
|
|
||||||
private static final float[] vertices = {
|
|
||||||
// pos // tex
|
|
||||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
|
||||||
1.0f, 1.0f, 1.0f, 1.0f,
|
|
||||||
-1.0f, 1.0f, 0.0f, 1.0f,
|
|
||||||
|
|
||||||
-1.0f, -1.0f, 0.0f, 0.0f,
|
|
||||||
1.0f, -1.0f, 1.0f, 0.0f,
|
|
||||||
1.0f, 1.0f, 1.0f, 1.0f
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final int bufferSize = vertices.length * 4;
|
|
||||||
|
|
||||||
private final GlVertexArray vao;
|
|
||||||
private final GlBuffer vbo;
|
|
||||||
|
|
||||||
private FullscreenQuad() {
|
|
||||||
vbo = new GlBuffer(GlBufferType.ARRAY_BUFFER);
|
|
||||||
vbo.bind();
|
|
||||||
vbo.alloc(bufferSize);
|
|
||||||
vbo.getBuffer(0, bufferSize)
|
|
||||||
.putFloatArray(vertices)
|
|
||||||
.flush();
|
|
||||||
|
|
||||||
vao = new GlVertexArray();
|
|
||||||
vao.bind();
|
|
||||||
|
|
||||||
GL20.glEnableVertexAttribArray(0);
|
|
||||||
|
|
||||||
GL20.glVertexAttribPointer(0, 4, GlNumericType.FLOAT.getGlEnum(), false, 4 * 4, 0);
|
|
||||||
|
|
||||||
vao.unbind();
|
|
||||||
vbo.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void draw() {
|
|
||||||
vao.bind();
|
|
||||||
GL20.glDrawArrays(GL20.GL_TRIANGLES, 0, 6);
|
|
||||||
vao.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
vao.delete();
|
|
||||||
vbo.delete();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialSpec;
|
|
||||||
import com.jozufozu.flywheel.core.materials.ModelData;
|
|
||||||
import com.jozufozu.flywheel.core.materials.OrientedData;
|
|
||||||
import com.jozufozu.flywheel.event.GatherContextEvent;
|
|
||||||
import com.simibubi.create.foundation.render.AllMaterialSpecs;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
|
|
||||||
@Mod.EventBusSubscriber(value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
|
|
||||||
public class Materials {
|
|
||||||
public static final MaterialSpec<OrientedData> ORIENTED = AllMaterialSpecs.register(new MaterialSpec<>(Locations.ORIENTED, Programs.ORIENTED, Formats.UNLIT_MODEL, Formats.ORIENTED, OrientedData::new));
|
|
||||||
public static final MaterialSpec<ModelData> TRANSFORMED = AllMaterialSpecs.register(new MaterialSpec<>(Locations.MODEL, Programs.TRANSFORMED, Formats.UNLIT_MODEL, Formats.TRANSFORMED, ModelData::new));
|
|
||||||
|
|
||||||
public static <D extends InstanceData> MaterialSpec<D> register(MaterialSpec<D> spec) {
|
|
||||||
return Backend.getInstance().register(spec);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void flwInit(GatherContextEvent event) {
|
|
||||||
register(ORIENTED);
|
|
||||||
register(TRANSFORMED);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Locations {
|
|
||||||
public static final ResourceLocation MODEL = new ResourceLocation("create", "model");
|
|
||||||
public static final ResourceLocation ORIENTED = new ResourceLocation("create", "oriented");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,56 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import net.minecraft.client.renderer.model.IBakedModel;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraftforge.client.event.ModelBakeEvent;
|
|
||||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
|
||||||
import net.minecraftforge.client.model.ModelLoader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper class for loading and accessing json models.
|
|
||||||
* <p>
|
|
||||||
* Creating a PartialModel will make the associated modelLocation automatically load.
|
|
||||||
* As such, PartialModels must be initialized at or before {@link ModelRegistryEvent}.
|
|
||||||
* Once {@link ModelBakeEvent} finishes, all PartialModels (with valid modelLocations)
|
|
||||||
* will have their bakedModel fields populated.
|
|
||||||
* <p>
|
|
||||||
* Attempting to create a PartialModel after ModelRegistryEvent will cause an error.
|
|
||||||
*/
|
|
||||||
public class PartialModel {
|
|
||||||
|
|
||||||
private static boolean tooLate = false;
|
|
||||||
private static final List<PartialModel> all = new ArrayList<>();
|
|
||||||
|
|
||||||
protected final ResourceLocation modelLocation;
|
|
||||||
protected IBakedModel bakedModel;
|
|
||||||
|
|
||||||
public PartialModel(ResourceLocation modelLocation) {
|
|
||||||
|
|
||||||
if (tooLate) throw new RuntimeException("PartialModel '" + modelLocation + "' loaded after ModelRegistryEvent");
|
|
||||||
|
|
||||||
this.modelLocation = modelLocation;
|
|
||||||
all.add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onModelRegistry(ModelRegistryEvent event) {
|
|
||||||
for (PartialModel partial : all)
|
|
||||||
ModelLoader.addSpecialModel(partial.modelLocation);
|
|
||||||
|
|
||||||
tooLate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onModelBake(ModelBakeEvent event) {
|
|
||||||
Map<ResourceLocation, IBakedModel> modelRegistry = event.getModelRegistry();
|
|
||||||
for (PartialModel partial : all)
|
|
||||||
partial.bakedModel = modelRegistry.get(partial.modelLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IBakedModel get() {
|
|
||||||
return bakedModel;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class Programs {
|
|
||||||
public static final ResourceLocation TRANSFORMED = new ResourceLocation(Flywheel.ID, "model");
|
|
||||||
public static final ResourceLocation ORIENTED = new ResourceLocation(Flywheel.ID, "oriented");
|
|
||||||
}
|
|
|
@ -1,174 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryStack;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.GlNumericType;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.GlBufferType;
|
|
||||||
import com.jozufozu.flywheel.backend.model.ElementBuffer;
|
|
||||||
import com.jozufozu.flywheel.event.ReloadRenderersEvent;
|
|
||||||
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
|
||||||
import net.minecraftforge.eventbus.api.EventPriority;
|
|
||||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
||||||
import net.minecraftforge.fml.common.Mod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A class to manage EBOs that index quads as triangles.
|
|
||||||
*/
|
|
||||||
@Mod.EventBusSubscriber(value = Dist.CLIENT)
|
|
||||||
public class QuadConverter {
|
|
||||||
|
|
||||||
public static final int STARTING_CAPACITY = 42;
|
|
||||||
|
|
||||||
private static QuadConverter INSTANCE;
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public static QuadConverter getInstance() {
|
|
||||||
if (INSTANCE == null) {
|
|
||||||
INSTANCE = new QuadConverter(STARTING_CAPACITY); // 255 / 6 = 42
|
|
||||||
}
|
|
||||||
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static QuadConverter getNullable() {
|
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<GlNumericType, GlBuffer> ebos;
|
|
||||||
int[] capacities;
|
|
||||||
|
|
||||||
public QuadConverter(int initialCapacity) {
|
|
||||||
this.ebos = new EnumMap<>(GlNumericType.class);
|
|
||||||
initCapacities();
|
|
||||||
|
|
||||||
fillBuffer(initialCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ElementBuffer quads2Tris(int quads) {
|
|
||||||
int indexCount = quads * 6;
|
|
||||||
GlNumericType type = getSmallestIndexType(indexCount);
|
|
||||||
|
|
||||||
if (quads > getCapacity(type)) {
|
|
||||||
fillBuffer(quads, indexCount, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ElementBuffer(getBuffer(type), indexCount, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initCapacities() {
|
|
||||||
this.capacities = new int[GlNumericType.values().length];
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getCapacity(GlNumericType type) {
|
|
||||||
return capacities[type.ordinal()];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCapacity(GlNumericType type, int capacity) {
|
|
||||||
if (getCapacity(type) < capacity) {
|
|
||||||
capacities[type.ordinal()] = capacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void free() {
|
|
||||||
ebos.values().forEach(GlBuffer::delete);
|
|
||||||
ebos.clear();
|
|
||||||
initCapacities();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillBuffer(int quads) {
|
|
||||||
int indexCount = quads * 6;
|
|
||||||
|
|
||||||
fillBuffer(quads, indexCount, getSmallestIndexType(indexCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillBuffer(int quads, int indexCount, GlNumericType type) {
|
|
||||||
MemoryStack stack = MemoryStack.stackPush();
|
|
||||||
int bytes = indexCount * type.getByteWidth();
|
|
||||||
|
|
||||||
ByteBuffer indices;
|
|
||||||
if (bytes > stack.getSize()) {
|
|
||||||
indices = MemoryUtil.memAlloc(bytes); // not enough space on the preallocated stack
|
|
||||||
} else {
|
|
||||||
stack.push();
|
|
||||||
indices = stack.malloc(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
indices.order(ByteOrder.nativeOrder());
|
|
||||||
|
|
||||||
fillBuffer(indices, type, quads);
|
|
||||||
|
|
||||||
GlBuffer buffer = getBuffer(type);
|
|
||||||
|
|
||||||
buffer.bind();
|
|
||||||
buffer.upload(indices);
|
|
||||||
buffer.unbind();
|
|
||||||
|
|
||||||
if (bytes > stack.getSize()) {
|
|
||||||
MemoryUtil.memFree(indices);
|
|
||||||
} else {
|
|
||||||
stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCapacity(type, quads);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillBuffer(ByteBuffer indices, GlNumericType type, int quads) {
|
|
||||||
for (int i = 0, max = 4 * quads; i < max; i += 4) {
|
|
||||||
// triangle a
|
|
||||||
type.castAndBuffer(indices, i);
|
|
||||||
type.castAndBuffer(indices, i + 1);
|
|
||||||
type.castAndBuffer(indices, i + 2);
|
|
||||||
// triangle b
|
|
||||||
type.castAndBuffer(indices, i);
|
|
||||||
type.castAndBuffer(indices, i + 2);
|
|
||||||
type.castAndBuffer(indices, i + 3);
|
|
||||||
}
|
|
||||||
indices.flip();
|
|
||||||
}
|
|
||||||
|
|
||||||
private GlBuffer getBuffer(GlNumericType type) {
|
|
||||||
return ebos.computeIfAbsent(type, $ -> new GlBuffer(GlBufferType.ELEMENT_ARRAY_BUFFER));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given the needed number of indices, what is the smallest bit width type that can index everything? <br>
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* | indexCount | type |
|
|
||||||
* |--------------|-------|
|
|
||||||
* | [0, 255) | byte |
|
|
||||||
* | [256, 65536) | short |
|
|
||||||
* | [65537, ) | int |
|
|
||||||
* </pre>
|
|
||||||
*/
|
|
||||||
private static GlNumericType getSmallestIndexType(int indexCount) {
|
|
||||||
indexCount = indexCount >>> 8;
|
|
||||||
if (indexCount == 0) {
|
|
||||||
return GlNumericType.UBYTE;
|
|
||||||
}
|
|
||||||
indexCount = indexCount >>> 8;
|
|
||||||
if (indexCount == 0) {
|
|
||||||
return GlNumericType.USHORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return GlNumericType.UINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure this gets reset first so it has a chance to repopulate
|
|
||||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
|
||||||
public static void onRendererReload(ReloadRenderersEvent event) {
|
|
||||||
if (INSTANCE != null) INSTANCE.free();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.inventory.container.PlayerContainer;
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public class StitchedSprite {
|
|
||||||
|
|
||||||
private final ResourceLocation loc;
|
|
||||||
|
|
||||||
TextureAtlasSprite sprite;
|
|
||||||
|
|
||||||
public StitchedSprite(ResourceLocation loc) {
|
|
||||||
this.loc = loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResourceLocation getLoc() {
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TextureAtlasSprite getSprite() {
|
|
||||||
if (sprite == null) {
|
|
||||||
sprite = Minecraft.getInstance()
|
|
||||||
.getSpriteAtlas(PlayerContainer.BLOCK_ATLAS_TEXTURE)
|
|
||||||
.apply(loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sprite;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,165 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core;
|
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.function.Supplier;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.Backend;
|
|
||||||
import com.jozufozu.flywheel.backend.ResourceUtil;
|
|
||||||
import com.jozufozu.flywheel.backend.ShaderContext;
|
|
||||||
import com.jozufozu.flywheel.backend.ShaderSources;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.ShaderType;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialManager;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.MaterialSpec;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.InstancedArraysTemplate;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Program;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.ProgramTemplate;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Shader;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.ShaderLoadingException;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.ShaderTransformer;
|
|
||||||
import com.jozufozu.flywheel.core.shader.ExtensibleGlProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.IMultiProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.StateSensitiveMultiProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.WorldProgram;
|
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraft.world.IWorld;
|
|
||||||
|
|
||||||
public class WorldContext<P extends WorldProgram> extends ShaderContext<P> {
|
|
||||||
|
|
||||||
private static final String declaration = "#flwbuiltins";
|
|
||||||
private static final Pattern builtinPattern = Pattern.compile(declaration);
|
|
||||||
|
|
||||||
protected ResourceLocation name;
|
|
||||||
protected Supplier<Stream<ResourceLocation>> specStream;
|
|
||||||
protected TemplateFactory templateFactory;
|
|
||||||
|
|
||||||
private final WorldAttached<MaterialManager<P>> materialManager = new WorldAttached<>($ -> new MaterialManager<>(this));
|
|
||||||
|
|
||||||
private final Map<ShaderType, ResourceLocation> builtins = new EnumMap<>(ShaderType.class);
|
|
||||||
private final Map<ShaderType, String> builtinSources = new EnumMap<>(ShaderType.class);
|
|
||||||
|
|
||||||
private final ExtensibleGlProgram.Factory<P> factory;
|
|
||||||
|
|
||||||
public WorldContext(Backend backend, ExtensibleGlProgram.Factory<P> factory) {
|
|
||||||
super(backend);
|
|
||||||
this.factory = factory;
|
|
||||||
|
|
||||||
specStream = () -> backend.allMaterials()
|
|
||||||
.stream()
|
|
||||||
.map(MaterialSpec::getProgramName);
|
|
||||||
|
|
||||||
templateFactory = InstancedArraysTemplate::new;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldContext<P> withName(ResourceLocation name) {
|
|
||||||
this.name = name;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldContext<P> withBuiltin(ShaderType shaderType, ResourceLocation folder, String file) {
|
|
||||||
return withBuiltin(shaderType, ResourceUtil.subPath(folder, file));
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldContext<P> withBuiltin(ShaderType shaderType, ResourceLocation file) {
|
|
||||||
builtins.put(shaderType, file);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MaterialManager<P> getMaterialManager(IWorld world) {
|
|
||||||
return materialManager.get(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldContext<P> withSpecStream(Supplier<Stream<ResourceLocation>> specStream) {
|
|
||||||
this.specStream = specStream;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public WorldContext<P> withTemplateFactory(TemplateFactory templateFactory) {
|
|
||||||
this.templateFactory = templateFactory;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ShaderTransformer transformer;
|
|
||||||
protected ProgramTemplate template;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void load() {
|
|
||||||
programs.values().forEach(IMultiProgram::delete);
|
|
||||||
programs.clear();
|
|
||||||
|
|
||||||
Backend.log.info("Loading context '{}'", name);
|
|
||||||
|
|
||||||
try {
|
|
||||||
builtins.forEach((type, resourceLocation) -> builtinSources.put(type, backend.sources.getShaderSource(resourceLocation)));
|
|
||||||
} catch (ShaderLoadingException e) {
|
|
||||||
backend.sources.notifyError();
|
|
||||||
|
|
||||||
Backend.log.error(String.format("Could not find builtin: %s", e.getMessage()));
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
template = templateFactory.create(backend.sources);
|
|
||||||
transformer = new ShaderTransformer()
|
|
||||||
.pushStage(this::injectBuiltins)
|
|
||||||
.pushStage(Shader::processIncludes)
|
|
||||||
.pushStage(template)
|
|
||||||
.pushStage(Shader::processIncludes);
|
|
||||||
|
|
||||||
specStream.get()
|
|
||||||
.map(backend::getSpec)
|
|
||||||
.forEach(spec -> {
|
|
||||||
|
|
||||||
try {
|
|
||||||
programs.put(spec.name, new StateSensitiveMultiProgram<>(factory, this, spec));
|
|
||||||
|
|
||||||
Backend.log.debug("Loaded program {}", spec.name);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Backend.log.error("Program '{}': {}", spec.name, e);
|
|
||||||
backend.sources.notifyError();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void delete() {
|
|
||||||
super.delete();
|
|
||||||
|
|
||||||
materialManager.forEach(MaterialManager::delete);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Shader getSource(ShaderType type, ResourceLocation name) {
|
|
||||||
Shader source = super.getSource(type, name);
|
|
||||||
transformer.transformSource(source);
|
|
||||||
return source;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Program link(Program program) {
|
|
||||||
template.attachAttributes(program);
|
|
||||||
|
|
||||||
return super.link(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace #flwbuiltins with whatever expansion this context provides for the given shader.
|
|
||||||
*/
|
|
||||||
public void injectBuiltins(Shader shader) {
|
|
||||||
Matcher matcher = builtinPattern.matcher(shader.getSource());
|
|
||||||
|
|
||||||
if (matcher.find())
|
|
||||||
shader.setSource(matcher.replaceFirst(builtinSources.get(shader.type)));
|
|
||||||
else
|
|
||||||
throw new ShaderLoadingException(String.format("%s is missing %s, cannot use in World Context", shader.type.name, declaration));
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface TemplateFactory {
|
|
||||||
ProgramTemplate create(ShaderSources loader);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.instancing;
|
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Instancer;
|
|
||||||
|
|
||||||
public class ConditionalInstance<D extends InstanceData> {
|
|
||||||
|
|
||||||
final Instancer<D> model;
|
|
||||||
ICondition condition;
|
|
||||||
|
|
||||||
Consumer<D> setupFunc;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private D instance;
|
|
||||||
|
|
||||||
public ConditionalInstance(Instancer<D> model) {
|
|
||||||
this.model = model;
|
|
||||||
this.condition = () -> true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConditionalInstance<D> withSetupFunc(Consumer<D> setupFunc) {
|
|
||||||
this.setupFunc = setupFunc;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConditionalInstance<D> withCondition(ICondition condition) {
|
|
||||||
this.condition = condition;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConditionalInstance<D> update() {
|
|
||||||
boolean shouldShow = condition.shouldShow();
|
|
||||||
if (shouldShow && instance == null) {
|
|
||||||
instance = model.createInstance();
|
|
||||||
if (setupFunc != null) setupFunc.accept(instance);
|
|
||||||
} else if (!shouldShow && instance != null) {
|
|
||||||
instance.delete();
|
|
||||||
instance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<D> get() {
|
|
||||||
return Optional.ofNullable(instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (instance != null) instance.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface ICondition {
|
|
||||||
boolean shouldShow();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,84 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.instancing;
|
|
||||||
|
|
||||||
import java.util.AbstractCollection;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Instancer;
|
|
||||||
|
|
||||||
public class GroupInstance<D extends InstanceData> extends AbstractCollection<D> {
|
|
||||||
|
|
||||||
final Instancer<D> model;
|
|
||||||
final List<D> backing;
|
|
||||||
|
|
||||||
public GroupInstance(Instancer<D> model) {
|
|
||||||
this.model = model;
|
|
||||||
|
|
||||||
this.backing = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public GroupInstance(Instancer<D> model, int size) {
|
|
||||||
this.model = model;
|
|
||||||
|
|
||||||
this.backing = new ArrayList<>(size);
|
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
addInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param count
|
|
||||||
* @return True if the number of elements changed.
|
|
||||||
*/
|
|
||||||
public boolean resize(int count) {
|
|
||||||
int size = size();
|
|
||||||
if (count == size) return false;
|
|
||||||
|
|
||||||
if (count <= 0) {
|
|
||||||
clear();
|
|
||||||
return size > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > size) {
|
|
||||||
for (int i = size; i < count; i++) {
|
|
||||||
addInstance();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
List<D> unnecessary = backing.subList(count, size);
|
|
||||||
unnecessary.forEach(InstanceData::delete);
|
|
||||||
unnecessary.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InstanceData addInstance() {
|
|
||||||
D instance = model.createInstance();
|
|
||||||
backing.add(instance);
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public D get(int index) {
|
|
||||||
return backing.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Iterator<D> iterator() {
|
|
||||||
return backing.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return backing.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
backing.forEach(InstanceData::delete);
|
|
||||||
backing.clear();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.instancing;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Instancer;
|
|
||||||
|
|
||||||
public class SelectInstance<D extends InstanceData> {
|
|
||||||
|
|
||||||
final List<Instancer<D>> models;
|
|
||||||
|
|
||||||
ModelSelector selector;
|
|
||||||
|
|
||||||
private int last = -1;
|
|
||||||
@Nullable
|
|
||||||
private D current;
|
|
||||||
|
|
||||||
public SelectInstance(ModelSelector selector) {
|
|
||||||
this.models = new ArrayList<>();
|
|
||||||
this.selector = selector;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectInstance<D> addModel(Instancer<D> model) {
|
|
||||||
models.add(model);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SelectInstance<D> update() {
|
|
||||||
int i = selector.modelIndexToShow();
|
|
||||||
|
|
||||||
if (i < 0 || i >= models.size()) {
|
|
||||||
if (current != null) {
|
|
||||||
current.delete();
|
|
||||||
current = null;
|
|
||||||
}
|
|
||||||
} else if (i != last) {
|
|
||||||
if (current != null) current.delete();
|
|
||||||
|
|
||||||
current = models.get(i).createInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
last = i;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<D> get() {
|
|
||||||
return Optional.ofNullable(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
if (current != null) current.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ModelSelector {
|
|
||||||
int modelIndexToShow();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.materials;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Instancer;
|
|
||||||
|
|
||||||
public abstract class BasicData extends InstanceData implements IFlatLight<BasicData> {
|
|
||||||
|
|
||||||
protected byte blockLight;
|
|
||||||
protected byte skyLight;
|
|
||||||
|
|
||||||
protected byte r = (byte) 0xFF;
|
|
||||||
protected byte g = (byte) 0xFF;
|
|
||||||
protected byte b = (byte) 0xFF;
|
|
||||||
protected byte a = (byte) 0xFF;
|
|
||||||
|
|
||||||
public BasicData(Instancer<?> owner) {
|
|
||||||
super(owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BasicData setBlockLight(int blockLight) {
|
|
||||||
this.blockLight = (byte) (blockLight << 4);
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BasicData setSkyLight(int skyLight) {
|
|
||||||
this.skyLight = (byte) (skyLight << 4);
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicData setColor(int color) {
|
|
||||||
return setColor(color, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicData setColor(int color, boolean alpha) {
|
|
||||||
byte r = (byte) ((color >> 16) & 0xFF);
|
|
||||||
byte g = (byte) ((color >> 8) & 0xFF);
|
|
||||||
byte b = (byte) (color & 0xFF);
|
|
||||||
|
|
||||||
if (alpha) {
|
|
||||||
byte a = (byte) ((color >> 24) & 0xFF);
|
|
||||||
return setColor(r, g, b, a);
|
|
||||||
} else {
|
|
||||||
return setColor(r, g, b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicData setColor(int r, int g, int b) {
|
|
||||||
return setColor((byte) r, (byte) g, (byte) b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicData setColor(byte r, byte g, byte b) {
|
|
||||||
this.r = r;
|
|
||||||
this.g = g;
|
|
||||||
this.b = b;
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BasicData setColor(byte r, byte g, byte b, byte a) {
|
|
||||||
this.r = r;
|
|
||||||
this.g = g;
|
|
||||||
this.b = b;
|
|
||||||
this.a = a;
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(MappedBuffer buf) {
|
|
||||||
buf.putByteArray(new byte[]{blockLight, skyLight, r, g, b, a});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.materials;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstanceData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface that implementors of {@link InstanceData} should also implement
|
|
||||||
* if they wish to make use of Flywheel's provided light update methods.
|
|
||||||
* <p>
|
|
||||||
* This only covers flat lighting, smooth lighting is still TODO.
|
|
||||||
*
|
|
||||||
* @param <D> The name of the class that implements this interface.
|
|
||||||
*/
|
|
||||||
public interface IFlatLight<D extends InstanceData & IFlatLight<D>> {
|
|
||||||
/**
|
|
||||||
* @param blockLight An integer in the range [0, 15] representing the
|
|
||||||
* amount of block light this instance should receive.
|
|
||||||
* @return <code>this</code>
|
|
||||||
*/
|
|
||||||
D setBlockLight(int blockLight);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param skyLight An integer in the range [0, 15] representing the
|
|
||||||
* amount of sky light this instance should receive.
|
|
||||||
* @return <code>this</code>
|
|
||||||
*/
|
|
||||||
D setSkyLight(int skyLight);
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.materials;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Instancer;
|
|
||||||
import com.jozufozu.flywheel.util.RenderUtil;
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
|
||||||
|
|
||||||
public class ModelData extends BasicData {
|
|
||||||
private static final float[] empty = new float[25];
|
|
||||||
|
|
||||||
private float[] matrices = empty;
|
|
||||||
|
|
||||||
public ModelData(Instancer<?> owner) {
|
|
||||||
super(owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModelData setTransform(MatrixStack stack) {
|
|
||||||
matrices = RenderUtil.writeMatrixStack(stack);
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(MappedBuffer buf) {
|
|
||||||
super.write(buf);
|
|
||||||
buf.putFloatArray(matrices);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,99 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.materials;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.buffer.MappedBuffer;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.Instancer;
|
|
||||||
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.vector.Quaternion;
|
|
||||||
import net.minecraft.util.math.vector.Vector3d;
|
|
||||||
import net.minecraft.util.math.vector.Vector3f;
|
|
||||||
|
|
||||||
public class OrientedData extends BasicData {
|
|
||||||
|
|
||||||
private float posX;
|
|
||||||
private float posY;
|
|
||||||
private float posZ;
|
|
||||||
private float pivotX = 0.5f;
|
|
||||||
private float pivotY = 0.5f;
|
|
||||||
private float pivotZ = 0.5f;
|
|
||||||
private float qX;
|
|
||||||
private float qY;
|
|
||||||
private float qZ;
|
|
||||||
private float qW;
|
|
||||||
|
|
||||||
public OrientedData(Instancer<?> owner) {
|
|
||||||
super(owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setPosition(BlockPos pos) {
|
|
||||||
return setPosition(pos.getX(), pos.getY(), pos.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setPosition(Vector3f pos) {
|
|
||||||
return setPosition(pos.getX(), pos.getY(), pos.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setPosition(float x, float y, float z) {
|
|
||||||
this.posX = x;
|
|
||||||
this.posY = y;
|
|
||||||
this.posZ = z;
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData nudge(float x, float y, float z) {
|
|
||||||
this.posX += x;
|
|
||||||
this.posY += y;
|
|
||||||
this.posZ += z;
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setPivot(Vector3f pos) {
|
|
||||||
return setPosition(pos.getX(), pos.getY(), pos.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setPivot(Vector3d pos) {
|
|
||||||
return setPosition((float) pos.getX(), (float) pos.getY(), (float) pos.getZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setPivot(float x, float y, float z) {
|
|
||||||
this.pivotX = x;
|
|
||||||
this.pivotY = y;
|
|
||||||
this.pivotZ = z;
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setRotation(Quaternion q) {
|
|
||||||
return setRotation(q.getX(), q.getY(), q.getZ(), q.getW());
|
|
||||||
}
|
|
||||||
|
|
||||||
public OrientedData setRotation(float x, float y, float z, float w) {
|
|
||||||
this.qX = x;
|
|
||||||
this.qY = y;
|
|
||||||
this.qZ = z;
|
|
||||||
this.qW = w;
|
|
||||||
markDirty();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(MappedBuffer buf) {
|
|
||||||
super.write(buf);
|
|
||||||
|
|
||||||
buf.putFloatArray(new float[]{
|
|
||||||
posX,
|
|
||||||
posY,
|
|
||||||
posZ,
|
|
||||||
pivotX,
|
|
||||||
pivotY,
|
|
||||||
pivotZ,
|
|
||||||
qX,
|
|
||||||
qY,
|
|
||||||
qZ,
|
|
||||||
qW
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
import com.jozufozu.flywheel.backend.loading.Program;
|
|
||||||
import com.jozufozu.flywheel.core.shader.extension.IExtensionInstance;
|
|
||||||
import com.jozufozu.flywheel.core.shader.extension.IProgramExtension;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 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
|
|
||||||
* per-program, this also allows for same extra specialization within a
|
|
||||||
* {@link com.jozufozu.flywheel.backend.ShaderContext ShaderContext}.
|
|
||||||
*/
|
|
||||||
public class ExtensibleGlProgram extends GlProgram {
|
|
||||||
|
|
||||||
protected final List<IExtensionInstance> extensions;
|
|
||||||
|
|
||||||
public ExtensibleGlProgram(Program program, @Nullable List<IProgramExtension> extensions) {
|
|
||||||
super(program);
|
|
||||||
|
|
||||||
if (extensions != null) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bind() {
|
|
||||||
super.bind();
|
|
||||||
|
|
||||||
extensions.forEach(IExtensionInstance::bind);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "ExtensibleGlProgram{" +
|
|
||||||
"name=" + name +
|
|
||||||
", extensions=" + extensions +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory interface to create {@link GlProgram}s parameterized by a list of extensions. This doesn't necessarily
|
|
||||||
* have to return an {@link ExtensibleGlProgram} if implementors want more flexibility for whatever reason.
|
|
||||||
*/
|
|
||||||
public interface Factory<P extends GlProgram> {
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
P create(Program program, @Nullable List<IProgramExtension> extensions);
|
|
||||||
|
|
||||||
default P create(Program program) {
|
|
||||||
return create(program, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package com.jozufozu.flywheel.core.shader;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL20;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
|
||||||
import com.jozufozu.flywheel.backend.gl.shader.GlProgram;
|
|
||||||
import com.jozufozu.flywheel.core.shader.extension.IExtensionInstance;
|
|
||||||
|
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
|
|
||||||
public abstract class FogMode {
|
|
||||||
|
|
||||||
public static class Linear implements IExtensionInstance {
|
|
||||||
|
|
||||||
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "fog_linear");
|
|
||||||
|
|
||||||
private final int uFogColor;
|
|
||||||
private final int uFogRange;
|
|
||||||
|
|
||||||
public Linear(GlProgram program) {
|
|
||||||
this.uFogColor = program.getUniformLocation("uFogColor");
|
|
||||||
this.uFogRange = program.getUniformLocation("uFogRange");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bind() {
|
|
||||||
GL20.glUniform2f(uFogRange, GlFog.getFogStart(), GlFog.getFogEnd());
|
|
||||||
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation name() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Exp2 implements IExtensionInstance {
|
|
||||||
|
|
||||||
public static final ResourceLocation NAME = new ResourceLocation(Flywheel.ID, "fog_exp2");
|
|
||||||
|
|
||||||
private final int uFogColor;
|
|
||||||
private final int uFogDensity;
|
|
||||||
|
|
||||||
public Exp2(GlProgram program) {
|
|
||||||
this.uFogColor = program.getUniformLocation("uFogColor");
|
|
||||||
this.uFogDensity = program.getUniformLocation("uFogDensity");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bind() {
|
|
||||||
GL20.glUniform1f(uFogDensity, GlFog.getFogDensity());
|
|
||||||
GL20.glUniform4fv(uFogColor, GlFog.FOG_COLOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResourceLocation name() {
|
|
||||||
return NAME;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue