Instance Refactor II

This commit is contained in:
PepperCode1 2023-04-05 18:03:25 -07:00
parent e6248f502e
commit 6002bfafd9
70 changed files with 972 additions and 1020 deletions

View file

@ -3,17 +3,17 @@ package com.jozufozu.flywheel;
import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.slf4j.Logger; import org.slf4j.Logger;
import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.backend.Loader; import com.jozufozu.flywheel.backend.Loader;
import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer; import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.config.BackendArgument; import com.jozufozu.flywheel.config.BackendArgument;
import com.jozufozu.flywheel.config.FlwCommands; import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.handler.EntityWorldHandler; import com.jozufozu.flywheel.handler.EntityWorldHandler;
import com.jozufozu.flywheel.handler.ForgeEvents; import com.jozufozu.flywheel.handler.ForgeEvents;
import com.jozufozu.flywheel.impl.BackendManagerImpl;
import com.jozufozu.flywheel.impl.IdRegistryImpl; import com.jozufozu.flywheel.impl.IdRegistryImpl;
import com.jozufozu.flywheel.impl.RegistryImpl; import com.jozufozu.flywheel.impl.RegistryImpl;
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.lib.backend.Backends; import com.jozufozu.flywheel.lib.backend.Backends;
import com.jozufozu.flywheel.lib.context.Contexts; import com.jozufozu.flywheel.lib.context.Contexts;
import com.jozufozu.flywheel.lib.format.Formats; import com.jozufozu.flywheel.lib.format.Formats;
@ -115,7 +115,7 @@ public class Flywheel {
VanillaInstances.init(); VanillaInstances.init();
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManager::getBackendNameForCrashReport); CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendNameForCrashReport);
// https://github.com/Jozufozu/Flywheel/issues/69 // https://github.com/Jozufozu/Flywheel/issues/69
// Weird issue with accessor loading. // Weird issue with accessor loading.
@ -129,7 +129,7 @@ public class Flywheel {
RegistryImpl.freezeAll(); RegistryImpl.freezeAll();
IdRegistryImpl.freezeAll(); IdRegistryImpl.freezeAll();
ArgumentTypes.register(rl("backend").toString(), BackendArgument.class, new EmptyArgumentSerializer<>(BackendArgument::getInstance)); ArgumentTypes.register(rl("backend").toString(), BackendArgument.class, new EmptyArgumentSerializer<>(() -> BackendArgument.INSTANCE));
} }
public static ArtifactVersion getVersion() { public static ArtifactVersion getVersion() {

View file

@ -7,6 +7,7 @@ import com.jozufozu.flywheel.api.registry.IdRegistry;
import com.jozufozu.flywheel.impl.IdRegistryImpl; import com.jozufozu.flywheel.impl.IdRegistryImpl;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.world.level.LevelAccessor;
public interface Backend { public interface Backend {
static IdRegistry<Backend> REGISTRY = IdRegistryImpl.create(); static IdRegistry<Backend> REGISTRY = IdRegistryImpl.create();
@ -19,7 +20,7 @@ public interface Backend {
/** /**
* Create a new engine instance. * Create a new engine instance.
*/ */
Engine createEngine(); Engine createEngine(LevelAccessor level);
/** /**
* Get a fallback backend in case this backend is not supported. * Get a fallback backend in case this backend is not supported.

View file

@ -13,13 +13,6 @@ public final class BackendManager {
return BackendManagerImpl.getBackend(); return BackendManagerImpl.getBackend();
} }
/**
* Get a string describing the current backend.
*/
public static String getBackendNameForCrashReport() {
return BackendManagerImpl.getBackendNameForCrashReport();
}
public static boolean isOn() { public static boolean isOn() {
return BackendManagerImpl.isOn(); return BackendManagerImpl.isOn();
} }

View file

@ -2,11 +2,30 @@ package com.jozufozu.flywheel.api.backend;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instancer.InstancerProvider; import com.jozufozu.flywheel.api.instancer.InstancerProvider;
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager; import com.jozufozu.flywheel.api.task.TaskExecutor;
public interface Engine extends RenderDispatcher, InstancerProvider { import net.minecraft.client.Camera;
void attachManagers(InstanceManager<?>... listener); import net.minecraft.core.Vec3i;
public interface Engine extends InstancerProvider {
void beginFrame(TaskExecutor executor, RenderContext context);
void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage);
/**
* Maintain the render origin to be within a certain distance from the camera in all directions,
* preventing floating point precision issues at high coordinates.
*
* @return {@code true} if the render origin changed, {@code false} otherwise.
*/
boolean updateRenderOrigin(Camera camera);
Vec3i renderOrigin();
void addDebugInfo(List<String> info); void addDebugInfo(List<String> info);
void delete();
} }

View file

@ -1,27 +0,0 @@
package com.jozufozu.flywheel.api.backend;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import net.minecraft.client.Camera;
import net.minecraft.core.Vec3i;
public interface RenderDispatcher {
void beginFrame(TaskExecutor executor, RenderContext context);
void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage);
/**
* Maintain the integer origin coordinate to be within a certain distance from the camera in all directions,
* preventing floating point precision issues at high coordinates.
*
* @return {@code true} if the origin coordinate was changed, {@code false} otherwise.
*/
boolean maintainOriginCoordinate(Camera camera);
Vec3i renderOrigin();
void delete();
}

View file

@ -1,5 +1,7 @@
package com.jozufozu.flywheel.api.instance; package com.jozufozu.flywheel.api.instance;
import org.joml.FrustumIntersection;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.instancer.Instancer; import com.jozufozu.flywheel.api.instancer.Instancer;
@ -34,4 +36,13 @@ public interface DynamicInstance extends Instance {
default boolean decreaseFramerateWithDistance() { default boolean decreaseFramerateWithDistance() {
return true; return true;
} }
/**
* Check this instance against a frustum.<p>
* An implementor may choose to return a constant to skip the frustum check.
*
* @param frustum A frustum intersection tester for the current frame.
* @return {@code true} if this instance should be considered for updates.
*/
boolean isVisible(FrustumIntersection frustum);
} }

View file

@ -0,0 +1,6 @@
package com.jozufozu.flywheel.api.instance;
import com.jozufozu.flywheel.api.instance.effect.Effect;
public interface EffectInstance<T extends Effect> extends Instance {
}

View file

@ -2,5 +2,5 @@ package com.jozufozu.flywheel.api.instance;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
public interface EntityInstance<E extends Entity> extends Instance { public interface EntityInstance<T extends Entity> extends Instance {
} }

View file

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.api.instance; package com.jozufozu.flywheel.api.instance;
import org.joml.FrustumIntersection;
/** /**
* A general interface providing information about any type of thing that could use Flywheel's instanced rendering. * A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
*/ */
@ -33,15 +31,6 @@ public interface Instance {
*/ */
boolean shouldReset(); boolean shouldReset();
/**
* Check this instance against a frustum.<p>
* An implementor may choose to return a constant to skip the frustum check.
*
* @param frustum A frustum intersection tester for the current frame.
* @return {@code true} if this instance should be considered for updates.
*/
boolean checkFrustum(FrustumIntersection frustum);
/** /**
* Calculate the distance squared between this instance and the given <em>world</em> position. * Calculate the distance squared between this instance and the given <em>world</em> position.
* *
@ -56,5 +45,4 @@ public interface Instance {
* Free any acquired resources. * Free any acquired resources.
*/ */
void delete(); void delete();
} }

View file

@ -10,7 +10,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
*/ */
public interface BlockEntityInstancingController<T extends BlockEntity> { public interface BlockEntityInstancingController<T extends BlockEntity> {
/** /**
* Given a block entity and an instancer manager, constructs an instance for the block entity. * Given a block entity and context, constructs an instance for the block entity.
* *
* @param ctx Context for creating an Instance. * @param ctx Context for creating an Instance.
* @param blockEntity The block entity to construct an instance for. * @param blockEntity The block entity to construct an instance for.

View file

@ -10,7 +10,7 @@ import net.minecraft.world.entity.Entity;
*/ */
public interface EntityInstancingController<T extends Entity> { public interface EntityInstancingController<T extends Entity> {
/** /**
* Given an entity and an instancer manager, constructs an instance for the entity. * Given an entity and context, constructs an instance for the entity.
* *
* @param ctx Context for creating an Instance. * @param ctx Context for creating an Instance.
* @param entity The entity to construct an instance for. * @param entity The entity to construct an instance for.

View file

@ -12,5 +12,4 @@ import net.minecraft.core.Vec3i;
* All models render as if this position is (0, 0, 0). * All models render as if this position is (0, 0, 0).
*/ */
public record InstanceContext(InstancerProvider instancerProvider, Vec3i renderOrigin) { public record InstanceContext(InstancerProvider instancerProvider, Vec3i renderOrigin) {
} }

View file

@ -2,7 +2,7 @@ package com.jozufozu.flywheel.api.instance.controller;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.impl.instance.InstancingControllerRegistryImpl; import com.jozufozu.flywheel.impl.InstancingControllerRegistryImpl;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;

View file

@ -2,9 +2,11 @@ package com.jozufozu.flywheel.api.instance.effect;
import java.util.Collection; import java.util.Collection;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.EffectInstance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
// TODO: add level getter?
// TODO: return single instance instead of many?
public interface Effect { public interface Effect {
Collection<Instance> createInstances(InstanceContext ctx); Collection<EffectInstance<?>> createInstances(InstanceContext ctx);
} }

View file

@ -10,5 +10,5 @@ public interface InstancerProvider {
* *
* @return An instancer for the given struct type, model, and render stage. * @return An instancer for the given struct type, model, and render stage.
*/ */
<D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage); <D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage);
} }

View file

@ -1,23 +1,5 @@
package com.jozufozu.flywheel.api.vertex; package com.jozufozu.flywheel.api.vertex;
import com.jozufozu.flywheel.extension.VertexFormatExtension;
import com.jozufozu.flywheel.impl.vertex.InferredVertexListProviderImpl;
import com.mojang.blaze3d.vertex.VertexFormat;
public interface VertexListProvider { public interface VertexListProvider {
ReusableVertexList createVertexList(); ReusableVertexList createVertexList();
static VertexListProvider get(VertexFormat format) {
VertexFormatExtension extension = (VertexFormatExtension) format;
VertexListProvider provider = extension.flywheel$getVertexListProvider();
if (provider == null) {
provider = new InferredVertexListProviderImpl(format);
extension.flywheel$setVertexListProvider(provider);
}
return provider;
}
static void set(VertexFormat format, VertexListProvider provider) {
((VertexFormatExtension) format).flywheel$setVertexListProvider(provider);
}
} }

View file

@ -0,0 +1,17 @@
package com.jozufozu.flywheel.api.vertex;
import com.jozufozu.flywheel.impl.VertexListProviderRegistryImpl;
import com.mojang.blaze3d.vertex.VertexFormat;
public final class VertexListProviderRegistry {
public static VertexListProvider getProvider(VertexFormat format) {
return VertexListProviderRegistryImpl.getProvider(format);
}
public static void setProvider(VertexFormat format, VertexListProvider provider) {
VertexListProviderRegistryImpl.setProvider(format, provider);
}
private VertexListProviderRegistry() {
}
}

View file

@ -8,12 +8,9 @@ import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.backend.task.ParallelTaskExecutor; import com.jozufozu.flywheel.backend.task.ParallelTaskExecutor;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
public class BackendUtil { public class BackendUtil {
public static final boolean DUMP_SHADER_SOURCE = System.getProperty("flw.dumpShaderSource") != null;
private static ParallelTaskExecutor executor; private static ParallelTaskExecutor executor;
/** /**
@ -30,7 +27,7 @@ public class BackendUtil {
} }
@Contract("null -> false") @Contract("null -> false")
public static boolean canUseInstancing(@Nullable Level level) { public static boolean canUseInstancing(@Nullable LevelAccessor level) {
return BackendManager.isOn() && isFlywheelLevel(level); return BackendManager.isOn() && isFlywheelLevel(level);
} }
@ -38,11 +35,17 @@ public class BackendUtil {
* Used to avoid calling Flywheel functions on (fake) levels that don't specifically support it. * Used to avoid calling Flywheel functions on (fake) levels that don't specifically support it.
*/ */
public static boolean isFlywheelLevel(@Nullable LevelAccessor level) { public static boolean isFlywheelLevel(@Nullable LevelAccessor level) {
if (level == null) return false; if (level == null) {
return false;
}
if (!level.isClientSide()) return false; if (!level.isClientSide()) {
return false;
}
if (level instanceof FlywheelLevel && ((FlywheelLevel) level).supportsFlywheel()) return true; if (level instanceof FlywheelLevel flywheelLevel && flywheelLevel.supportsFlywheel()) {
return true;
}
return level == Minecraft.getInstance().level; return level == Minecraft.getInstance().level;
} }
@ -50,8 +53,4 @@ public class BackendUtil {
public static boolean isGameActive() { public static boolean isGameActive() {
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null); return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
} }
public static void reloadWorldRenderers() {
Minecraft.getInstance().levelRenderer.allChanged();
}
} }

View file

@ -2,9 +2,9 @@ package com.jozufozu.flywheel.backend;
import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.backend.compile.FlwCompiler; import com.jozufozu.flywheel.backend.compile.FlwCompiler;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.glsl.ShaderSources; import com.jozufozu.flywheel.glsl.ShaderSources;
import com.jozufozu.flywheel.glsl.error.ErrorReporter; import com.jozufozu.flywheel.glsl.error.ErrorReporter;
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
@ -39,8 +39,8 @@ public class Loader implements ResourceManagerReloadListener {
FlwCompiler.INSTANCE = new FlwCompiler(sources); FlwCompiler.INSTANCE = new FlwCompiler(sources);
ClientLevel level = Minecraft.getInstance().level; ClientLevel level = Minecraft.getInstance().level;
if (BackendUtil.canUseInstancing(level)) { if (level != null) {
InstancedRenderDispatcher.resetInstanceLevel(level); InstancedRenderDispatcher.resetInstanceWorld(level);
} }
} }

View file

@ -10,7 +10,6 @@ import org.jetbrains.annotations.NotNull;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.gl.GLSLVersion; import com.jozufozu.flywheel.gl.GLSLVersion;
import com.jozufozu.flywheel.gl.shader.GlShader; import com.jozufozu.flywheel.gl.shader.GlShader;
import com.jozufozu.flywheel.gl.shader.ShaderType; import com.jozufozu.flywheel.gl.shader.ShaderType;
@ -29,6 +28,8 @@ import net.minecraft.resources.ResourceLocation;
* and interprets/pretty prints any errors that occur. * and interprets/pretty prints any errors that occur.
*/ */
public class Compilation { public class Compilation {
public static final boolean DUMP_SHADER_SOURCE = System.getProperty("flw.dumpShaderSource") != null;
private final List<SourceFile> files = new ArrayList<>(); private final List<SourceFile> files = new ArrayList<>();
private final List<ResourceLocation> componentNames = new ArrayList<>(); private final List<ResourceLocation> componentNames = new ArrayList<>();
private final StringBuilder generatedSource; private final StringBuilder generatedSource;
@ -131,7 +132,7 @@ public class Compilation {
} }
private static void dumpSource(String source, String fileName) { private static void dumpSource(String source, String fileName) {
if (!BackendUtil.DUMP_SHADER_SOURCE) { if (!DUMP_SHADER_SOURCE) {
return; return;
} }

View file

@ -11,7 +11,7 @@ import org.lwjgl.system.MemoryUtil;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.model.Mesh; import com.jozufozu.flywheel.api.model.Mesh;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider; import com.jozufozu.flywheel.api.vertex.VertexListProviderRegistry;
import com.jozufozu.flywheel.lib.memory.MemoryBlock; import com.jozufozu.flywheel.lib.memory.MemoryBlock;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
@ -35,7 +35,7 @@ public class BatchedMeshPool {
*/ */
public BatchedMeshPool(VertexFormat vertexFormat) { public BatchedMeshPool(VertexFormat vertexFormat) {
this.vertexFormat = vertexFormat; this.vertexFormat = vertexFormat;
vertexList = VertexListProvider.get(vertexFormat).createVertexList(); vertexList = VertexListProviderRegistry.getProvider(vertexFormat).createVertexList();
growthMargin = vertexFormat.getVertexSize() * 32; growthMargin = vertexFormat.getVertexSize() * 32;
} }

View file

@ -10,7 +10,6 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
import com.jozufozu.flywheel.util.FlwUtil; import com.jozufozu.flywheel.util.FlwUtil;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
@ -21,11 +20,11 @@ import net.minecraft.core.Vec3i;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class BatchingEngine implements Engine { public class BatchingEngine implements Engine {
protected final BatchingTransformManager transformManager = new BatchingTransformManager(); private final BatchingTransformManager transformManager = new BatchingTransformManager();
protected final BatchingDrawTracker drawTracker = new BatchingDrawTracker(); private final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
@Override @Override
public <D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage) { public <D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage) {
return transformManager.getInstancer(type, model, stage); return transformManager.getInstancer(type, model, stage);
} }
@ -42,7 +41,7 @@ public class BatchingEngine implements Engine {
submitTasks(executor, stack.last(), context.level()); submitTasks(executor, stack.last(), context.level());
} }
public void submitTasks(TaskExecutor executor, PoseStack.Pose matrices, ClientLevel level) { private void submitTasks(TaskExecutor executor, PoseStack.Pose matrices, ClientLevel level) {
for (var transformSetEntry : transformManager.getTransformSetsView().entrySet()) { for (var transformSetEntry : transformManager.getTransformSetsView().entrySet()) {
var stage = transformSetEntry.getKey(); var stage = transformSetEntry.getKey();
var transformSet = transformSetEntry.getValue(); var transformSet = transformSetEntry.getValue();
@ -79,16 +78,10 @@ public class BatchingEngine implements Engine {
} }
@Override @Override
public boolean maintainOriginCoordinate(Camera camera) { public boolean updateRenderOrigin(Camera camera) {
// do nothing
return false; return false;
} }
@Override
public void attachManagers(InstanceManager<?>... listener) {
// noop
}
@Override @Override
public Vec3i renderOrigin() { public Vec3i renderOrigin() {
return BlockPos.ZERO; return BlockPos.ZERO;

View file

@ -7,6 +7,7 @@ import org.jetbrains.annotations.ApiStatus;
import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.vertex.VertexListProvider; import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.jozufozu.flywheel.api.vertex.VertexListProviderRegistry;
import com.mojang.blaze3d.vertex.VertexFormat; import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
@ -24,7 +25,7 @@ public class DrawBufferSet {
this.renderType = renderType; this.renderType = renderType;
format = renderType.format(); format = renderType.format();
stride = format.getVertexSize(); stride = format.getVertexSize();
provider = VertexListProvider.get(format); provider = VertexListProviderRegistry.getProvider(format);
} }
public DrawBuffer getBuffer(RenderStage stage) { public DrawBuffer getBuffer(RenderStage stage) {

View file

@ -1,13 +1,10 @@
package com.jozufozu.flywheel.backend.engine.indirect; package com.jozufozu.flywheel.backend.engine.indirect;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import com.jozufozu.flywheel.api.backend.Engine; import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.context.Context;
import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
@ -15,40 +12,29 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
import com.jozufozu.flywheel.api.model.Model; import com.jozufozu.flywheel.api.model.Model;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
import com.jozufozu.flywheel.gl.GlStateTracker; import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.GlTextureUnit; import com.jozufozu.flywheel.gl.GlTextureUnit;
import com.jozufozu.flywheel.util.FlwUtil;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class IndirectEngine implements Engine { public class IndirectEngine implements Engine {
private final int sqrMaxOriginDistance;
protected final IndirectDrawManager drawManager = new IndirectDrawManager(); private final IndirectDrawManager drawManager = new IndirectDrawManager();
/** private BlockPos renderOrigin = BlockPos.ZERO;
* The set of instance managers that are attached to this engine.
*/
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
protected final Context context; public IndirectEngine(int maxOriginDistance) {
protected final int sqrMaxOriginDistance; this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
protected BlockPos originCoordinate = BlockPos.ZERO;
public IndirectEngine(Context context, int sqrMaxOriginDistance) {
this.context = context;
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
} }
@Override @Override
public <D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage) { public <D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage) {
return drawManager.getInstancer(type, model, stage); return drawManager.getInstancer(type, model, stage);
} }
@ -70,7 +56,7 @@ public class IndirectEngine implements Engine {
} }
} }
protected void setup() { private void setup() {
GlTextureUnit.T2.makeActive(); GlTextureUnit.T2.makeActive();
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer(); Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
@ -82,36 +68,25 @@ public class IndirectEngine implements Engine {
} }
@Override @Override
public boolean maintainOriginCoordinate(Camera camera) { public boolean updateRenderOrigin(Camera camera) {
Vec3 cameraPos = camera.getPosition(); Vec3 cameraPos = camera.getPosition();
double dx = renderOrigin.getX() - cameraPos.x;
double dy = renderOrigin.getY() - cameraPos.y;
double dz = renderOrigin.getZ() - cameraPos.z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
double distanceSqr = Vec3.atLowerCornerOf(originCoordinate) if (distanceSqr <= sqrMaxOriginDistance) {
.subtract(cameraPos) return false;
.lengthSqr();
if (distanceSqr > sqrMaxOriginDistance) {
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
return true;
} }
return false;
}
private void shiftListeners(int cX, int cY, int cZ) {
originCoordinate = new BlockPos(cX, cY, cZ);
renderOrigin = new BlockPos(cameraPos);
drawManager.clearInstancers(); drawManager.clearInstancers();
return true;
instanceManagers.forEach(InstanceManager::onOriginShift);
}
@Override
public void attachManagers(InstanceManager<?>... listener) {
Collections.addAll(instanceManagers, listener);
} }
@Override @Override
public Vec3i renderOrigin() { public Vec3i renderOrigin() {
return originCoordinate; return renderOrigin;
} }
@Override @Override
@ -122,6 +97,6 @@ public class IndirectEngine implements Engine {
@Override @Override
public void addDebugInfo(List<String> info) { public void addDebugInfo(List<String> info) {
info.add("GL46 Indirect"); info.add("GL46 Indirect");
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
} }
} }

View file

@ -1,8 +1,6 @@
package com.jozufozu.flywheel.backend.engine.instancing; package com.jozufozu.flywheel.backend.engine.instancing;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
@ -17,42 +15,33 @@ import com.jozufozu.flywheel.api.struct.StructType;
import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.compile.FlwCompiler; import com.jozufozu.flywheel.backend.compile.FlwCompiler;
import com.jozufozu.flywheel.backend.engine.UniformBuffer; import com.jozufozu.flywheel.backend.engine.UniformBuffer;
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
import com.jozufozu.flywheel.gl.GlStateTracker; import com.jozufozu.flywheel.gl.GlStateTracker;
import com.jozufozu.flywheel.gl.GlTextureUnit; import com.jozufozu.flywheel.gl.GlTextureUnit;
import com.jozufozu.flywheel.lib.material.MaterialIndices; import com.jozufozu.flywheel.lib.material.MaterialIndices;
import com.jozufozu.flywheel.lib.pipeline.Pipelines; import com.jozufozu.flywheel.lib.pipeline.Pipelines;
import com.jozufozu.flywheel.util.FlwUtil;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera; import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Vec3i; import net.minecraft.core.Vec3i;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class InstancingEngine implements Engine { public class InstancingEngine implements Engine {
private final Context context;
private final int sqrMaxOriginDistance;
protected final InstancingDrawManager drawManager = new InstancingDrawManager(); private final InstancingDrawManager drawManager = new InstancingDrawManager();
/** private BlockPos renderOrigin = BlockPos.ZERO;
* The set of instance managers that are attached to this engine.
*/
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
protected final Context context; public InstancingEngine(Context context, int maxOriginDistance) {
protected final int sqrMaxOriginDistance;
protected BlockPos originCoordinate = BlockPos.ZERO;
public InstancingEngine(Context context, int sqrMaxOriginDistance) {
this.context = context; this.context = context;
this.sqrMaxOriginDistance = sqrMaxOriginDistance; this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
} }
@Override @Override
public <D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage) { public <D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage) {
return drawManager.getInstancer(type, model, stage); return drawManager.getInstancer(type, model, stage);
} }
@ -78,7 +67,7 @@ public class InstancingEngine implements Engine {
} }
} }
protected void setup() { private void setup() {
GlTextureUnit.T2.makeActive(); GlTextureUnit.T2.makeActive();
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer(); Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
@ -89,7 +78,7 @@ public class InstancingEngine implements Engine {
RenderSystem.enableCull(); RenderSystem.enableCull();
} }
protected void render(InstancingDrawManager.DrawSet drawSet) { private void render(InstancingDrawManager.DrawSet drawSet) {
for (var entry : drawSet) { for (var entry : drawSet) {
var shader = entry.getKey(); var shader = entry.getKey();
var drawCalls = entry.getValue(); var drawCalls = entry.getValue();
@ -112,7 +101,7 @@ public class InstancingEngine implements Engine {
} }
} }
protected void setup(ShaderState desc) { private void setup(ShaderState desc) {
var vertexType = desc.vertex(); var vertexType = desc.vertex();
var structType = desc.instance(); var structType = desc.instance();
var material = desc.material(); var material = desc.material();
@ -127,36 +116,25 @@ public class InstancingEngine implements Engine {
} }
@Override @Override
public boolean maintainOriginCoordinate(Camera camera) { public boolean updateRenderOrigin(Camera camera) {
Vec3 cameraPos = camera.getPosition(); Vec3 cameraPos = camera.getPosition();
double dx = renderOrigin.getX() - cameraPos.x;
double dy = renderOrigin.getY() - cameraPos.y;
double dz = renderOrigin.getZ() - cameraPos.z;
double distanceSqr = dx * dx + dy * dy + dz * dz;
double distanceSqr = Vec3.atLowerCornerOf(originCoordinate) if (distanceSqr <= sqrMaxOriginDistance) {
.subtract(cameraPos) return false;
.lengthSqr();
if (distanceSqr > sqrMaxOriginDistance) {
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
return true;
} }
return false;
}
private void shiftListeners(int cX, int cY, int cZ) {
originCoordinate = new BlockPos(cX, cY, cZ);
renderOrigin = new BlockPos(cameraPos);
drawManager.clearInstancers(); drawManager.clearInstancers();
return true;
instanceManagers.forEach(InstanceManager::onOriginShift);
}
@Override
public void attachManagers(InstanceManager<?>... listener) {
Collections.addAll(instanceManagers, listener);
} }
@Override @Override
public Vec3i renderOrigin() { public Vec3i renderOrigin() {
return originCoordinate; return renderOrigin;
} }
@Override @Override
@ -167,6 +145,6 @@ public class InstancingEngine implements Engine {
@Override @Override
public void addDebugInfo(List<String> info) { public void addDebugInfo(List<String> info) {
info.add("GL33 Instanced Arrays"); info.add("GL33 Instanced Arrays");
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ()); info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
} }
} }

View file

@ -1,151 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instance.effect.Effect;
import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.manager.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.manager.EffectInstanceManager;
import com.jozufozu.flywheel.backend.instancing.manager.EntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
import com.jozufozu.flywheel.backend.task.ParallelTaskExecutor;
import com.jozufozu.flywheel.extension.ClientLevelExtension;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
* A manager class for a single world where instancing is supported.
* <p>
* The instancer manager is shared between the different instance managers.
* </p>
*/
public class InstanceWorld implements AutoCloseable {
protected final Engine engine;
protected final InstanceManager<Entity> entities;
protected final InstanceManager<BlockEntity> blockEntities;
public final ParallelTaskExecutor taskExecutor;
private final InstanceManager<Effect> effects;
public static InstanceWorld create(LevelAccessor level) {
var engine = BackendManager.getBackend()
.createEngine();
var entities = new EntityInstanceManager(engine);
var blockEntities = new BlockEntityInstanceManager(engine);
var effects = new EffectInstanceManager(engine);
engine.attachManagers(entities, blockEntities, effects);
return new InstanceWorld(engine, entities, blockEntities, effects);
}
public InstanceWorld(Engine engine, InstanceManager<Entity> entities, InstanceManager<BlockEntity> blockEntities,
InstanceManager<Effect> effects) {
this.engine = engine;
this.entities = entities;
this.blockEntities = blockEntities;
this.effects = effects;
this.taskExecutor = BackendUtil.getTaskExecutor();
}
public InstanceManager<Entity> getEntities() {
return entities;
}
public InstanceManager<Effect> getEffects() {
return effects;
}
public InstanceManager<BlockEntity> getBlockEntities() {
return blockEntities;
}
/**
* Free all acquired resources and invalidate this instance world.
*/
public void delete() {
engine.delete();
entities.delete();
blockEntities.delete();
}
/**
* Get ready to render a frame.
* <p>
* Check and shift the origin coordinate.
* <br>
* Call {@link DynamicInstance#beginFrame()} on all instances in this world.
* </p>
*/
public void beginFrame(BeginFrameEvent event) {
RenderContext context = event.getContext();
boolean shifted = engine.maintainOriginCoordinate(context.camera());
taskExecutor.syncPoint();
if (!shifted) {
blockEntities.beginFrame(taskExecutor, context);
entities.beginFrame(taskExecutor, context);
effects.beginFrame(taskExecutor, context);
}
engine.beginFrame(taskExecutor, context);
}
/**
* Tick the renderers after the game has ticked:
* <p>
* Call {@link TickableInstance#tick()} on all instances in this world.
* </p>
*/
public void tick() {
Minecraft mc = Minecraft.getInstance();
if (mc.isPaused()) return;
Entity renderViewEntity = mc.cameraEntity != null ? mc.cameraEntity : mc.player;
if (renderViewEntity == null) return;
double x = renderViewEntity.getX();
double y = renderViewEntity.getY();
double z = renderViewEntity.getZ();
blockEntities.tick(taskExecutor, x, y, z);
entities.tick(taskExecutor, x, y, z);
effects.tick(taskExecutor, x, y, z);
}
/**
* Draw all instances for the given stage.
*/
public void renderStage(RenderContext context, RenderStage stage) {
taskExecutor.syncPoint();
engine.renderStage(taskExecutor, context, stage);
}
/**
* Instantiate all the necessary instances to render the given world.
*/
public void loadEntities(ClientLevel level) {
// Block entities are loaded while chunks are baked.
// Entities are loaded with the level, so when chunks are reloaded they need to be re-added.
ClientLevelExtension.getAllLoadedEntities(level)
.forEach(entities::add);
}
@Override
public void close() {
delete();
}
}

View file

@ -1,133 +0,0 @@
package com.jozufozu.flywheel.backend.instancing;
import java.util.List;
import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.RenderStageEvent;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.effect.Effect;
import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.WorldAttached;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.event.TickEvent;
public class InstancedRenderDispatcher {
private static final WorldAttached<InstanceWorld> instanceWorlds = new WorldAttached<>(InstanceWorld::create);
/**
* Call this when you want to manually run {@link Instance#update()}.
* @param blockEntity The block entity whose instance you want to update.
*/
public static void enqueueUpdate(BlockEntity blockEntity) {
if (BackendManager.isOn() && blockEntity.hasLevel() && blockEntity.getLevel() instanceof ClientLevel) {
instanceWorlds.get(blockEntity.getLevel())
.getBlockEntities()
.queueUpdate(blockEntity);
}
}
/**
* Call this when you want to manually run {@link Instance#update()}.
* @param entity The entity whose instance you want to update.
*/
public static void enqueueUpdate(Entity entity) {
if (BackendManager.isOn()) {
instanceWorlds.get(entity.level)
.getEntities()
.queueUpdate(entity);
}
}
public static InstanceManager<BlockEntity> getBlockEntities(LevelAccessor world) {
return getInstanceWorld(world).getBlockEntities();
}
public static InstanceManager<Entity> getEntities(LevelAccessor world) {
return getInstanceWorld(world).getEntities();
}
public static InstanceManager<Effect> getEffects(LevelAccessor world) {
return getInstanceWorld(world).getEffects();
}
/**
* Get or create the {@link InstanceWorld} for the given world.
* @throws NullPointerException if the backend is off
*/
public static InstanceWorld getInstanceWorld(LevelAccessor world) {
if (BackendManager.isOn()) {
return instanceWorlds.get(world);
} else {
throw new NullPointerException("Backend is off, cannot retrieve instance world.");
}
}
public static void tick(TickEvent.ClientTickEvent event) {
if (!BackendUtil.isGameActive() || event.phase == TickEvent.Phase.START) {
return;
}
Minecraft mc = Minecraft.getInstance();
ClientLevel level = mc.level;
AnimationTickHolder.tick();
if (BackendManager.isOn()) {
instanceWorlds.get(level)
.tick();
}
}
public static void onBeginFrame(BeginFrameEvent event) {
if (BackendUtil.isGameActive() && BackendManager.isOn()) {
instanceWorlds.get(event.getContext().level())
.beginFrame(event);
}
}
public static void onRenderStage(RenderStageEvent event) {
ClientLevel level = event.getContext().level();
if (!BackendUtil.canUseInstancing(level)) return;
instanceWorlds.get(level).renderStage(event.getContext(), event.getStage());
}
public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel level = event.getLevel();
if (BackendManager.isOn() && level != null) {
resetInstanceLevel(level);
}
}
public static void resetInstanceLevel(ClientLevel level) {
instanceWorlds.replace(level, InstanceWorld::delete)
.loadEntities(level);
}
public static void getDebugString(List<String> debug) {
if (BackendManager.isOn()) {
InstanceWorld instanceWorld = instanceWorlds.get(Minecraft.getInstance().level);
debug.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
debug.add("B: " + instanceWorld.blockEntities.getInstanceCount() + ", E: " + instanceWorld.entities.getInstanceCount());
instanceWorld.engine.addDebugInfo(debug);
} else {
debug.add("Disabled");
}
}
public static Vec3i getOriginCoordinate(ClientLevel level) {
return instanceWorlds.get(level).engine.renderOrigin();
}
}

View file

@ -1,111 +0,0 @@
package com.jozufozu.flywheel.backend.instancing.manager;
import java.util.ArrayList;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.api.instance.effect.Effect;
import com.jozufozu.flywheel.backend.instancing.storage.AbstractStorage;
import com.jozufozu.flywheel.backend.instancing.storage.Storage;
public class EffectInstanceManager extends InstanceManager<Effect> {
private final EffectStorage<Effect> storage;
public EffectInstanceManager(Engine engine) {
storage = new EffectStorage<>(engine);
}
@Override
protected Storage<Effect> getStorage() {
return storage;
}
@Override
protected boolean canCreateInstance(Effect obj) {
return true;
}
private static class EffectStorage<T extends Effect> extends AbstractStorage<T> {
private final Multimap<T, Instance> instances;
public EffectStorage(Engine engine) {
super(engine);
this.instances = HashMultimap.create();
}
@Override
public Iterable<Instance> getAllInstances() {
return instances.values();
}
@Override
public int getInstanceCount() {
return instances.size();
}
@Override
public void add(T obj) {
var instances = this.instances.get(obj);
if (instances.isEmpty()) {
create(obj);
}
}
@Override
public void remove(T obj) {
var instances = this.instances.removeAll(obj);
if (instances.isEmpty()) {
return;
}
this.tickableInstances.removeAll(instances);
this.dynamicInstances.removeAll(instances);
for (Instance instance : instances) {
instance.delete();
}
}
@Override
public void update(T obj) {
var instances = this.instances.get(obj);
if (instances.isEmpty()) {
return;
}
instances.forEach(Instance::update);
}
@Override
public void recreateAll() {
tickableInstances.clear();
dynamicInstances.clear();
instances.values().forEach(Instance::delete);
var backup = new ArrayList<>(instances.keySet());
instances.clear();
backup.forEach(this::create);
}
@Override
public void invalidate() {
instances.values().forEach(Instance::delete);
instances.clear();
tickableInstances.clear();
dynamicInstances.clear();
}
private void create(T obj) {
var instances = obj.createInstances(new InstanceContext(engine, engine.renderOrigin()));
this.instances.putAll(obj, instances);
instances.forEach(this::setup);
}
}
}

View file

@ -17,9 +17,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
public enum BackendArgument implements ArgumentType<Backend> { public class BackendArgument implements ArgumentType<Backend> {
INSTANCE;
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds().stream().map(ResourceLocation::toString).toList(); private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds().stream().map(ResourceLocation::toString).toList();
private static final Dynamic2CommandExceptionType INVALID = new Dynamic2CommandExceptionType((found, constants) -> { private static final Dynamic2CommandExceptionType INVALID = new Dynamic2CommandExceptionType((found, constants) -> {
@ -27,9 +25,7 @@ public enum BackendArgument implements ArgumentType<Backend> {
return new TranslatableComponent("commands.forge.arguments.enum.invalid", constants, found); return new TranslatableComponent("commands.forge.arguments.enum.invalid", constants, found);
}); });
public static BackendArgument getInstance() { public static final BackendArgument INSTANCE = new BackendArgument();
return INSTANCE;
}
@Override @Override
public Backend parse(StringReader reader) throws CommandSyntaxException { public Backend parse(StringReader reader) throws CommandSyntaxException {

View file

@ -3,10 +3,8 @@ package com.jozufozu.flywheel.config;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import com.jozufozu.flywheel.api.backend.Backend; import com.jozufozu.flywheel.api.backend.Backend;
import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms; import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
import com.mojang.brigadier.Command; import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType; import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@ -30,9 +28,9 @@ public class FlwCommands {
public static void registerClientCommands(RegisterClientCommandsEvent event) { public static void registerClientCommands(RegisterClientCommandsEvent event) {
FlwConfig config = FlwConfig.get(); FlwConfig config = FlwConfig.get();
ConfigCommandBuilder commandBuilder = new ConfigCommandBuilder("flywheel"); LiteralArgumentBuilder<CommandSourceStack> command = Commands.literal("flywheel");
commandBuilder.addValue(config.client.backend, "backend", (builder, value) -> addValue(command, config.client.backend, "backend", (builder, value) ->
builder builder
.executes(context -> { .executes(context -> {
LocalPlayer player = Minecraft.getInstance().player; LocalPlayer player = Minecraft.getInstance().player;
@ -68,12 +66,12 @@ public class FlwCommands {
Component message = backend.engineMessage(); Component message = backend.engineMessage();
player.displayClientMessage(message, false); player.displayClientMessage(message, false);
BackendUtil.reloadWorldRenderers(); Minecraft.getInstance().levelRenderer.allChanged();
} }
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
}))); })));
commandBuilder.addValue(config.client.limitUpdates, "limitUpdates", (builder, value) -> booleanValueCommand(builder, value, addValue(command, config.client.limitUpdates, "limitUpdates", (builder, value) -> booleanValueCommand(builder, value,
(source, bool) -> { (source, bool) -> {
LocalPlayer player = Minecraft.getInstance().player; LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return; if (player == null) return;
@ -88,12 +86,12 @@ public class FlwCommands {
Component text = boolToText(bool).append(new TextComponent(" update limiting.").withStyle(ChatFormatting.WHITE)); Component text = boolToText(bool).append(new TextComponent(" update limiting.").withStyle(ChatFormatting.WHITE));
player.displayClientMessage(text, false); player.displayClientMessage(text, false);
BackendUtil.reloadWorldRenderers(); Minecraft.getInstance().levelRenderer.allChanged();
} }
)); ));
// TODO // TODO
commandBuilder.command.then(Commands.literal("debugNormals")) command.then(Commands.literal("debugNormals"))
.executes(context -> { .executes(context -> {
LocalPlayer player = Minecraft.getInstance().player; LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return 0; if (player == null) return 0;
@ -103,7 +101,7 @@ public class FlwCommands {
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
}); });
commandBuilder.command.then(Commands.literal("debugCrumbling") command.then(Commands.literal("debugCrumbling")
.then(Commands.argument("pos", BlockPosArgument.blockPos()) .then(Commands.argument("pos", BlockPosArgument.blockPos())
.then(Commands.argument("stage", IntegerArgumentType.integer(0, 9)) .then(Commands.argument("stage", IntegerArgumentType.integer(0, 9))
.executes(context -> { .executes(context -> {
@ -122,7 +120,7 @@ public class FlwCommands {
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
})))); }))));
commandBuilder.command.then(Commands.literal("debugFrustum") command.then(Commands.literal("debugFrustum")
.then(Commands.literal("pause") .then(Commands.literal("pause")
.executes(context -> { .executes(context -> {
FlwShaderUniforms.FRUSTUM_PAUSED = true; FlwShaderUniforms.FRUSTUM_PAUSED = true;
@ -139,10 +137,16 @@ public class FlwCommands {
return 1; return 1;
}))); })));
commandBuilder.build(event.getDispatcher()); event.getDispatcher().register(command);
} }
public static void booleanValueCommand(LiteralArgumentBuilder<CommandSourceStack> builder, ConfigValue<Boolean> value, BiConsumer<CommandSourceStack, Boolean> displayAction, BiConsumer<CommandSourceStack, Boolean> setAction) { private static <T extends ConfigValue<?>> void addValue(LiteralArgumentBuilder<CommandSourceStack> command, T value, String subcommand, BiConsumer<LiteralArgumentBuilder<CommandSourceStack>, T> consumer) {
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(subcommand);
consumer.accept(builder, value);
command.then(builder);
}
private static void booleanValueCommand(LiteralArgumentBuilder<CommandSourceStack> builder, ConfigValue<Boolean> value, BiConsumer<CommandSourceStack, Boolean> displayAction, BiConsumer<CommandSourceStack, Boolean> setAction) {
builder builder
.executes(context -> { .executes(context -> {
displayAction.accept(context.getSource(), value.get()); displayAction.accept(context.getSource(), value.get());
@ -165,22 +169,4 @@ public class FlwCommands {
public static MutableComponent boolToText(boolean b) { public static MutableComponent boolToText(boolean b) {
return b ? new TextComponent("enabled").withStyle(ChatFormatting.DARK_GREEN) : new TextComponent("disabled").withStyle(ChatFormatting.RED); return b ? new TextComponent("enabled").withStyle(ChatFormatting.DARK_GREEN) : new TextComponent("disabled").withStyle(ChatFormatting.RED);
} }
public static class ConfigCommandBuilder {
protected LiteralArgumentBuilder<CommandSourceStack> command;
public ConfigCommandBuilder(String baseLiteral) {
command = Commands.literal(baseLiteral);
}
public <T extends ConfigValue<?>> void addValue(T value, String subcommand, BiConsumer<LiteralArgumentBuilder<CommandSourceStack>, T> consumer) {
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(subcommand);
consumer.accept(builder, value);
command.then(builder);
}
public void build(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(command);
}
}
} }

View file

@ -1,19 +1,34 @@
package com.jozufozu.flywheel.handler; package com.jozufozu.flywheel.handler;
import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import net.minecraft.world.level.Level;
import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.entity.EntityLeaveWorldEvent; import net.minecraftforge.event.entity.EntityLeaveWorldEvent;
public class EntityWorldHandler { public class EntityWorldHandler {
public static void onEntityJoinWorld(EntityJoinWorldEvent event) { public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
if (event.getWorld().isClientSide && BackendManager.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld()) Level level = event.getWorld();
.queueAdd(event.getEntity()); if (!level.isClientSide) {
return;
}
if (BackendUtil.canUseInstancing(level)) {
InstancedRenderDispatcher.getEntities(level)
.queueAdd(event.getEntity());
}
} }
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) { public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
if (event.getWorld().isClientSide && BackendManager.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld()) Level level = event.getWorld();
.remove(event.getEntity()); if (!level.isClientSide) {
return;
}
if (BackendUtil.canUseInstancing(level)) {
InstancedRenderDispatcher.getEntities(level)
.remove(event.getEntity());
}
} }
} }

View file

@ -4,7 +4,7 @@ import java.util.ArrayList;
import com.jozufozu.flywheel.Flywheel; import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.backend.BackendUtil; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.lib.light.LightUpdater; import com.jozufozu.flywheel.lib.light.LightUpdater;
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker; import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
import com.jozufozu.flywheel.util.StringUtil; import com.jozufozu.flywheel.util.StringUtil;
@ -16,14 +16,13 @@ import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.world.WorldEvent;
public class ForgeEvents { public class ForgeEvents {
public static void addToDebugScreen(RenderGameOverlayEvent.Text event) { public static void addToDebugScreen(RenderGameOverlayEvent.Text event) {
if (Minecraft.getInstance().options.renderDebug) { if (Minecraft.getInstance().options.renderDebug) {
ArrayList<String> debug = event.getRight(); ArrayList<String> debug = event.getRight();
debug.add(""); debug.add("");
debug.add("Flywheel: " + Flywheel.getVersion()); debug.add("Flywheel: " + Flywheel.getVersion());
InstancedRenderDispatcher.getDebugString(debug); InstancedRenderDispatcher.addDebugInfo(debug);
debug.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory())); debug.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory()));
} }
@ -39,5 +38,4 @@ public class ForgeEvents {
.tick(); .tick();
} }
} }
} }

View file

@ -18,17 +18,6 @@ public final class BackendManagerImpl {
return backend; return backend;
} }
public static String getBackendNameForCrashReport() {
if (backend == null) {
return "Uninitialized";
}
var backendId = Backend.REGISTRY.getId(backend);
if (backendId == null) {
return "Unregistered";
}
return backendId.toString();
}
public static boolean isOn() { public static boolean isOn() {
return backend != null && backend != Backends.OFF; return backend != null && backend != Backends.OFF;
} }
@ -58,6 +47,17 @@ public final class BackendManagerImpl {
return Backends.INDIRECT; return Backends.INDIRECT;
} }
public static String getBackendNameForCrashReport() {
if (backend == null) {
return "Uninitialized";
}
var backendId = Backend.REGISTRY.getId(backend);
if (backendId == null) {
return "Unregistered";
}
return backendId.toString();
}
private BackendManagerImpl() { private BackendManagerImpl() {
} }
} }

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.impl.instance; package com.jozufozu.flywheel.impl;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -12,6 +12,7 @@ import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
//TODO: Add freezing
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public final class InstancingControllerRegistryImpl { public final class InstancingControllerRegistryImpl {
@Nullable @Nullable

View file

@ -0,0 +1,26 @@
package com.jozufozu.flywheel.impl;
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
import com.jozufozu.flywheel.extension.VertexFormatExtension;
import com.jozufozu.flywheel.impl.vertex.InferredVertexListProviderImpl;
import com.mojang.blaze3d.vertex.VertexFormat;
// TODO: Add freezing
public final class VertexListProviderRegistryImpl {
public static VertexListProvider getProvider(VertexFormat format) {
VertexFormatExtension extension = (VertexFormatExtension) format;
VertexListProvider provider = extension.flywheel$getVertexListProvider();
if (provider == null) {
provider = new InferredVertexListProviderImpl(format);
extension.flywheel$setVertexListProvider(provider);
}
return provider;
}
public static void setProvider(VertexFormat format, VertexListProvider provider) {
((VertexFormatExtension) format).flywheel$setVertexListProvider(provider);
}
private VertexListProviderRegistryImpl() {
}
}

View file

@ -0,0 +1,135 @@
package com.jozufozu.flywheel.impl.instancing;
import java.util.List;
import org.joml.FrustumIntersection;
import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instance.effect.Effect;
import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.config.FlwCommands;
import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.impl.instancing.manager.BlockEntityInstanceManager;
import com.jozufozu.flywheel.impl.instancing.manager.EffectInstanceManager;
import com.jozufozu.flywheel.impl.instancing.manager.EntityInstanceManager;
import com.jozufozu.flywheel.impl.instancing.manager.InstanceManager;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
/**
* A manager class for a single world where instancing is supported.
*/
public class InstanceWorld {
private final Engine engine;
private final TaskExecutor taskExecutor;
private final InstanceManager<BlockEntity> blockEntities;
private final InstanceManager<Entity> entities;
private final InstanceManager<Effect> effects;
public InstanceWorld(LevelAccessor level) {
engine = BackendManager.getBackend().createEngine(level);
taskExecutor = BackendUtil.getTaskExecutor();
blockEntities = new BlockEntityInstanceManager(engine);
entities = new EntityInstanceManager(engine);
effects = new EffectInstanceManager(engine);
}
public Engine getEngine() {
return engine;
}
public InstanceManager<BlockEntity> getBlockEntities() {
return blockEntities;
}
public InstanceManager<Entity> getEntities() {
return entities;
}
public InstanceManager<Effect> getEffects() {
return effects;
}
/**
* Tick the instances after the game has ticked:
* <p>
* Call {@link TickableInstance#tick()} on all instances in this world.
* </p>
*/
public void tick(double cameraX, double cameraY, double cameraZ) {
blockEntities.tick(taskExecutor, cameraX, cameraY, cameraZ);
entities.tick(taskExecutor, cameraX, cameraY, cameraZ);
effects.tick(taskExecutor, cameraX, cameraY, cameraZ);
}
/**
* Get ready to render a frame.
* <p>
* Check and update the render origin.
* <br>
* Call {@link DynamicInstance#beginFrame()} on all instances in this world.
* </p>
*/
public void beginFrame(RenderContext context) {
boolean originChanged = engine.updateRenderOrigin(context.camera());
if (originChanged) {
blockEntities.recreateAll();
entities.recreateAll();
effects.recreateAll();
}
taskExecutor.syncPoint();
if (!originChanged) {
var cameraPos = context.camera()
.getPosition();
double cameraX = cameraPos.x;
double cameraY = cameraPos.y;
double cameraZ = cameraPos.z;
FrustumIntersection culler = context.culler();
blockEntities.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, culler);
entities.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, culler);
effects.beginFrame(taskExecutor, cameraX, cameraY, cameraZ, culler);
}
engine.beginFrame(taskExecutor, context);
}
/**
* Draw all instances for the given stage.
*/
public void renderStage(RenderContext context, RenderStage stage) {
taskExecutor.syncPoint();
engine.renderStage(taskExecutor, context, stage);
}
public void addDebugInfo(List<String> info) {
info.add("B: " + blockEntities.getInstanceCount()
+ ", E: " + entities.getInstanceCount()
+ ", F: " + effects.getInstanceCount());
info.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
engine.addDebugInfo(info);
}
/**
* Free all acquired resources and invalidate this instance world.
*/
public void delete() {
blockEntities.invalidate();
entities.invalidate();
effects.invalidate();
engine.delete();
}
}

View file

@ -0,0 +1,184 @@
package com.jozufozu.flywheel.impl.instancing;
import java.util.List;
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.RenderStageEvent;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.effect.Effect;
import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.extension.ClientLevelExtension;
import com.jozufozu.flywheel.impl.instancing.manager.InstanceManager;
import com.jozufozu.flywheel.util.AnimationTickHolder;
import com.jozufozu.flywheel.util.WorldAttached;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.Vec3i;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.event.TickEvent;
public class InstancedRenderDispatcher {
private static final WorldAttached<InstanceWorld> INSTANCE_WORLDS = new WorldAttached<>(InstanceWorld::new);
/**
* Call this when you want to run {@link Instance#update()}.
* @param blockEntity The block entity whose instance you want to update.
*/
public static void queueUpdate(BlockEntity blockEntity) {
if (!(blockEntity.getLevel() instanceof ClientLevel level)) {
return;
}
if (!BackendUtil.canUseInstancing(level)) {
return;
}
INSTANCE_WORLDS.get(level)
.getBlockEntities()
.queueUpdate(blockEntity);
}
/**
* Call this when you want to run {@link Instance#update()}.
* @param entity The entity whose instance you want to update.
*/
public static void queueUpdate(Entity entity) {
Level level = entity.level;
if (!BackendUtil.canUseInstancing(level)) {
return;
}
INSTANCE_WORLDS.get(level)
.getEntities()
.queueUpdate(entity);
}
/**
* Call this when you want to run {@link Instance#update()}.
* @param effect The effect whose instance you want to update.
*/
public static void queueUpdate(LevelAccessor level, Effect effect) {
if (!BackendUtil.canUseInstancing(level)) {
return;
}
INSTANCE_WORLDS.get(level)
.getEffects()
.queueUpdate(effect);
}
/**
* Get or create the {@link InstanceWorld} for the given world.
* @throws IllegalStateException if the backend is off
*/
private static InstanceWorld getInstanceWorld(LevelAccessor level) {
if (!BackendUtil.canUseInstancing(level)) {
throw new IllegalStateException("Cannot retrieve instance world when backend is off!");
}
return INSTANCE_WORLDS.get(level);
}
public static InstanceManager<BlockEntity> getBlockEntities(LevelAccessor level) {
return getInstanceWorld(level).getBlockEntities();
}
public static InstanceManager<Entity> getEntities(LevelAccessor level) {
return getInstanceWorld(level).getEntities();
}
public static InstanceManager<Effect> getEffects(LevelAccessor level) {
return getInstanceWorld(level).getEffects();
}
public static Vec3i getRenderOrigin(LevelAccessor level) {
return getInstanceWorld(level).getEngine().renderOrigin();
}
public static void tick(TickEvent.ClientTickEvent event) {
if (!BackendUtil.isGameActive() || event.phase == TickEvent.Phase.START) {
return;
}
AnimationTickHolder.tick();
Minecraft mc = Minecraft.getInstance();
if (mc.isPaused()) {
return;
}
Entity cameraEntity = mc.getCameraEntity() == null ? mc.player : mc.getCameraEntity();
if (cameraEntity == null) {
return;
}
Level level = cameraEntity.level;
if (!BackendUtil.canUseInstancing(level)) {
return;
}
double cameraX = cameraEntity.getX();
double cameraY = cameraEntity.getEyeY();
double cameraZ = cameraEntity.getZ();
INSTANCE_WORLDS.get(level).tick(cameraX, cameraY, cameraZ);
}
public static void onBeginFrame(BeginFrameEvent event) {
if (!BackendUtil.isGameActive()) {
return;
}
ClientLevel level = event.getContext().level();
if (!BackendUtil.canUseInstancing(level)) {
return;
}
INSTANCE_WORLDS.get(level).beginFrame(event.getContext());
}
public static void onRenderStage(RenderStageEvent event) {
ClientLevel level = event.getContext().level();
if (!BackendUtil.canUseInstancing(level)) {
return;
}
INSTANCE_WORLDS.get(level).renderStage(event.getContext(), event.getStage());
}
public static void onReloadRenderers(ReloadRenderersEvent event) {
ClientLevel level = event.getLevel();
if (level == null) {
return;
}
resetInstanceWorld(level);
}
public static void resetInstanceWorld(ClientLevel level) {
INSTANCE_WORLDS.remove(level, InstanceWorld::delete);
if (!BackendUtil.canUseInstancing(level)) {
return;
}
InstanceWorld world = INSTANCE_WORLDS.get(level);
// Block entities are loaded while chunks are baked.
// Entities are loaded with the level, so when chunks are reloaded they need to be re-added.
ClientLevelExtension.getAllLoadedEntities(level)
.forEach(world.getEntities()::add);
}
public static void addDebugInfo(List<String> info) {
ClientLevel level = Minecraft.getInstance().level;
if (BackendUtil.canUseInstancing(level)) {
INSTANCE_WORLDS.get(level).addDebugInfo(info);
} else {
info.add("Disabled");
}
}
}

View file

@ -0,0 +1,77 @@
package com.jozufozu.flywheel.impl.instancing;
import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.instance.controller.BlockEntityInstancingController;
import com.jozufozu.flywheel.api.instance.controller.EntityInstancingController;
import com.jozufozu.flywheel.api.instance.controller.InstancingControllerRegistry;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
public final class InstancingControllerHelper {
@SuppressWarnings("unchecked")
@Nullable
public static <T extends BlockEntity> BlockEntityInstancingController<? super T> getController(T blockEntity) {
return InstancingControllerRegistry.getController((BlockEntityType<? super T>) blockEntity.getType());
}
@SuppressWarnings("unchecked")
@Nullable
public static <T extends Entity> EntityInstancingController<? super T> getController(T entity) {
return InstancingControllerRegistry.getController((EntityType<? super T>) entity.getType());
}
/**
* Checks if the given block entity can be instanced.
* @param type The block entity to check.
* @param <T> The block entity.
* @return {@code true} if the block entity can be instanced.
*/
public static <T extends BlockEntity> boolean canInstance(T blockEntity) {
return getController(blockEntity) != null;
}
/**
* Checks if the given entity can be instanced.
* @param type The entity to check.
* @param <T> The entity.
* @return {@code true} if the entity can be instanced.
*/
public static <T extends Entity> boolean canInstance(T entity) {
return getController(entity) != null;
}
/**
* Checks if the given block entity is instanced and should not be rendered normally.
* @param blockEntity The block entity to check.
* @param <T> The type of the block entity.
* @return {@code true} if the block entity is instanced and should not be rendered normally.
*/
public static <T extends BlockEntity> boolean shouldSkipRender(T blockEntity) {
BlockEntityInstancingController<? super T> controller = getController(blockEntity);
if (controller == null) {
return false;
}
return controller.shouldSkipRender(blockEntity);
}
/**
* Checks if the given entity is instanced and should not be rendered normally.
* @param entity The entity to check.
* @param <T> The type of the entity.
* @return {@code true} if the entity is instanced and should not be rendered normally.
*/
public static <T extends Entity> boolean shouldSkipRender(T entity) {
EntityInstancingController<? super T> controller = getController(entity);
if (controller == null) {
return false;
}
return controller.shouldSkipRender(entity);
}
private InstancingControllerHelper() {
}
}

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.instancing.manager; package com.jozufozu.flywheel.impl.instancing.manager;
import java.util.List; import java.util.List;
@ -8,11 +8,10 @@ import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.instance.BlockEntityInstance; import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.api.instance.controller.InstancingControllerRegistry;
import com.jozufozu.flywheel.backend.BackendUtil; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.storage.One2OneStorage; import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
import com.jozufozu.flywheel.backend.instancing.storage.Storage; import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper; import com.jozufozu.flywheel.impl.instancing.storage.Storage;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@ -46,7 +45,7 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
return false; return false;
} }
if (!InstancingControllerHelper.canInstance(blockEntity.getType())) { if (!InstancingControllerHelper.canInstance(blockEntity)) {
return false; return false;
} }
@ -81,17 +80,17 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
@Override @Override
@Nullable @Nullable
protected Instance createRaw(BlockEntity obj) { protected Instance createRaw(BlockEntity obj) {
var controller = InstancingControllerRegistry.getController(InstancingControllerHelper.getType(obj)); var controller = InstancingControllerHelper.getController(obj);
if (controller == null) { if (controller == null) {
return null; return null;
} }
var out = controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj); var instance = controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
BlockPos blockPos = obj.getBlockPos(); BlockPos blockPos = obj.getBlockPos();
posLookup.put(blockPos.asLong(), out); posLookup.put(blockPos.asLong(), instance);
return out; return instance;
} }
@Override @Override

View file

@ -0,0 +1,39 @@
package com.jozufozu.flywheel.impl.instancing.manager;
import java.util.Collection;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.api.instance.effect.Effect;
import com.jozufozu.flywheel.impl.instancing.storage.One2ManyStorage;
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
public class EffectInstanceManager extends InstanceManager<Effect> {
private final EffectStorage storage;
public EffectInstanceManager(Engine engine) {
storage = new EffectStorage(engine);
}
@Override
protected Storage<Effect> getStorage() {
return storage;
}
@Override
protected boolean canCreateInstance(Effect obj) {
return true;
}
private static class EffectStorage extends One2ManyStorage<Effect> {
public EffectStorage(Engine engine) {
super(engine);
}
@Override
protected Collection<? extends Instance> createRaw(Effect obj) {
return obj.createInstances(new InstanceContext(engine, engine.renderOrigin()));
}
}
}

View file

@ -1,15 +1,14 @@
package com.jozufozu.flywheel.backend.instancing.manager; package com.jozufozu.flywheel.impl.instancing.manager;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.jozufozu.flywheel.api.backend.Engine; import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.api.instance.controller.InstancingControllerRegistry;
import com.jozufozu.flywheel.backend.BackendUtil; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.storage.One2OneStorage; import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
import com.jozufozu.flywheel.backend.instancing.storage.Storage; import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper; import com.jozufozu.flywheel.impl.instancing.storage.Storage;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -32,7 +31,7 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
return false; return false;
} }
if (!InstancingControllerHelper.canInstance(entity.getType())) { if (!InstancingControllerHelper.canInstance(entity)) {
return false; return false;
} }
@ -49,10 +48,11 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
@Override @Override
@Nullable @Nullable
protected Instance createRaw(Entity obj) { protected Instance createRaw(Entity obj) {
var controller = InstancingControllerRegistry.getController(InstancingControllerHelper.getType(obj)); var controller = InstancingControllerHelper.getController(obj);
if (controller == null) { if (controller == null) {
return null; return null;
} }
return controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj); return controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
} }
} }

View file

@ -1,7 +1,5 @@
package com.jozufozu.flywheel.backend.instancing.manager; package com.jozufozu.flywheel.impl.instancing.manager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -9,28 +7,26 @@ import java.util.function.Consumer;
import org.joml.FrustumIntersection; import org.joml.FrustumIntersection;
import com.jozufozu.flywheel.api.backend.BackendManager;
import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.task.TaskExecutor; import com.jozufozu.flywheel.api.task.TaskExecutor;
import com.jozufozu.flywheel.backend.instancing.ratelimit.BandedPrimeLimiter;
import com.jozufozu.flywheel.backend.instancing.ratelimit.DistanceUpdateLimiter;
import com.jozufozu.flywheel.backend.instancing.ratelimit.NonLimiter;
import com.jozufozu.flywheel.backend.instancing.storage.Storage;
import com.jozufozu.flywheel.config.FlwConfig; import com.jozufozu.flywheel.config.FlwConfig;
import com.jozufozu.flywheel.impl.instancing.ratelimit.BandedPrimeLimiter;
import com.jozufozu.flywheel.impl.instancing.ratelimit.DistanceUpdateLimiter;
import com.jozufozu.flywheel.impl.instancing.ratelimit.NonLimiter;
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
public abstract class InstanceManager<T> { public abstract class InstanceManager<T> {
private final Set<T> queuedAdditions = new HashSet<>(64); private final Set<T> queuedAdditions = new HashSet<>(64);
private final Set<T> queuedUpdates = new HashSet<>(64); private final Set<T> queuedUpdates = new HashSet<>(64);
protected DistanceUpdateLimiter frameLimiter;
protected DistanceUpdateLimiter tickLimiter; protected DistanceUpdateLimiter tickLimiter;
protected DistanceUpdateLimiter frameLimiter;
public InstanceManager() { public InstanceManager() {
frameLimiter = createUpdateLimiter();
tickLimiter = createUpdateLimiter(); tickLimiter = createUpdateLimiter();
frameLimiter = createUpdateLimiter();
} }
protected abstract Storage<T> getStorage(); protected abstract Storage<T> getStorage();
@ -60,22 +56,18 @@ public abstract class InstanceManager<T> {
* @return The object count. * @return The object count.
*/ */
public int getInstanceCount() { public int getInstanceCount() {
return getStorage().getInstanceCount(); return getStorage().getAllInstances().size();
} }
public void add(T obj) { public void add(T obj) {
if (!BackendManager.isOn()) return; if (!canCreateInstance(obj)) {
if (canCreateInstance(obj)) {
getStorage().add(obj);
}
}
public void queueAdd(T obj) {
if (!BackendManager.isOn()) {
return; return;
} }
getStorage().add(obj);
}
public void queueAdd(T obj) {
if (!canCreateInstance(obj)) { if (!canCreateInstance(obj)) {
return; return;
} }
@ -85,38 +77,26 @@ public abstract class InstanceManager<T> {
} }
} }
public void queueAddAll(Collection<? extends T> objects) {
if (!BackendManager.isOn() || objects.isEmpty()) {
return;
}
synchronized (queuedAdditions) {
queuedAdditions.addAll(objects);
}
}
/** /**
* Update the instance associated with an object. * Update the instance associated with an object.
* *
* <p> * <p>
* By default this is the only hook an IInstance has to change its internal state. This is the lowest frequency * By default this is the only hook an {@link Instance} has to change its internal state. This is the lowest frequency
* update hook IInstance gets. For more frequent updates, see {@link TickableInstance} and * update hook {@link Instance} gets. For more frequent updates, see {@link TickableInstance} and
* {@link DynamicInstance}. * {@link DynamicInstance}.
* </p> * </p>
* *
* @param obj the object to update. * @param obj the object to update.
*/ */
public void update(T obj) { public void update(T obj) {
if (!BackendManager.isOn()) return; if (!canCreateInstance(obj)) {
return;
if (canCreateInstance(obj)) {
getStorage().update(obj);
} }
getStorage().update(obj);
} }
public void queueUpdate(T obj) { public void queueUpdate(T obj) {
if (!BackendManager.isOn()) return;
if (!canCreateInstance(obj)) { if (!canCreateInstance(obj)) {
return; return;
} }
@ -127,12 +107,10 @@ public abstract class InstanceManager<T> {
} }
public void remove(T obj) { public void remove(T obj) {
if (!BackendManager.isOn()) return;
getStorage().remove(obj); getStorage().remove(obj);
} }
public void onOriginShift() { public void recreateAll() {
getStorage().recreateAll(); getStorage().recreateAll();
} }
@ -140,21 +118,15 @@ public abstract class InstanceManager<T> {
getStorage().invalidate(); getStorage().invalidate();
} }
public void delete() {
for (Instance instance : getStorage().getAllInstances()) {
instance.delete();
}
}
protected void processQueuedAdditions() { protected void processQueuedAdditions() {
if (queuedAdditions.isEmpty()) { if (queuedAdditions.isEmpty()) {
return; return;
} }
ArrayList<T> queued; List<T> queued;
synchronized (queuedAdditions) { synchronized (queuedAdditions) {
queued = new ArrayList<>(queuedAdditions); queued = List.copyOf(queuedAdditions);
queuedAdditions.clear(); queuedAdditions.clear();
} }
@ -164,14 +136,18 @@ public abstract class InstanceManager<T> {
} }
protected void processQueuedUpdates() { protected void processQueuedUpdates() {
ArrayList<T> queued; if (queuedUpdates.isEmpty()) {
return;
}
List<T> queued;
synchronized (queuedUpdates) { synchronized (queuedUpdates) {
queued = new ArrayList<>(queuedUpdates); queued = List.copyOf(queuedUpdates);
queuedUpdates.clear(); queuedUpdates.clear();
} }
if (queued.size() > 0) { if (!queued.isEmpty()) {
queued.forEach(getStorage()::update); queued.forEach(getStorage()::update);
} }
} }
@ -187,40 +163,34 @@ public abstract class InstanceManager<T> {
*/ */
public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) { public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) {
tickLimiter.tick(); tickLimiter.tick();
processQueuedAdditions();
processQueuedUpdates(); processQueuedUpdates();
var instances = getStorage().getInstancesForTicking(); var instances = getStorage().getTickableInstances();
distributeWork(executor, instances, instance -> tickInstance(instance, cameraX, cameraY, cameraZ)); distributeWork(executor, instances, instance -> tickInstance(instance, cameraX, cameraY, cameraZ));
} }
protected void tickInstance(TickableInstance instance, double cX, double cY, double cZ) { protected void tickInstance(TickableInstance instance, double cameraX, double cameraY, double cameraZ) {
if (!instance.decreaseTickRateWithDistance()) { if (!instance.decreaseTickRateWithDistance() || tickLimiter.shouldUpdate(instance.distanceSquared(cameraX, cameraY, cameraZ))) {
instance.tick(); instance.tick();
return;
} }
var dsq = instance.distanceSquared(cX, cY, cZ);
if (!tickLimiter.shouldUpdate(dsq)) {
return;
}
instance.tick();
} }
public void beginFrame(TaskExecutor executor, RenderContext context) { public void beginFrame(TaskExecutor executor, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
frameLimiter.tick(); frameLimiter.tick();
processQueuedAdditions(); processQueuedAdditions();
processQueuedUpdates();
var cameraPos = context.camera() var instances = getStorage().getDynamicInstances();
.getPosition(); distributeWork(executor, instances, instance -> updateInstance(instance, cameraX, cameraY, cameraZ, frustum));
double cX = cameraPos.x; }
double cY = cameraPos.y;
double cZ = cameraPos.z;
FrustumIntersection culler = context.culler();
var instances = getStorage().getInstancesForUpdate(); protected void updateInstance(DynamicInstance instance, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
distributeWork(executor, instances, instance -> updateInstance(instance, culler, cX, cY, cZ)); if (!instance.decreaseFramerateWithDistance() || frameLimiter.shouldUpdate(instance.distanceSquared(cameraX, cameraY, cameraZ))) {
if (instance.isVisible(frustum)) {
instance.beginFrame();
}
}
} }
private static <I> void distributeWork(TaskExecutor executor, List<I> instances, Consumer<I> action) { private static <I> void distributeWork(TaskExecutor executor, List<I> instances, Consumer<I> action) {
@ -239,19 +209,4 @@ public abstract class InstanceManager<T> {
} }
} }
} }
protected void updateInstance(DynamicInstance instance, FrustumIntersection frustum, double cX, double cY, double cZ) {
if (!instance.decreaseFramerateWithDistance()) {
instance.beginFrame();
return;
}
if (!frameLimiter.shouldUpdate(instance.distanceSquared(cX, cY, cZ))) {
return;
}
if (instance.checkFrustum(frustum)) {
instance.beginFrame();
}
}
} }

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.instancing.ratelimit; package com.jozufozu.flywheel.impl.instancing.ratelimit;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.instancing.ratelimit; package com.jozufozu.flywheel.impl.instancing.ratelimit;
/** /**
* Interface for rate-limiting updates based on an object's distance from the camera. * Interface for rate-limiting updates based on an object's distance from the camera.

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.instancing.ratelimit; package com.jozufozu.flywheel.impl.instancing.ratelimit;
public class NonLimiter implements DistanceUpdateLimiter { public class NonLimiter implements DistanceUpdateLimiter {
@Override @Override

View file

@ -1,4 +1,4 @@
package com.jozufozu.flywheel.backend.instancing.storage; package com.jozufozu.flywheel.impl.instancing.storage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -18,12 +18,12 @@ public abstract class AbstractStorage<T> implements Storage<T> {
} }
@Override @Override
public List<TickableInstance> getInstancesForTicking() { public List<TickableInstance> getTickableInstances() {
return tickableInstances; return tickableInstances;
} }
@Override @Override
public List<DynamicInstance> getInstancesForUpdate() { public List<DynamicInstance> getDynamicInstances() {
return dynamicInstances; return dynamicInstances;
} }

View file

@ -0,0 +1,86 @@
package com.jozufozu.flywheel.impl.instancing.storage;
import java.util.Collection;
import java.util.List;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.instance.Instance;
public abstract class One2ManyStorage<T> extends AbstractStorage<T> {
private final Multimap<T, Instance> allInstances = HashMultimap.create();
public One2ManyStorage(Engine engine) {
super(engine);
}
@Override
public Collection<Instance> getAllInstances() {
return allInstances.values();
}
@Override
public void add(T obj) {
Collection<Instance> instances = allInstances.get(obj);
if (instances.isEmpty()) {
create(obj);
}
}
@Override
public void remove(T obj) {
Collection<Instance> instances = allInstances.removeAll(obj);
if (instances.isEmpty()) {
return;
}
tickableInstances.removeAll(instances);
dynamicInstances.removeAll(instances);
instances.forEach(Instance::delete);
}
@Override
public void update(T obj) {
Collection<Instance> instances = allInstances.get(obj);
if (instances.isEmpty()) {
return;
}
// TODO: shouldReset cannot be checked here because all instances are created at once
instances.forEach(Instance::update);
}
@Override
public void recreateAll() {
tickableInstances.clear();
dynamicInstances.clear();
allInstances.values().forEach(Instance::delete);
List<T> objects = List.copyOf(allInstances.keySet());
allInstances.clear();
objects.forEach(this::create);
}
@Override
public void invalidate() {
tickableInstances.clear();
dynamicInstances.clear();
allInstances.values().forEach(Instance::delete);
allInstances.clear();
}
private void create(T obj) {
Collection<? extends Instance> instances = createRaw(obj);
if (!instances.isEmpty()) {
instances.forEach(this::setup);
allInstances.putAll(obj, instances);
}
}
protected abstract Collection<? extends Instance> createRaw(T obj);
}

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.storage; package com.jozufozu.flywheel.impl.instancing.storage;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -9,23 +10,17 @@ import com.jozufozu.flywheel.api.backend.Engine;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.Instance;
public abstract class One2OneStorage<T> extends AbstractStorage<T> { public abstract class One2OneStorage<T> extends AbstractStorage<T> {
private final Map<T, Instance> instances; private final Map<T, Instance> instances = new HashMap<>();
public One2OneStorage(Engine engine) { public One2OneStorage(Engine engine) {
super(engine); super(engine);
this.instances = new HashMap<>();
} }
@Override @Override
public Iterable<Instance> getAllInstances() { public Collection<Instance> getAllInstances() {
return instances.values(); return instances.values();
} }
@Override
public int getInstanceCount() {
return instances.size();
}
@Override @Override
public void add(T obj) { public void add(T obj) {
Instance instance = instances.get(obj); Instance instance = instances.get(obj);
@ -43,9 +38,9 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
return; return;
} }
instance.delete();
dynamicInstances.remove(instance);
tickableInstances.remove(instance); tickableInstances.remove(instance);
dynamicInstances.remove(instance);
instance.delete();
} }
@Override @Override
@ -69,8 +64,8 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
@Override @Override
public void recreateAll() { public void recreateAll() {
dynamicInstances.clear();
tickableInstances.clear(); tickableInstances.clear();
dynamicInstances.clear();
instances.replaceAll((obj, instance) -> { instances.replaceAll((obj, instance) -> {
instance.delete(); instance.delete();
@ -86,10 +81,10 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
@Override @Override
public void invalidate() { public void invalidate() {
instances.values().forEach(Instance::delete);
instances.clear();
tickableInstances.clear(); tickableInstances.clear();
dynamicInstances.clear(); dynamicInstances.clear();
instances.values().forEach(Instance::delete);
instances.clear();
} }
private void create(T obj) { private void create(T obj) {

View file

@ -1,5 +1,6 @@
package com.jozufozu.flywheel.backend.instancing.storage; package com.jozufozu.flywheel.impl.instancing.storage;
import java.util.Collection;
import java.util.List; import java.util.List;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
@ -7,13 +8,11 @@ import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
public interface Storage<T> { public interface Storage<T> {
Iterable<Instance> getAllInstances(); Collection<Instance> getAllInstances();
int getInstanceCount(); List<TickableInstance> getTickableInstances();
List<TickableInstance> getInstancesForTicking(); List<DynamicInstance> getDynamicInstances();
List<DynamicInstance> getInstancesForUpdate();
void add(T obj); void add(T obj);

View file

@ -16,7 +16,7 @@ import net.minecraft.network.chat.TextComponent;
public class Backends { public class Backends {
public static final Backend OFF = SimpleBackend.builder() public static final Backend OFF = SimpleBackend.builder()
.engineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED)) .engineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED))
.engineSupplier(() -> { .engineFactory(level -> {
throw new IllegalStateException("Cannot create engine when backend is off."); throw new IllegalStateException("Cannot create engine when backend is off.");
}) })
.fallback(() -> Backends.OFF) .fallback(() -> Backends.OFF)
@ -28,7 +28,7 @@ public class Backends {
*/ */
public static final Backend BATCHING = SimpleBackend.builder() public static final Backend BATCHING = SimpleBackend.builder()
.engineMessage(new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN)) .engineMessage(new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN))
.engineSupplier(BatchingEngine::new) .engineFactory(level -> new BatchingEngine())
.fallback(() -> Backends.OFF) .fallback(() -> Backends.OFF)
.supported(() -> !ShadersModHandler.isShaderPackInUse()) .supported(() -> !ShadersModHandler.isShaderPackInUse())
.register(Flywheel.rl("batching")); .register(Flywheel.rl("batching"));
@ -38,7 +38,7 @@ public class Backends {
*/ */
public static final Backend INSTANCING = SimpleBackend.builder() public static final Backend INSTANCING = SimpleBackend.builder()
.engineMessage(new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN)) .engineMessage(new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN))
.engineSupplier(() -> new InstancingEngine(Contexts.WORLD, 100 * 100)) .engineFactory(level -> new InstancingEngine(Contexts.WORLD, 100))
.fallback(() -> Backends.BATCHING) .fallback(() -> Backends.BATCHING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance() .supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.instancedArraysSupported()) .instancedArraysSupported())
@ -50,7 +50,7 @@ public class Backends {
*/ */
public static final Backend INDIRECT = SimpleBackend.builder() public static final Backend INDIRECT = SimpleBackend.builder()
.engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN)) .engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
.engineSupplier(() -> new IndirectEngine(Contexts.WORLD, 100 * 100)) .engineFactory(level -> new IndirectEngine(100))
.fallback(() -> Backends.INSTANCING) .fallback(() -> Backends.INSTANCING)
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance() .supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
.supportsIndirect()) .supportsIndirect())

View file

@ -1,6 +1,7 @@
package com.jozufozu.flywheel.lib.backend; package com.jozufozu.flywheel.lib.backend;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -11,17 +12,18 @@ import com.jozufozu.flywheel.api.pipeline.Pipeline;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.LevelAccessor;
public class SimpleBackend implements Backend { public class SimpleBackend implements Backend {
private final Component engineMessage; private final Component engineMessage;
private final Supplier<Engine> engineSupplier; private final Function<LevelAccessor, Engine> engineFactory;
private final Supplier<Backend> fallback; private final Supplier<Backend> fallback;
private final BooleanSupplier isSupported; private final BooleanSupplier isSupported;
private final Pipeline pipelineShader; private final Pipeline pipelineShader;
public SimpleBackend(Component engineMessage, Supplier<Engine> engineSupplier, Supplier<Backend> fallback, BooleanSupplier isSupported, @Nullable Pipeline pipelineShader) { public SimpleBackend(Component engineMessage, Function<LevelAccessor, Engine> engineFactory, Supplier<Backend> fallback, BooleanSupplier isSupported, @Nullable Pipeline pipelineShader) {
this.engineMessage = engineMessage; this.engineMessage = engineMessage;
this.engineSupplier = engineSupplier; this.engineFactory = engineFactory;
this.fallback = fallback; this.fallback = fallback;
this.isSupported = isSupported; this.isSupported = isSupported;
this.pipelineShader = pipelineShader; this.pipelineShader = pipelineShader;
@ -37,8 +39,8 @@ public class SimpleBackend implements Backend {
} }
@Override @Override
public Engine createEngine() { public Engine createEngine(LevelAccessor level) {
return engineSupplier.get(); return engineFactory.apply(level);
} }
@Override @Override
@ -63,7 +65,7 @@ public class SimpleBackend implements Backend {
public static class Builder { public static class Builder {
private Component engineMessage; private Component engineMessage;
private Supplier<Engine> engineSupplier; private Function<LevelAccessor, Engine> engineFactory;
private Supplier<Backend> fallback; private Supplier<Backend> fallback;
private BooleanSupplier isSupported; private BooleanSupplier isSupported;
private Pipeline pipelineShader; private Pipeline pipelineShader;
@ -73,8 +75,8 @@ public class SimpleBackend implements Backend {
return this; return this;
} }
public Builder engineSupplier(Supplier<Engine> engineSupplier) { public Builder engineFactory(Function<LevelAccessor, Engine> engineFactory) {
this.engineSupplier = engineSupplier; this.engineFactory = engineFactory;
return this; return this;
} }
@ -94,7 +96,7 @@ public class SimpleBackend implements Backend {
} }
public Backend register(ResourceLocation id) { public Backend register(ResourceLocation id) {
return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineMessage, engineSupplier, fallback, isSupported, pipelineShader)); return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineMessage, engineFactory, fallback, isSupported, pipelineShader));
} }
} }
} }

View file

@ -1,16 +1,12 @@
package com.jozufozu.flywheel.lib.instance; package com.jozufozu.flywheel.lib.instance;
import java.util.ArrayList;
import java.util.List;
import org.joml.FrustumIntersection; import org.joml.FrustumIntersection;
import com.jozufozu.flywheel.api.instance.BlockEntityInstance; import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.impl.instancing.manager.BlockEntityInstanceManager;
import com.jozufozu.flywheel.backend.instancing.manager.BlockEntityInstanceManager;
import com.jozufozu.flywheel.lib.box.ImmutableBox; import com.jozufozu.flywheel.lib.box.ImmutableBox;
import com.jozufozu.flywheel.lib.box.MutableBox; import com.jozufozu.flywheel.lib.box.MutableBox;
@ -30,13 +26,12 @@ import net.minecraft.world.level.block.state.BlockState;
* </ul> * </ul>
* See the interfaces' documentation for more information about each one. * See the interfaces' documentation for more information about each one.
* *
* <br> Implementing one or more of these will give a {@link AbstractBlockEntityInstance} access * <br> Implementing one or more of these will give an {@link AbstractBlockEntityInstance} access
* to more interesting and regular points within a tick or a frame. * to more interesting and regular points within a tick or a frame.
* *
* @param <T> The type of {@link BlockEntity} your class is an instance of. * @param <T> The type of {@link BlockEntity} your class is an instance of.
*/ */
public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends AbstractInstance implements BlockEntityInstance<T> { public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends AbstractInstance implements BlockEntityInstance<T> {
protected final T blockEntity; protected final T blockEntity;
protected final BlockPos pos; protected final BlockPos pos;
protected final BlockPos instancePos; protected final BlockPos instancePos;
@ -50,17 +45,6 @@ public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends
this.instancePos = pos.subtract(renderOrigin); this.instancePos = pos.subtract(renderOrigin);
} }
@Override
public List<InstancedPart> getCrumblingParts() {
var out = new ArrayList<InstancedPart>();
addCrumblingParts(out);
return out;
}
public void addCrumblingParts(List<InstancedPart> data) {
}
/** /**
* Just before {@link #update()} would be called, {@code shouldReset()} is checked. * Just before {@link #update()} would be called, {@code shouldReset()} is checked.
* If this function returns {@code true}, then this instance will be {@link #delete removed}, * If this function returns {@code true}, then this instance will be {@link #delete removed},
@ -69,10 +53,21 @@ public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends
* *
* @return {@code true} if this instance should be discarded and refreshed. * @return {@code true} if this instance should be discarded and refreshed.
*/ */
@Override
public boolean shouldReset() { public boolean shouldReset() {
return blockEntity.getBlockState() != blockState; return blockEntity.getBlockState() != blockState;
} }
@Override
public double distanceSquared(double x, double y, double z) {
return pos.distToCenterSqr(x, y, z);
}
@Override
public ImmutableBox getVolume() {
return MutableBox.from(pos);
}
/** /**
* In order to accommodate for floating point precision errors at high coordinates, * In order to accommodate for floating point precision errors at high coordinates,
* {@link BlockEntityInstanceManager}s are allowed to arbitrarily adjust the origin, and * {@link BlockEntityInstanceManager}s are allowed to arbitrarily adjust the origin, and
@ -85,18 +80,7 @@ public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends
return pos.subtract(renderOrigin); return pos.subtract(renderOrigin);
} }
@Override public boolean isVisible(FrustumIntersection frustum) {
public ImmutableBox getVolume() {
return MutableBox.from(pos);
}
@Override
public boolean checkFrustum(FrustumIntersection frustum) {
return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1); return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
} }
@Override
public double distanceSquared(double x, double y, double z) {
return pos.distToCenterSqr(x, y, z);
}
} }

View file

@ -6,19 +6,19 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.EntityInstance; import com.jozufozu.flywheel.api.instance.EntityInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.backend.instancing.manager.BlockEntityInstanceManager; import com.jozufozu.flywheel.impl.instancing.manager.BlockEntityInstanceManager;
import com.jozufozu.flywheel.lib.box.ImmutableBox;
import com.jozufozu.flywheel.lib.box.MutableBox; import com.jozufozu.flywheel.lib.box.MutableBox;
import com.jozufozu.flywheel.lib.light.TickingLightListener; import com.jozufozu.flywheel.lib.light.TickingLightListener;
import com.mojang.math.Vector3f; import com.mojang.math.Vector3f;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
/** /**
* The layer between a {@link BlockEntity} and the Flywheel backend. * The layer between an {@link Entity} and the Flywheel backend.
* * * *
* <br><br> There are a few additional features that overriding classes can opt in to: * <br><br> There are a few additional features that overriding classes can opt in to:
* <ul> * <ul>
@ -27,13 +27,12 @@ import net.minecraft.world.phys.Vec3;
* </ul> * </ul>
* See the interfaces' documentation for more information about each one. * See the interfaces' documentation for more information about each one.
* *
* <br> Implementing one or more of these will give a {@link AbstractEntityInstance} access * <br> Implementing one or more of these will give an {@link AbstractEntityInstance} access
* to more interesting and regular points within a tick or a frame. * 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. * @param <E> The type of {@link Entity} your class is an instance of.
*/ */
public abstract class AbstractEntityInstance<E extends Entity> extends AbstractInstance implements EntityInstance<E>, TickingLightListener { public abstract class AbstractEntityInstance<E extends Entity> extends AbstractInstance implements EntityInstance<E>, TickingLightListener {
protected final E entity; protected final E entity;
protected final MutableBox bounds; protected final MutableBox bounds;
@ -44,7 +43,12 @@ public abstract class AbstractEntityInstance<E extends Entity> extends AbstractI
} }
@Override @Override
public MutableBox getVolume() { public double distanceSquared(double x, double y, double z) {
return entity.distanceToSqr(x, y, z);
}
@Override
public ImmutableBox getVolume() {
return bounds; return bounds;
} }
@ -87,14 +91,8 @@ public abstract class AbstractEntityInstance<E extends Entity> extends AbstractI
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()), (float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()), (float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ())); return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()), (float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()), (float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
} }
@Override public boolean isVisible(FrustumIntersection frustum) {
public boolean checkFrustum(FrustumIntersection frustum) {
AABB aabb = entity.getBoundingBox(); AABB aabb = entity.getBoundingBox();
return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ); return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
} }
@Override
public double distanceSquared(double x, double y, double z) {
return entity.distanceToSqr(x, y, z);
}
} }

View file

@ -1,14 +1,13 @@
package com.jozufozu.flywheel.lib.instance; package com.jozufozu.flywheel.lib.instance;
import java.util.Arrays;
import java.util.stream.Stream; import java.util.stream.Stream;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.Instance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.api.instancer.FlatLit;
import com.jozufozu.flywheel.api.instancer.InstancerProvider; import com.jozufozu.flywheel.api.instancer.InstancerProvider;
import com.jozufozu.flywheel.lib.light.LightListener; import com.jozufozu.flywheel.lib.light.LightListener;
import com.jozufozu.flywheel.lib.light.LightUpdater; import com.jozufozu.flywheel.lib.light.LightUpdater;
import com.jozufozu.flywheel.lib.struct.FlatLit;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos; import net.minecraft.core.SectionPos;
@ -17,23 +16,22 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.LightLayer;
public abstract class AbstractInstance implements Instance, LightListener { public abstract class AbstractInstance implements Instance, LightListener {
public final Level level; protected final InstancerProvider instancerProvider;
public final Vec3i renderOrigin; protected final Vec3i renderOrigin;
protected final Level level;
protected final InstancerProvider instancerManager;
protected boolean deleted = false; protected boolean deleted = false;
public AbstractInstance(InstanceContext ctx, Level level) { public AbstractInstance(InstanceContext ctx, Level level) {
this.instancerManager = ctx.instancerProvider(); this.instancerProvider = ctx.instancerProvider();
this.renderOrigin = ctx.renderOrigin(); this.renderOrigin = ctx.renderOrigin();
this.level = level; this.level = level;
} }
@Override @Override
public void init() { public void init() {
LightUpdater.get(level).addListener(this);
updateLight(); updateLight();
LightUpdater.get(level)
.addListener(this);
} }
@Override @Override
@ -65,31 +63,31 @@ public abstract class AbstractInstance implements Instance, LightListener {
deleted = true; deleted = true;
} }
@Override
public boolean isInvalid() {
return deleted;
}
@Override @Override
public void onLightUpdate(LightLayer type, SectionPos pos) { public void onLightUpdate(LightLayer type, SectionPos pos) {
updateLight(); updateLight();
} }
protected void relight(BlockPos pos, FlatLit<?>... models) { @Override
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), models); public boolean isInvalid() {
return deleted;
} }
protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> models) { protected void relight(BlockPos pos, FlatLit<?>... parts) {
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), models); relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
} }
protected void relight(int block, int sky, FlatLit<?>... models) { protected void relight(int block, int sky, FlatLit<?>... parts) {
relight(block, sky, Arrays.stream(models)); for (FlatLit<?> part : parts) {
part.setLight(block, sky);
}
} }
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> models) { protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> parts) {
models.forEach(model -> model.setBlockLight(block) relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
.setSkyLight(sky));
} }
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> parts) {
parts.forEach(model -> model.setLight(block, sky));
}
} }

View file

@ -1,85 +0,0 @@
package com.jozufozu.flywheel.lib.instance;
import com.jozufozu.flywheel.api.instance.controller.BlockEntityInstancingController;
import com.jozufozu.flywheel.api.instance.controller.EntityInstancingController;
import com.jozufozu.flywheel.api.instance.controller.InstancingControllerRegistry;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
public final class InstancingControllerHelper {
/**
* Checks if the given block entity type can be instanced.
* @param type The block entity type to check.
* @param <T> The type of the block entity.
* @return {@code true} if the block entity type can be instanced.
*/
public static <T extends BlockEntity> boolean canInstance(BlockEntityType<? extends T> type) {
return InstancingControllerRegistry.getController(type) != null;
}
/**
* Checks if the given entity type can be instanced.
* @param type The entity type to check.
* @param <T> The type of the entity.
* @return {@code true} if the entity type can be instanced.
*/
public static <T extends Entity> boolean canInstance(EntityType<? extends T> type) {
return InstancingControllerRegistry.getController(type) != null;
}
/**
* Checks if the given block entity is instanced and should not be rendered normally.
* @param blockEntity The block entity to check.
* @param <T> The type of the block entity.
* @return {@code true} if the block entity is instanced and should not be rendered normally.
*/
public static <T extends BlockEntity> boolean shouldSkipRender(T blockEntity) {
BlockEntityInstancingController<? super T> controller = InstancingControllerRegistry.getController(getType(blockEntity));
if (controller == null) {
return false;
}
return controller.shouldSkipRender(blockEntity);
}
/**
* Checks if the given entity is instanced and should not be rendered normally.
* @param entity The entity to check.
* @param <T> The type of the entity.
* @return {@code true} if the entity is instanced and should not be rendered normally.
*/
public static <T extends Entity> boolean shouldSkipRender(T entity) {
EntityInstancingController<? super T> controller = InstancingControllerRegistry.getController(getType(entity));
if (controller == null) {
return false;
}
return controller.shouldSkipRender(entity);
}
/**
* Gets the type of the given block entity.
* @param blockEntity The block entity to get the type of.
* @param <T> The type of the block entity.
* @return The {@link BlockEntityType} associated with the given block entity.
*/
@SuppressWarnings("unchecked")
public static <T extends BlockEntity> BlockEntityType<? super T> getType(T blockEntity) {
return (BlockEntityType<? super T>) blockEntity.getType();
}
/**
* Gets the type of the given entity.
* @param entity The entity to get the type of.
* @param <T> The type of the entity.
* @return The {@link EntityType} associated with the given entity.
*/
@SuppressWarnings("unchecked")
public static <T extends Entity> EntityType<? super T> getType(T entity) {
return (EntityType<? super T>) entity.getType();
}
private InstancingControllerHelper() {
}
}

View file

@ -13,7 +13,7 @@ import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.material.Material; import com.jozufozu.flywheel.api.material.Material;
import com.jozufozu.flywheel.api.vertex.ReusableVertexList; import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
import com.jozufozu.flywheel.api.vertex.VertexList; import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.api.vertex.VertexListProvider; import com.jozufozu.flywheel.api.vertex.VertexListProviderRegistry;
import com.jozufozu.flywheel.api.vertex.VertexType; import com.jozufozu.flywheel.api.vertex.VertexType;
import com.jozufozu.flywheel.lib.format.Formats; import com.jozufozu.flywheel.lib.format.Formats;
import com.jozufozu.flywheel.lib.material.Materials; import com.jozufozu.flywheel.lib.material.Materials;
@ -62,7 +62,7 @@ public class ModelUtil {
long srcPtr = MemoryUtil.memAddress(src); long srcPtr = MemoryUtil.memAddress(src);
long dstPtr = dst.ptr(); long dstPtr = dst.ptr();
ReusableVertexList srcList = VertexListProvider.get(srcFormat).createVertexList(); ReusableVertexList srcList = VertexListProviderRegistry.getProvider(srcFormat).createVertexList();
ReusableVertexList dstList = dstVertexType.createVertexList(); ReusableVertexList dstList = dstVertexType.createVertexList();
srcList.ptr(srcPtr); srcList.ptr(srcPtr);
dstList.ptr(dstPtr); dstList.ptr(dstPtr);

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.lib.struct; package com.jozufozu.flywheel.lib.struct;
import com.jozufozu.flywheel.api.instancer.FlatLit;
import com.jozufozu.flywheel.api.instancer.InstancedPart; import com.jozufozu.flywheel.api.instancer.InstancedPart;
import com.jozufozu.flywheel.api.struct.StructType; import com.jozufozu.flywheel.api.struct.StructType;

View file

@ -1,4 +1,6 @@
package com.jozufozu.flywheel.api.instancer; package com.jozufozu.flywheel.lib.struct;
import com.jozufozu.flywheel.api.instancer.InstancedPart;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.BlockAndTintGetter;
@ -27,9 +29,13 @@ public interface FlatLit<D extends InstancedPart & FlatLit<D>> {
*/ */
D setSkyLight(int skyLight); D setSkyLight(int skyLight);
default D setLight(int blockLight, int skyLight) {
return setBlockLight(blockLight)
.setSkyLight(skyLight);
}
default D updateLight(BlockAndTintGetter level, BlockPos pos) { default D updateLight(BlockAndTintGetter level, BlockPos pos) {
return setBlockLight(level.getBrightness(LightLayer.BLOCK, pos)) return setLight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos));
.setSkyLight(level.getBrightness(LightLayer.SKY, pos));
} }
int getPackedLight(); int getPackedLight();

View file

@ -8,7 +8,7 @@ import com.jozufozu.flywheel.Flywheel;
import com.jozufozu.flywheel.api.event.BeginFrameEvent; import com.jozufozu.flywheel.api.event.BeginFrameEvent;
import com.jozufozu.flywheel.api.event.RenderContext; import com.jozufozu.flywheel.api.event.RenderContext;
import com.jozufozu.flywheel.api.uniform.ShaderUniforms; import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.util.FlwUtil; import com.jozufozu.flywheel.util.FlwUtil;
import com.jozufozu.flywheel.util.MatrixUtil; import com.jozufozu.flywheel.util.MatrixUtil;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
@ -73,13 +73,13 @@ public class FlwShaderUniforms implements ShaderUniforms {
} }
RenderContext context = event.getContext(); RenderContext context = event.getContext();
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level()); Vec3i renderOrigin = InstancedRenderDispatcher.getRenderOrigin(context.level());
Vec3 camera = context.camera() Vec3 camera = context.camera()
.getPosition(); .getPosition();
var camX = (float) (camera.x - originCoordinate.getX()); var camX = (float) (camera.x - renderOrigin.getX());
var camY = (float) (camera.y - originCoordinate.getY()); var camY = (float) (camera.y - renderOrigin.getY());
var camZ = (float) (camera.z - originCoordinate.getZ()); var camZ = (float) (camera.z - renderOrigin.getZ());
// don't want to mutate viewProjection // don't want to mutate viewProjection
var vp = context.viewProjection() var vp = context.viewProjection()

View file

@ -9,9 +9,9 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.extension.ClientLevelExtension; import com.jozufozu.flywheel.extension.ClientLevelExtension;
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper; import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
@ -29,7 +29,7 @@ public abstract class ClientLevelMixin implements ClientLevelExtension {
@Inject(method = "entitiesForRendering", at = @At("RETURN"), cancellable = true) @Inject(method = "entitiesForRendering", at = @At("RETURN"), cancellable = true)
private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) { private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
if (BackendManager.isOn()) { if (BackendUtil.canUseInstancing((ClientLevel) (Object) this)) {
Iterable<Entity> entities = cir.getReturnValue(); Iterable<Entity> entities = cir.getReturnValue();
ArrayList<Entity> filtered = Lists.newArrayList(entities); ArrayList<Entity> filtered = Lists.newArrayList(entities);

View file

@ -8,8 +8,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.backend.BackendUtil; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper; import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@ -19,7 +19,7 @@ public class ChunkRebuildHooksMixin {
@Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true) @Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true)
private <E extends BlockEntity> void flywheel$addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set<BlockEntity> set, E be, CallbackInfo ci) { private <E extends BlockEntity> void flywheel$addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set<BlockEntity> set, E be, CallbackInfo ci) {
if (BackendUtil.canUseInstancing(be.getLevel())) { if (BackendUtil.canUseInstancing(be.getLevel())) {
if (InstancingControllerHelper.canInstance(be.getType())) if (InstancingControllerHelper.canInstance(be))
InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be); InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be);
if (InstancingControllerHelper.shouldSkipRender(be)) if (InstancingControllerHelper.shouldSkipRender(be))

View file

@ -7,8 +7,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
@ -22,10 +22,10 @@ public class InstanceAddMixin {
@Inject(method = "setBlockEntity", @Inject(method = "setBlockEntity",
at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")) at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"))
private void flywheel$onBlockEntityAdded(BlockEntity be, CallbackInfo ci) { private void flywheel$onBlockEntityAdded(BlockEntity blockEntity, CallbackInfo ci) {
if (level.isClientSide && BackendManager.isOn()) { if (level.isClientSide && BackendUtil.canUseInstancing(level)) {
InstancedRenderDispatcher.getBlockEntities(this.level) InstancedRenderDispatcher.getBlockEntities(level)
.add(be); .add(blockEntity);
} }
} }
} }

View file

@ -7,8 +7,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
@ -22,8 +22,8 @@ public class InstanceRemoveMixin {
@Inject(at = @At("TAIL"), method = "setRemoved") @Inject(at = @At("TAIL"), method = "setRemoved")
private void flywheel$removeInstance(CallbackInfo ci) { private void flywheel$removeInstance(CallbackInfo ci) {
if (level instanceof ClientLevel && BackendManager.isOn()) { if (level instanceof ClientLevel && BackendUtil.canUseInstancing(level)) {
InstancedRenderDispatcher.getBlockEntities(this.level) InstancedRenderDispatcher.getBlockEntities(level)
.remove((BlockEntity) (Object) this); .remove((BlockEntity) (Object) this);
} }
} }

View file

@ -6,8 +6,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.jozufozu.flywheel.api.backend.BackendManager; import com.jozufozu.flywheel.backend.BackendUtil;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.LevelRenderer;
@ -25,7 +25,7 @@ public class InstanceUpdateMixin {
*/ */
@Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V") @Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V")
private void flywheel$checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) { private void flywheel$checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
if (!BackendManager.isOn()) { if (!BackendUtil.canUseInstancing(level)) {
return; return;
} }
BlockEntity blockEntity = level.getBlockEntity(pos); BlockEntity blockEntity = level.getBlockEntity(pos);

View file

@ -78,14 +78,18 @@ public class WorldAttached<T> {
*/ */
@NotNull @NotNull
public T replace(LevelAccessor world, Consumer<T> finalizer) { public T replace(LevelAccessor world, Consumer<T> finalizer) {
T remove = attached.remove(world); remove(world, finalizer);
if (remove != null)
finalizer.accept(remove);
return get(world); return get(world);
} }
public void remove(LevelAccessor world, Consumer<T> finalizer) {
T removed = attached.remove(world);
if (removed != null)
finalizer.accept(removed);
}
/** /**
* Deletes all entries after calling a function on them. * Deletes all entries after calling a function on them.
* *

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -24,18 +23,22 @@ import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BellBlockEntity; import net.minecraft.world.level.block.entity.BellBlockEntity;
public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> implements DynamicInstance { public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> implements DynamicInstance {
private static final SimpleLazyModel MODEL = new SimpleLazyModel(BellInstance::createBellModel, Materials.BELL); private static final SimpleLazyModel MODEL = new SimpleLazyModel(BellInstance::createBellModel, Materials.BELL);
private final OrientedPart bell; private OrientedPart bell;
private float lastRingTime = Float.NaN; private float lastRingTime = Float.NaN;
public BellInstance(InstanceContext ctx, BellBlockEntity blockEntity) { public BellInstance(InstanceContext ctx, BellBlockEntity blockEntity) {
super(ctx, blockEntity); super(ctx, blockEntity);
}
@Override
public void init() {
bell = createBellInstance().setPivot(0.5f, 0.75f, 0.5f) bell = createBellInstance().setPivot(0.5f, 0.75f, 0.5f)
.setPosition(getInstancePosition()); .setPosition(getInstancePosition());
super.init();
} }
@Override @Override
@ -65,8 +68,8 @@ public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> i
} }
@Override @Override
public void addCrumblingParts(List<InstancedPart> data) { public List<InstancedPart> getCrumblingParts() {
Collections.addAll(data, bell); return List.of(bell);
} }
@Override @Override
@ -75,7 +78,7 @@ public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> i
} }
private OrientedPart createBellInstance() { private OrientedPart createBellInstance() {
return instancerManager.getInstancer(StructTypes.ORIENTED, MODEL, RenderStage.AFTER_BLOCK_ENTITIES) return instancerProvider.instancer(StructTypes.ORIENTED, MODEL, RenderStage.AFTER_BLOCK_ENTITIES)
.createInstance(); .createInstance();
} }

View file

@ -1,12 +1,9 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import org.jetbrains.annotations.NotNull;
import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
@ -36,24 +33,25 @@ import net.minecraft.world.level.block.entity.LidBlockEntity;
import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.block.state.properties.ChestType;
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends AbstractBlockEntityInstance<T> implements DynamicInstance { public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends AbstractBlockEntityInstance<T> implements DynamicInstance {
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createLidModel(type, mat), Materials.CHEST)); private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createLidModel(type, mat), Materials.CHEST));
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createBaseModel(type, mat), Materials.CHEST)); private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createBaseModel(type, mat), Materials.CHEST));
private final OrientedPart body; private OrientedPart body;
private final TransformedPart lid; private TransformedPart lid;
private final Float2FloatFunction lidProgress; private Float2FloatFunction lidProgress;
private final TextureAtlasSprite sprite; private TextureAtlasSprite sprite;
@NotNull private ChestType chestType;
private final ChestType chestType; private Quaternion baseRotation;
private final Quaternion baseRotation;
private float lastProgress = Float.NaN; private float lastProgress = Float.NaN;
public ChestInstance(InstanceContext ctx, T blockEntity) { public ChestInstance(InstanceContext ctx, T blockEntity) {
super(ctx, blockEntity); super(ctx, blockEntity);
}
@Override
public void init() {
Block block = blockState.getBlock(); Block block = blockState.getBlock();
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE; chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
@ -64,7 +62,6 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
lid = lidInstance(); lid = lidInstance();
if (block instanceof AbstractChestBlock<?> chestBlock) { if (block instanceof AbstractChestBlock<?> chestBlock) {
float horizontalAngle = blockState.getValue(ChestBlock.FACING).toYRot(); float horizontalAngle = blockState.getValue(ChestBlock.FACING).toYRot();
baseRotation = Vector3f.YP.rotationDegrees(-horizontalAngle); baseRotation = Vector3f.YP.rotationDegrees(-horizontalAngle);
@ -78,6 +75,8 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
baseRotation = Quaternion.ONE; baseRotation = Quaternion.ONE;
lidProgress = $ -> 0f; lidProgress = $ -> 0f;
} }
super.init();
} }
@Override @Override
@ -112,8 +111,8 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
} }
@Override @Override
public void addCrumblingParts(List<InstancedPart> data) { public List<InstancedPart> getCrumblingParts() {
Collections.addAll(data, body, lid); return List.of(body, lid);
} }
@Override @Override
@ -123,12 +122,12 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
} }
private OrientedPart baseInstance() { private OrientedPart baseInstance() {
return instancerManager.getInstancer(StructTypes.ORIENTED, BASE.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES) return instancerProvider.instancer(StructTypes.ORIENTED, BASE.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
.createInstance(); .createInstance();
} }
private TransformedPart lidInstance() { private TransformedPart lidInstance() {
return instancerManager.getInstancer(StructTypes.TRANSFORMED, LID.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES) return instancerProvider.instancer(StructTypes.TRANSFORMED, LID.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
.createInstance(); .createInstance();
} }

View file

@ -26,22 +26,26 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntityInstance<T> implements DynamicInstance, TickableInstance { public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntityInstance<T> implements DynamicInstance, TickableInstance {
private static final SimpleLazyModel MODEL = new SimpleLazyModel(MinecartInstance::getBodyModel, Materials.MINECART); private static final SimpleLazyModel MODEL = new SimpleLazyModel(MinecartInstance::getBodyModel, Materials.MINECART);
private final PoseStack stack = new PoseStack(); private final PoseStack stack = new PoseStack();
private final TransformedPart body; private TransformedPart body;
private TransformedPart contents; private TransformedPart contents;
private BlockState blockState; private BlockState blockState;
private boolean active; private boolean active;
public MinecartInstance(InstanceContext ctx, T entity) { public MinecartInstance(InstanceContext ctx, T entity) {
super(ctx, entity); super(ctx, entity);
}
@Override
public void init() {
body = getBody(); body = getBody();
blockState = entity.getDisplayBlockState(); blockState = entity.getDisplayBlockState();
contents = getContents(); contents = getContents();
super.init();
} }
@Override @Override
@ -167,12 +171,12 @@ public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntity
return null; return null;
} }
return instancerManager.getInstancer(StructTypes.TRANSFORMED, Models.block(blockState), RenderStage.AFTER_ENTITIES) return instancerProvider.instancer(StructTypes.TRANSFORMED, Models.block(blockState), RenderStage.AFTER_ENTITIES)
.createInstance(); .createInstance();
} }
private TransformedPart getBody() { private TransformedPart getBody() {
return instancerManager.getInstancer(StructTypes.TRANSFORMED, MODEL, RenderStage.AFTER_ENTITIES) return instancerProvider.instancer(StructTypes.TRANSFORMED, MODEL, RenderStage.AFTER_ENTITIES)
.createInstance(); .createInstance();
} }

View file

@ -1,6 +1,5 @@
package com.jozufozu.flywheel.vanilla; package com.jozufozu.flywheel.vanilla;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
@ -33,17 +32,20 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
private static final Function<TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize(it -> new SimpleLazyModel(() -> makeBaseModel(it), Materials.SHULKER)); private static final Function<TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize(it -> new SimpleLazyModel(() -> makeBaseModel(it), Materials.SHULKER));
private static final Function<TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize(it -> new SimpleLazyModel(() -> makeLidModel(it), Materials.SHULKER)); private static final Function<TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize(it -> new SimpleLazyModel(() -> makeLidModel(it), Materials.SHULKER));
private final TextureAtlasSprite texture; private TextureAtlasSprite texture;
private final TransformedPart base; private TransformedPart base;
private final TransformedPart lid; private TransformedPart lid;
private final PoseStack stack = new PoseStack(); private final PoseStack stack = new PoseStack();
private float lastProgress = Float.NaN; private float lastProgress = Float.NaN;
public ShulkerBoxInstance(InstanceContext ctx, ShulkerBoxBlockEntity blockEntity) { public ShulkerBoxInstance(InstanceContext ctx, ShulkerBoxBlockEntity blockEntity) {
super(ctx, blockEntity); super(ctx, blockEntity);
}
@Override
public void init() {
DyeColor color = blockEntity.getColor(); DyeColor color = blockEntity.getColor();
if (color == null) { if (color == null) {
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite(); texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite();
@ -67,6 +69,8 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
tstack.translateY(0.25); tstack.translateY(0.25);
lid = makeLidInstance().setTransform(stack); lid = makeLidInstance().setTransform(stack);
super.init();
} }
@Override @Override
@ -91,8 +95,8 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
} }
@Override @Override
public void addCrumblingParts(List<InstancedPart> data) { public List<InstancedPart> getCrumblingParts() {
Collections.addAll(data, base, lid); return List.of(base, lid);
} }
@Override @Override
@ -107,12 +111,12 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
} }
private TransformedPart makeBaseInstance() { private TransformedPart makeBaseInstance() {
return instancerManager.getInstancer(StructTypes.TRANSFORMED, BASE.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES) return instancerProvider.instancer(StructTypes.TRANSFORMED, BASE.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
.createInstance(); .createInstance();
} }
private TransformedPart makeLidInstance() { private TransformedPart makeLidInstance() {
return instancerManager.getInstancer(StructTypes.TRANSFORMED, LID.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES) return instancerProvider.instancer(StructTypes.TRANSFORMED, LID.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
.createInstance(); .createInstance();
} }

View file

@ -11,11 +11,11 @@ import org.joml.Vector3f;
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent; import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
import com.jozufozu.flywheel.api.event.RenderStage; import com.jozufozu.flywheel.api.event.RenderStage;
import com.jozufozu.flywheel.api.instance.DynamicInstance; import com.jozufozu.flywheel.api.instance.DynamicInstance;
import com.jozufozu.flywheel.api.instance.Instance; import com.jozufozu.flywheel.api.instance.EffectInstance;
import com.jozufozu.flywheel.api.instance.TickableInstance; import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.api.instance.controller.InstanceContext; import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
import com.jozufozu.flywheel.api.instance.effect.Effect; import com.jozufozu.flywheel.api.instance.effect.Effect;
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
import com.jozufozu.flywheel.lib.box.ImmutableBox; import com.jozufozu.flywheel.lib.box.ImmutableBox;
import com.jozufozu.flywheel.lib.box.MutableBox; import com.jozufozu.flywheel.lib.box.MutableBox;
import com.jozufozu.flywheel.lib.instance.AbstractInstance; import com.jozufozu.flywheel.lib.instance.AbstractInstance;
@ -110,7 +110,7 @@ public class ExampleEffect implements Effect {
} }
@Override @Override
public Collection<Instance> createInstances(InstanceContext ctx) { public Collection<EffectInstance<?>> createInstances(InstanceContext ctx) {
effects.clear(); effects.clear();
boids.clear(); boids.clear();
for (int i = 0; i < INSTANCE_COUNT; i++) { for (int i = 0; i < INSTANCE_COUNT; i++) {
@ -238,10 +238,10 @@ public class ExampleEffect implements Effect {
} }
} }
public class BoidInstance extends AbstractInstance implements DynamicInstance, TickableInstance { public class BoidInstance extends AbstractInstance implements EffectInstance<ExampleEffect>, DynamicInstance, TickableInstance {
private final Boid self; private final Boid self;
TransformedPart instance; private TransformedPart instance;
public BoidInstance(InstanceContext ctx, Level level, Boid self) { public BoidInstance(InstanceContext ctx, Level level, Boid self) {
super(ctx, level); super(ctx, level);
@ -250,11 +250,13 @@ public class ExampleEffect implements Effect {
@Override @Override
public void init() { public void init() {
instance = instancerManager.getInstancer(StructTypes.TRANSFORMED, Models.block(Blocks.SHROOMLIGHT.defaultBlockState()), RenderStage.AFTER_PARTICLES) instance = instancerProvider.instancer(StructTypes.TRANSFORMED, Models.block(Blocks.SHROOMLIGHT.defaultBlockState()), RenderStage.AFTER_PARTICLES)
.createInstance(); .createInstance();
instance.setBlockLight(15) instance.setBlockLight(15)
.setSkyLight(15); .setSkyLight(15);
super.init();
} }
@Override @Override
@ -297,7 +299,7 @@ public class ExampleEffect implements Effect {
} }
@Override @Override
public boolean checkFrustum(FrustumIntersection frustum) { public boolean isVisible(FrustumIntersection frustum) {
return true; return true;
} }