mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-11 20:55:00 +01:00
Instance Refactor II
This commit is contained in:
parent
227aef509b
commit
391adfef1a
70 changed files with 972 additions and 1020 deletions
|
@ -3,17 +3,17 @@ package com.jozufozu.flywheel;
|
|||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||
import com.jozufozu.flywheel.backend.Loader;
|
||||
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.FlwCommands;
|
||||
import com.jozufozu.flywheel.config.FlwConfig;
|
||||
import com.jozufozu.flywheel.handler.EntityWorldHandler;
|
||||
import com.jozufozu.flywheel.handler.ForgeEvents;
|
||||
import com.jozufozu.flywheel.impl.BackendManagerImpl;
|
||||
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
||||
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.context.Contexts;
|
||||
import com.jozufozu.flywheel.lib.format.Formats;
|
||||
|
@ -115,7 +115,7 @@ public class Flywheel {
|
|||
|
||||
VanillaInstances.init();
|
||||
|
||||
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManager::getBackendNameForCrashReport);
|
||||
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendNameForCrashReport);
|
||||
|
||||
// https://github.com/Jozufozu/Flywheel/issues/69
|
||||
// Weird issue with accessor loading.
|
||||
|
@ -129,7 +129,7 @@ public class Flywheel {
|
|||
RegistryImpl.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() {
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.jozufozu.flywheel.api.registry.IdRegistry;
|
|||
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
public interface Backend {
|
||||
static IdRegistry<Backend> REGISTRY = IdRegistryImpl.create();
|
||||
|
@ -19,7 +20,7 @@ public interface Backend {
|
|||
/**
|
||||
* Create a new engine instance.
|
||||
*/
|
||||
Engine createEngine();
|
||||
Engine createEngine(LevelAccessor level);
|
||||
|
||||
/**
|
||||
* Get a fallback backend in case this backend is not supported.
|
||||
|
|
|
@ -13,13 +13,6 @@ public final class BackendManager {
|
|||
return BackendManagerImpl.getBackend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a string describing the current backend.
|
||||
*/
|
||||
public static String getBackendNameForCrashReport() {
|
||||
return BackendManagerImpl.getBackendNameForCrashReport();
|
||||
}
|
||||
|
||||
public static boolean isOn() {
|
||||
return BackendManagerImpl.isOn();
|
||||
}
|
||||
|
|
|
@ -2,11 +2,30 @@ package com.jozufozu.flywheel.api.backend;
|
|||
|
||||
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.backend.instancing.manager.InstanceManager;
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
|
||||
public interface Engine extends RenderDispatcher, InstancerProvider {
|
||||
void attachManagers(InstanceManager<?>... listener);
|
||||
import net.minecraft.client.Camera;
|
||||
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 delete();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.jozufozu.flywheel.api.instance;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
|
||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||
|
||||
|
@ -34,4 +36,13 @@ public interface DynamicInstance extends Instance {
|
|||
default boolean decreaseFramerateWithDistance() {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -2,5 +2,5 @@ package com.jozufozu.flywheel.api.instance;
|
|||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
public interface EntityInstance<E extends Entity> extends Instance {
|
||||
public interface EntityInstance<T extends Entity> extends Instance {
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
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.
|
||||
*/
|
||||
|
@ -33,15 +31,6 @@ public interface Instance {
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -56,5 +45,4 @@ public interface Instance {
|
|||
* Free any acquired resources.
|
||||
*/
|
||||
void delete();
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import net.minecraft.world.level.block.entity.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 blockEntity The block entity to construct an instance for.
|
||||
|
|
|
@ -10,7 +10,7 @@ import net.minecraft.world.entity.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 entity The entity to construct an instance for.
|
||||
|
|
|
@ -12,5 +12,4 @@ import net.minecraft.core.Vec3i;
|
|||
* All models render as if this position is (0, 0, 0).
|
||||
*/
|
||||
public record InstanceContext(InstancerProvider instancerProvider, Vec3i renderOrigin) {
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.api.instance.controller;
|
|||
|
||||
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.EntityType;
|
||||
|
|
|
@ -2,9 +2,11 @@ package com.jozufozu.flywheel.api.instance.effect;
|
|||
|
||||
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;
|
||||
|
||||
// TODO: add level getter?
|
||||
// TODO: return single instance instead of many?
|
||||
public interface Effect {
|
||||
Collection<Instance> createInstances(InstanceContext ctx);
|
||||
Collection<EffectInstance<?>> createInstances(InstanceContext ctx);
|
||||
}
|
||||
|
|
|
@ -10,5 +10,5 @@ public interface InstancerProvider {
|
|||
*
|
||||
* @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);
|
||||
}
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
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 {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -8,12 +8,9 @@ import com.jozufozu.flywheel.api.backend.BackendManager;
|
|||
import com.jozufozu.flywheel.backend.task.ParallelTaskExecutor;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
public class BackendUtil {
|
||||
public static final boolean DUMP_SHADER_SOURCE = System.getProperty("flw.dumpShaderSource") != null;
|
||||
|
||||
private static ParallelTaskExecutor executor;
|
||||
|
||||
/**
|
||||
|
@ -30,7 +27,7 @@ public class BackendUtil {
|
|||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
public static boolean canUseInstancing(@Nullable Level level) {
|
||||
public static boolean canUseInstancing(@Nullable LevelAccessor 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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
@ -50,8 +53,4 @@ public class BackendUtil {
|
|||
public static boolean isGameActive() {
|
||||
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
|
||||
}
|
||||
|
||||
public static void reloadWorldRenderers() {
|
||||
Minecraft.getInstance().levelRenderer.allChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package com.jozufozu.flywheel.backend;
|
|||
|
||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||
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.error.ErrorReporter;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
@ -39,8 +39,8 @@ public class Loader implements ResourceManagerReloadListener {
|
|||
FlwCompiler.INSTANCE = new FlwCompiler(sources);
|
||||
|
||||
ClientLevel level = Minecraft.getInstance().level;
|
||||
if (BackendUtil.canUseInstancing(level)) {
|
||||
InstancedRenderDispatcher.resetInstanceLevel(level);
|
||||
if (level != null) {
|
||||
InstancedRenderDispatcher.resetInstanceWorld(level);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import org.jetbrains.annotations.NotNull;
|
|||
import org.lwjgl.opengl.GL20;
|
||||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.gl.GLSLVersion;
|
||||
import com.jozufozu.flywheel.gl.shader.GlShader;
|
||||
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||
|
@ -29,6 +28,8 @@ import net.minecraft.resources.ResourceLocation;
|
|||
* and interprets/pretty prints any errors that occur.
|
||||
*/
|
||||
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<ResourceLocation> componentNames = new ArrayList<>();
|
||||
private final StringBuilder generatedSource;
|
||||
|
@ -131,7 +132,7 @@ public class Compilation {
|
|||
}
|
||||
|
||||
private static void dumpSource(String source, String fileName) {
|
||||
if (!BackendUtil.DUMP_SHADER_SOURCE) {
|
||||
if (!DUMP_SHADER_SOURCE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import org.lwjgl.system.MemoryUtil;
|
|||
import com.jozufozu.flywheel.Flywheel;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
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.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
|
@ -35,7 +35,7 @@ public class BatchedMeshPool {
|
|||
*/
|
||||
public BatchedMeshPool(VertexFormat vertexFormat) {
|
||||
this.vertexFormat = vertexFormat;
|
||||
vertexList = VertexListProvider.get(vertexFormat).createVertexList();
|
||||
vertexList = VertexListProviderRegistry.getProvider(vertexFormat).createVertexList();
|
||||
growthMargin = vertexFormat.getVertexSize() * 32;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
|
|||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.api.struct.StructType;
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
|
@ -21,11 +20,11 @@ import net.minecraft.core.Vec3i;
|
|||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class BatchingEngine implements Engine {
|
||||
protected final BatchingTransformManager transformManager = new BatchingTransformManager();
|
||||
protected final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
|
||||
private final BatchingTransformManager transformManager = new BatchingTransformManager();
|
||||
private final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
@ -42,7 +41,7 @@ public class BatchingEngine implements Engine {
|
|||
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()) {
|
||||
var stage = transformSetEntry.getKey();
|
||||
var transformSet = transformSetEntry.getValue();
|
||||
|
@ -79,16 +78,10 @@ public class BatchingEngine implements Engine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean maintainOriginCoordinate(Camera camera) {
|
||||
// do nothing
|
||||
public boolean updateRenderOrigin(Camera camera) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachManagers(InstanceManager<?>... listener) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3i renderOrigin() {
|
||||
return BlockPos.ZERO;
|
||||
|
|
|
@ -7,6 +7,7 @@ import org.jetbrains.annotations.ApiStatus;
|
|||
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexListProviderRegistry;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
|
@ -24,7 +25,7 @@ public class DrawBufferSet {
|
|||
this.renderType = renderType;
|
||||
format = renderType.format();
|
||||
stride = format.getVertexSize();
|
||||
provider = VertexListProvider.get(format);
|
||||
provider = VertexListProviderRegistry.getProvider(format);
|
||||
}
|
||||
|
||||
public DrawBuffer getBuffer(RenderStage stage) {
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package com.jozufozu.flywheel.backend.engine.indirect;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
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.RenderStage;
|
||||
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.struct.StructType;
|
||||
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.GlTextureUnit;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class IndirectEngine implements Engine {
|
||||
private final int sqrMaxOriginDistance;
|
||||
|
||||
protected final IndirectDrawManager drawManager = new IndirectDrawManager();
|
||||
private final IndirectDrawManager drawManager = new IndirectDrawManager();
|
||||
|
||||
/**
|
||||
* The set of instance managers that are attached to this engine.
|
||||
*/
|
||||
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
||||
private BlockPos renderOrigin = BlockPos.ZERO;
|
||||
|
||||
protected final Context context;
|
||||
protected final int sqrMaxOriginDistance;
|
||||
|
||||
protected BlockPos originCoordinate = BlockPos.ZERO;
|
||||
|
||||
public IndirectEngine(Context context, int sqrMaxOriginDistance) {
|
||||
this.context = context;
|
||||
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
||||
public IndirectEngine(int maxOriginDistance) {
|
||||
this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
@ -70,7 +56,7 @@ public class IndirectEngine implements Engine {
|
|||
}
|
||||
}
|
||||
|
||||
protected void setup() {
|
||||
private void setup() {
|
||||
GlTextureUnit.T2.makeActive();
|
||||
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
||||
|
||||
|
@ -82,36 +68,25 @@ public class IndirectEngine implements Engine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean maintainOriginCoordinate(Camera camera) {
|
||||
public boolean updateRenderOrigin(Camera camera) {
|
||||
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)
|
||||
.subtract(cameraPos)
|
||||
.lengthSqr();
|
||||
|
||||
if (distanceSqr > sqrMaxOriginDistance) {
|
||||
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
|
||||
return true;
|
||||
if (distanceSqr <= sqrMaxOriginDistance) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void shiftListeners(int cX, int cY, int cZ) {
|
||||
originCoordinate = new BlockPos(cX, cY, cZ);
|
||||
|
||||
renderOrigin = new BlockPos(cameraPos);
|
||||
drawManager.clearInstancers();
|
||||
|
||||
instanceManagers.forEach(InstanceManager::onOriginShift);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachManagers(InstanceManager<?>... listener) {
|
||||
Collections.addAll(instanceManagers, listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3i renderOrigin() {
|
||||
return originCoordinate;
|
||||
return renderOrigin;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -122,6 +97,6 @@ public class IndirectEngine implements Engine {
|
|||
@Override
|
||||
public void addDebugInfo(List<String> info) {
|
||||
info.add("GL46 Indirect");
|
||||
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
||||
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
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.backend.compile.FlwCompiler;
|
||||
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.GlTextureUnit;
|
||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||
import com.jozufozu.flywheel.lib.pipeline.Pipelines;
|
||||
import com.jozufozu.flywheel.util.FlwUtil;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
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();
|
||||
|
||||
/**
|
||||
* The set of instance managers that are attached to this engine.
|
||||
*/
|
||||
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
||||
private BlockPos renderOrigin = BlockPos.ZERO;
|
||||
|
||||
protected final Context context;
|
||||
protected final int sqrMaxOriginDistance;
|
||||
|
||||
protected BlockPos originCoordinate = BlockPos.ZERO;
|
||||
|
||||
public InstancingEngine(Context context, int sqrMaxOriginDistance) {
|
||||
public InstancingEngine(Context context, int maxOriginDistance) {
|
||||
this.context = context;
|
||||
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
||||
this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
|
@ -78,7 +67,7 @@ public class InstancingEngine implements Engine {
|
|||
}
|
||||
}
|
||||
|
||||
protected void setup() {
|
||||
private void setup() {
|
||||
GlTextureUnit.T2.makeActive();
|
||||
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
||||
|
||||
|
@ -89,7 +78,7 @@ public class InstancingEngine implements Engine {
|
|||
RenderSystem.enableCull();
|
||||
}
|
||||
|
||||
protected void render(InstancingDrawManager.DrawSet drawSet) {
|
||||
private void render(InstancingDrawManager.DrawSet drawSet) {
|
||||
for (var entry : drawSet) {
|
||||
var shader = entry.getKey();
|
||||
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 structType = desc.instance();
|
||||
var material = desc.material();
|
||||
|
@ -127,36 +116,25 @@ public class InstancingEngine implements Engine {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean maintainOriginCoordinate(Camera camera) {
|
||||
public boolean updateRenderOrigin(Camera camera) {
|
||||
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)
|
||||
.subtract(cameraPos)
|
||||
.lengthSqr();
|
||||
|
||||
if (distanceSqr > sqrMaxOriginDistance) {
|
||||
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
|
||||
return true;
|
||||
if (distanceSqr <= sqrMaxOriginDistance) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void shiftListeners(int cX, int cY, int cZ) {
|
||||
originCoordinate = new BlockPos(cX, cY, cZ);
|
||||
|
||||
renderOrigin = new BlockPos(cameraPos);
|
||||
drawManager.clearInstancers();
|
||||
|
||||
instanceManagers.forEach(InstanceManager::onOriginShift);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attachManagers(InstanceManager<?>... listener) {
|
||||
Collections.addAll(instanceManagers, listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3i renderOrigin() {
|
||||
return originCoordinate;
|
||||
return renderOrigin;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,6 +145,6 @@ public class InstancingEngine implements Engine {
|
|||
@Override
|
||||
public void addDebugInfo(List<String> info) {
|
||||
info.add("GL33 Instanced Arrays");
|
||||
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
||||
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,9 +17,7 @@ import net.minecraft.commands.SharedSuggestionProvider;
|
|||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public enum BackendArgument implements ArgumentType<Backend> {
|
||||
INSTANCE;
|
||||
|
||||
public class BackendArgument implements ArgumentType<Backend> {
|
||||
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds().stream().map(ResourceLocation::toString).toList();
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
public static BackendArgument getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
public static final BackendArgument INSTANCE = new BackendArgument();
|
||||
|
||||
@Override
|
||||
public Backend parse(StringReader reader) throws CommandSyntaxException {
|
||||
|
|
|
@ -3,10 +3,8 @@ package com.jozufozu.flywheel.config;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
import com.jozufozu.flywheel.api.backend.Backend;
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
|
||||
|
@ -30,9 +28,9 @@ public class FlwCommands {
|
|||
public static void registerClientCommands(RegisterClientCommandsEvent event) {
|
||||
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
|
||||
.executes(context -> {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
|
@ -68,12 +66,12 @@ public class FlwCommands {
|
|||
Component message = backend.engineMessage();
|
||||
player.displayClientMessage(message, false);
|
||||
|
||||
BackendUtil.reloadWorldRenderers();
|
||||
Minecraft.getInstance().levelRenderer.allChanged();
|
||||
}
|
||||
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) -> {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if (player == null) return;
|
||||
|
@ -88,12 +86,12 @@ public class FlwCommands {
|
|||
Component text = boolToText(bool).append(new TextComponent(" update limiting.").withStyle(ChatFormatting.WHITE));
|
||||
player.displayClientMessage(text, false);
|
||||
|
||||
BackendUtil.reloadWorldRenderers();
|
||||
Minecraft.getInstance().levelRenderer.allChanged();
|
||||
}
|
||||
));
|
||||
|
||||
// TODO
|
||||
commandBuilder.command.then(Commands.literal("debugNormals"))
|
||||
command.then(Commands.literal("debugNormals"))
|
||||
.executes(context -> {
|
||||
LocalPlayer player = Minecraft.getInstance().player;
|
||||
if (player == null) return 0;
|
||||
|
@ -103,7 +101,7 @@ public class FlwCommands {
|
|||
return Command.SINGLE_SUCCESS;
|
||||
});
|
||||
|
||||
commandBuilder.command.then(Commands.literal("debugCrumbling")
|
||||
command.then(Commands.literal("debugCrumbling")
|
||||
.then(Commands.argument("pos", BlockPosArgument.blockPos())
|
||||
.then(Commands.argument("stage", IntegerArgumentType.integer(0, 9))
|
||||
.executes(context -> {
|
||||
|
@ -122,7 +120,7 @@ public class FlwCommands {
|
|||
return Command.SINGLE_SUCCESS;
|
||||
}))));
|
||||
|
||||
commandBuilder.command.then(Commands.literal("debugFrustum")
|
||||
command.then(Commands.literal("debugFrustum")
|
||||
.then(Commands.literal("pause")
|
||||
.executes(context -> {
|
||||
FlwShaderUniforms.FRUSTUM_PAUSED = true;
|
||||
|
@ -139,10 +137,16 @@ public class FlwCommands {
|
|||
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
|
||||
.executes(context -> {
|
||||
displayAction.accept(context.getSource(), value.get());
|
||||
|
@ -165,22 +169,4 @@ public class FlwCommands {
|
|||
public static MutableComponent boolToText(boolean b) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,34 @@
|
|||
package com.jozufozu.flywheel.handler;
|
||||
|
||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
import net.minecraftforge.event.entity.EntityLeaveWorldEvent;
|
||||
|
||||
public class EntityWorldHandler {
|
||||
public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
|
||||
if (event.getWorld().isClientSide && BackendManager.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
|
||||
.queueAdd(event.getEntity());
|
||||
Level level = event.getWorld();
|
||||
if (!level.isClientSide) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BackendUtil.canUseInstancing(level)) {
|
||||
InstancedRenderDispatcher.getEntities(level)
|
||||
.queueAdd(event.getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
|
||||
if (event.getWorld().isClientSide && BackendManager.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
|
||||
.remove(event.getEntity());
|
||||
Level level = event.getWorld();
|
||||
if (!level.isClientSide) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (BackendUtil.canUseInstancing(level)) {
|
||||
InstancedRenderDispatcher.getEntities(level)
|
||||
.remove(event.getEntity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.util.ArrayList;
|
|||
|
||||
import com.jozufozu.flywheel.Flywheel;
|
||||
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.memory.FlwMemoryTracker;
|
||||
import com.jozufozu.flywheel.util.StringUtil;
|
||||
|
@ -16,14 +16,13 @@ import net.minecraftforge.event.TickEvent;
|
|||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
public class ForgeEvents {
|
||||
|
||||
public static void addToDebugScreen(RenderGameOverlayEvent.Text event) {
|
||||
if (Minecraft.getInstance().options.renderDebug) {
|
||||
ArrayList<String> debug = event.getRight();
|
||||
debug.add("");
|
||||
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()));
|
||||
}
|
||||
|
@ -39,5 +38,4 @@ public class ForgeEvents {
|
|||
.tick();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,17 +18,6 @@ public final class BackendManagerImpl {
|
|||
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() {
|
||||
return backend != null && backend != Backends.OFF;
|
||||
}
|
||||
|
@ -58,6 +47,17 @@ public final class BackendManagerImpl {
|
|||
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() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.impl.instance;
|
||||
package com.jozufozu.flywheel.impl;
|
||||
|
||||
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.BlockEntityType;
|
||||
|
||||
//TODO: Add freezing
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class InstancingControllerRegistryImpl {
|
||||
@Nullable
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.manager;
|
||||
package com.jozufozu.flywheel.impl.instancing.manager;
|
||||
|
||||
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.Instance;
|
||||
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.instancing.storage.One2OneStorage;
|
||||
import com.jozufozu.flywheel.backend.instancing.storage.Storage;
|
||||
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||
import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
|
||||
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
|
@ -46,7 +45,7 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!InstancingControllerHelper.canInstance(blockEntity.getType())) {
|
||||
if (!InstancingControllerHelper.canInstance(blockEntity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -81,17 +80,17 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
|||
@Override
|
||||
@Nullable
|
||||
protected Instance createRaw(BlockEntity obj) {
|
||||
var controller = InstancingControllerRegistry.getController(InstancingControllerHelper.getType(obj));
|
||||
var controller = InstancingControllerHelper.getController(obj);
|
||||
if (controller == 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();
|
||||
posLookup.put(blockPos.asLong(), out);
|
||||
posLookup.put(blockPos.asLong(), instance);
|
||||
|
||||
return out;
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,14 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.manager;
|
||||
package com.jozufozu.flywheel.impl.instancing.manager;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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.controller.InstancingControllerRegistry;
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.backend.instancing.storage.One2OneStorage;
|
||||
import com.jozufozu.flywheel.backend.instancing.storage.Storage;
|
||||
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||
import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
|
||||
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
@ -32,7 +31,7 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!InstancingControllerHelper.canInstance(entity.getType())) {
|
||||
if (!InstancingControllerHelper.canInstance(entity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -49,10 +48,11 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
|
|||
@Override
|
||||
@Nullable
|
||||
protected Instance createRaw(Entity obj) {
|
||||
var controller = InstancingControllerRegistry.getController(InstancingControllerHelper.getType(obj));
|
||||
var controller = InstancingControllerHelper.getController(obj);
|
||||
if (controller == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
|
||||
}
|
||||
}
|
|
@ -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.List;
|
||||
import java.util.Set;
|
||||
|
@ -9,28 +7,26 @@ import java.util.function.Consumer;
|
|||
|
||||
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.Instance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
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.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> {
|
||||
private final Set<T> queuedAdditions = new HashSet<>(64);
|
||||
private final Set<T> queuedUpdates = new HashSet<>(64);
|
||||
|
||||
protected DistanceUpdateLimiter frameLimiter;
|
||||
protected DistanceUpdateLimiter tickLimiter;
|
||||
protected DistanceUpdateLimiter frameLimiter;
|
||||
|
||||
public InstanceManager() {
|
||||
frameLimiter = createUpdateLimiter();
|
||||
tickLimiter = createUpdateLimiter();
|
||||
frameLimiter = createUpdateLimiter();
|
||||
}
|
||||
|
||||
protected abstract Storage<T> getStorage();
|
||||
|
@ -60,22 +56,18 @@ public abstract class InstanceManager<T> {
|
|||
* @return The object count.
|
||||
*/
|
||||
public int getInstanceCount() {
|
||||
return getStorage().getInstanceCount();
|
||||
return getStorage().getAllInstances().size();
|
||||
}
|
||||
|
||||
public void add(T obj) {
|
||||
if (!BackendManager.isOn()) return;
|
||||
|
||||
if (canCreateInstance(obj)) {
|
||||
getStorage().add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public void queueAdd(T obj) {
|
||||
if (!BackendManager.isOn()) {
|
||||
if (!canCreateInstance(obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
getStorage().add(obj);
|
||||
}
|
||||
|
||||
public void queueAdd(T obj) {
|
||||
if (!canCreateInstance(obj)) {
|
||||
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.
|
||||
*
|
||||
* <p>
|
||||
* By default this is the only hook an IInstance has to change its internal state. This is the lowest frequency
|
||||
* update hook IInstance gets. For more frequent updates, see {@link TickableInstance} and
|
||||
* By default this is the only hook an {@link Instance} has to change its internal state. This is the lowest frequency
|
||||
* update hook {@link Instance} gets. For more frequent updates, see {@link TickableInstance} and
|
||||
* {@link DynamicInstance}.
|
||||
* </p>
|
||||
*
|
||||
* @param obj the object to update.
|
||||
*/
|
||||
public void update(T obj) {
|
||||
if (!BackendManager.isOn()) return;
|
||||
|
||||
if (canCreateInstance(obj)) {
|
||||
getStorage().update(obj);
|
||||
if (!canCreateInstance(obj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
getStorage().update(obj);
|
||||
}
|
||||
|
||||
public void queueUpdate(T obj) {
|
||||
if (!BackendManager.isOn()) return;
|
||||
|
||||
if (!canCreateInstance(obj)) {
|
||||
return;
|
||||
}
|
||||
|
@ -127,12 +107,10 @@ public abstract class InstanceManager<T> {
|
|||
}
|
||||
|
||||
public void remove(T obj) {
|
||||
if (!BackendManager.isOn()) return;
|
||||
|
||||
getStorage().remove(obj);
|
||||
}
|
||||
|
||||
public void onOriginShift() {
|
||||
public void recreateAll() {
|
||||
getStorage().recreateAll();
|
||||
}
|
||||
|
||||
|
@ -140,21 +118,15 @@ public abstract class InstanceManager<T> {
|
|||
getStorage().invalidate();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
for (Instance instance : getStorage().getAllInstances()) {
|
||||
instance.delete();
|
||||
}
|
||||
}
|
||||
|
||||
protected void processQueuedAdditions() {
|
||||
if (queuedAdditions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArrayList<T> queued;
|
||||
List<T> queued;
|
||||
|
||||
synchronized (queuedAdditions) {
|
||||
queued = new ArrayList<>(queuedAdditions);
|
||||
queued = List.copyOf(queuedAdditions);
|
||||
queuedAdditions.clear();
|
||||
}
|
||||
|
||||
|
@ -164,14 +136,18 @@ public abstract class InstanceManager<T> {
|
|||
}
|
||||
|
||||
protected void processQueuedUpdates() {
|
||||
ArrayList<T> queued;
|
||||
if (queuedUpdates.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<T> queued;
|
||||
|
||||
synchronized (queuedUpdates) {
|
||||
queued = new ArrayList<>(queuedUpdates);
|
||||
queued = List.copyOf(queuedUpdates);
|
||||
queuedUpdates.clear();
|
||||
}
|
||||
|
||||
if (queued.size() > 0) {
|
||||
if (!queued.isEmpty()) {
|
||||
queued.forEach(getStorage()::update);
|
||||
}
|
||||
}
|
||||
|
@ -187,40 +163,34 @@ public abstract class InstanceManager<T> {
|
|||
*/
|
||||
public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) {
|
||||
tickLimiter.tick();
|
||||
processQueuedAdditions();
|
||||
processQueuedUpdates();
|
||||
|
||||
var instances = getStorage().getInstancesForTicking();
|
||||
var instances = getStorage().getTickableInstances();
|
||||
distributeWork(executor, instances, instance -> tickInstance(instance, cameraX, cameraY, cameraZ));
|
||||
}
|
||||
|
||||
protected void tickInstance(TickableInstance instance, double cX, double cY, double cZ) {
|
||||
if (!instance.decreaseTickRateWithDistance()) {
|
||||
protected void tickInstance(TickableInstance instance, double cameraX, double cameraY, double cameraZ) {
|
||||
if (!instance.decreaseTickRateWithDistance() || tickLimiter.shouldUpdate(instance.distanceSquared(cameraX, cameraY, cameraZ))) {
|
||||
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();
|
||||
processQueuedAdditions();
|
||||
processQueuedUpdates();
|
||||
|
||||
var cameraPos = context.camera()
|
||||
.getPosition();
|
||||
double cX = cameraPos.x;
|
||||
double cY = cameraPos.y;
|
||||
double cZ = cameraPos.z;
|
||||
FrustumIntersection culler = context.culler();
|
||||
var instances = getStorage().getDynamicInstances();
|
||||
distributeWork(executor, instances, instance -> updateInstance(instance, cameraX, cameraY, cameraZ, frustum));
|
||||
}
|
||||
|
||||
var instances = getStorage().getInstancesForUpdate();
|
||||
distributeWork(executor, instances, instance -> updateInstance(instance, culler, cX, cY, cZ));
|
||||
protected void updateInstance(DynamicInstance instance, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
||||
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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.ratelimit;
|
||||
package com.jozufozu.flywheel.impl.instancing.ratelimit;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
|
|
@ -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.
|
|
@ -1,4 +1,4 @@
|
|||
package com.jozufozu.flywheel.backend.instancing.ratelimit;
|
||||
package com.jozufozu.flywheel.impl.instancing.ratelimit;
|
||||
|
||||
public class NonLimiter implements DistanceUpdateLimiter {
|
||||
@Override
|
|
@ -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.List;
|
||||
|
@ -18,12 +18,12 @@ public abstract class AbstractStorage<T> implements Storage<T> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<TickableInstance> getInstancesForTicking() {
|
||||
public List<TickableInstance> getTickableInstances() {
|
||||
return tickableInstances;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DynamicInstance> getInstancesForUpdate() {
|
||||
public List<DynamicInstance> getDynamicInstances() {
|
||||
return dynamicInstances;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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.Map;
|
||||
|
||||
|
@ -9,23 +10,17 @@ import com.jozufozu.flywheel.api.backend.Engine;
|
|||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
|
||||
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) {
|
||||
super(engine);
|
||||
this.instances = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Instance> getAllInstances() {
|
||||
public Collection<Instance> getAllInstances() {
|
||||
return instances.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInstanceCount() {
|
||||
return instances.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(T obj) {
|
||||
Instance instance = instances.get(obj);
|
||||
|
@ -43,9 +38,9 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
|||
return;
|
||||
}
|
||||
|
||||
instance.delete();
|
||||
dynamicInstances.remove(instance);
|
||||
tickableInstances.remove(instance);
|
||||
dynamicInstances.remove(instance);
|
||||
instance.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -69,8 +64,8 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
|||
|
||||
@Override
|
||||
public void recreateAll() {
|
||||
dynamicInstances.clear();
|
||||
tickableInstances.clear();
|
||||
dynamicInstances.clear();
|
||||
instances.replaceAll((obj, instance) -> {
|
||||
instance.delete();
|
||||
|
||||
|
@ -86,10 +81,10 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
|||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
instances.values().forEach(Instance::delete);
|
||||
instances.clear();
|
||||
tickableInstances.clear();
|
||||
dynamicInstances.clear();
|
||||
instances.values().forEach(Instance::delete);
|
||||
instances.clear();
|
||||
}
|
||||
|
||||
private void create(T obj) {
|
|
@ -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 com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
|
@ -7,13 +8,11 @@ import com.jozufozu.flywheel.api.instance.Instance;
|
|||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
|
||||
public interface Storage<T> {
|
||||
Iterable<Instance> getAllInstances();
|
||||
Collection<Instance> getAllInstances();
|
||||
|
||||
int getInstanceCount();
|
||||
List<TickableInstance> getTickableInstances();
|
||||
|
||||
List<TickableInstance> getInstancesForTicking();
|
||||
|
||||
List<DynamicInstance> getInstancesForUpdate();
|
||||
List<DynamicInstance> getDynamicInstances();
|
||||
|
||||
void add(T obj);
|
||||
|
|
@ -16,7 +16,7 @@ import net.minecraft.network.chat.TextComponent;
|
|||
public class Backends {
|
||||
public static final Backend OFF = SimpleBackend.builder()
|
||||
.engineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED))
|
||||
.engineSupplier(() -> {
|
||||
.engineFactory(level -> {
|
||||
throw new IllegalStateException("Cannot create engine when backend is off.");
|
||||
})
|
||||
.fallback(() -> Backends.OFF)
|
||||
|
@ -28,7 +28,7 @@ public class Backends {
|
|||
*/
|
||||
public static final Backend BATCHING = SimpleBackend.builder()
|
||||
.engineMessage(new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN))
|
||||
.engineSupplier(BatchingEngine::new)
|
||||
.engineFactory(level -> new BatchingEngine())
|
||||
.fallback(() -> Backends.OFF)
|
||||
.supported(() -> !ShadersModHandler.isShaderPackInUse())
|
||||
.register(Flywheel.rl("batching"));
|
||||
|
@ -38,7 +38,7 @@ public class Backends {
|
|||
*/
|
||||
public static final Backend INSTANCING = SimpleBackend.builder()
|
||||
.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)
|
||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||
.instancedArraysSupported())
|
||||
|
@ -50,7 +50,7 @@ public class Backends {
|
|||
*/
|
||||
public static final Backend INDIRECT = SimpleBackend.builder()
|
||||
.engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
|
||||
.engineSupplier(() -> new IndirectEngine(Contexts.WORLD, 100 * 100))
|
||||
.engineFactory(level -> new IndirectEngine(100))
|
||||
.fallback(() -> Backends.INSTANCING)
|
||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||
.supportsIndirect())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.jozufozu.flywheel.lib.backend;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
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.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
|
||||
public class SimpleBackend implements Backend {
|
||||
private final Component engineMessage;
|
||||
private final Supplier<Engine> engineSupplier;
|
||||
private final Function<LevelAccessor, Engine> engineFactory;
|
||||
private final Supplier<Backend> fallback;
|
||||
private final BooleanSupplier isSupported;
|
||||
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.engineSupplier = engineSupplier;
|
||||
this.engineFactory = engineFactory;
|
||||
this.fallback = fallback;
|
||||
this.isSupported = isSupported;
|
||||
this.pipelineShader = pipelineShader;
|
||||
|
@ -37,8 +39,8 @@ public class SimpleBackend implements Backend {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Engine createEngine() {
|
||||
return engineSupplier.get();
|
||||
public Engine createEngine(LevelAccessor level) {
|
||||
return engineFactory.apply(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -63,7 +65,7 @@ public class SimpleBackend implements Backend {
|
|||
|
||||
public static class Builder {
|
||||
private Component engineMessage;
|
||||
private Supplier<Engine> engineSupplier;
|
||||
private Function<LevelAccessor, Engine> engineFactory;
|
||||
private Supplier<Backend> fallback;
|
||||
private BooleanSupplier isSupported;
|
||||
private Pipeline pipelineShader;
|
||||
|
@ -73,8 +75,8 @@ public class SimpleBackend implements Backend {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder engineSupplier(Supplier<Engine> engineSupplier) {
|
||||
this.engineSupplier = engineSupplier;
|
||||
public Builder engineFactory(Function<LevelAccessor, Engine> engineFactory) {
|
||||
this.engineFactory = engineFactory;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -94,7 +96,7 @@ public class SimpleBackend implements Backend {
|
|||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
package com.jozufozu.flywheel.lib.instance;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.joml.FrustumIntersection;
|
||||
|
||||
import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
|
||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||
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;
|
||||
|
||||
|
@ -30,13 +26,12 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
* </ul>
|
||||
* 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.
|
||||
*
|
||||
* @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> {
|
||||
|
||||
protected final T blockEntity;
|
||||
protected final BlockPos pos;
|
||||
protected final BlockPos instancePos;
|
||||
|
@ -50,17 +45,6 @@ public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends
|
|||
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.
|
||||
* 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.
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldReset() {
|
||||
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,
|
||||
* {@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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableBox getVolume() {
|
||||
return MutableBox.from(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkFrustum(FrustumIntersection frustum) {
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,19 +6,19 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
|||
import com.jozufozu.flywheel.api.instance.EntityInstance;
|
||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||
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.light.TickingLightListener;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import net.minecraft.util.Mth;
|
||||
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.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:
|
||||
* <ul>
|
||||
|
@ -27,13 +27,12 @@ import net.minecraft.world.phys.Vec3;
|
|||
* </ul>
|
||||
* 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.
|
||||
*
|
||||
* @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 {
|
||||
|
||||
protected final E entity;
|
||||
protected final MutableBox bounds;
|
||||
|
||||
|
@ -44,7 +43,12 @@ public abstract class AbstractEntityInstance<E extends Entity> extends AbstractI
|
|||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkFrustum(FrustumIntersection frustum) {
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double distanceSquared(double x, double y, double z) {
|
||||
return entity.distanceToSqr(x, y, z);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package com.jozufozu.flywheel.lib.instance;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
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.lib.light.LightListener;
|
||||
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
||||
import com.jozufozu.flywheel.lib.struct.FlatLit;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
|
@ -17,23 +16,22 @@ import net.minecraft.world.level.Level;
|
|||
import net.minecraft.world.level.LightLayer;
|
||||
|
||||
public abstract class AbstractInstance implements Instance, LightListener {
|
||||
public final Level level;
|
||||
public final Vec3i renderOrigin;
|
||||
protected final InstancerProvider instancerProvider;
|
||||
protected final Vec3i renderOrigin;
|
||||
protected final Level level;
|
||||
|
||||
protected final InstancerProvider instancerManager;
|
||||
protected boolean deleted = false;
|
||||
|
||||
public AbstractInstance(InstanceContext ctx, Level level) {
|
||||
this.instancerManager = ctx.instancerProvider();
|
||||
this.instancerProvider = ctx.instancerProvider();
|
||||
this.renderOrigin = ctx.renderOrigin();
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
LightUpdater.get(level).addListener(this);
|
||||
updateLight();
|
||||
LightUpdater.get(level)
|
||||
.addListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,31 +63,31 @@ public abstract class AbstractInstance implements Instance, LightListener {
|
|||
deleted = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvalid() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLightUpdate(LightLayer type, SectionPos pos) {
|
||||
updateLight();
|
||||
}
|
||||
|
||||
protected void relight(BlockPos pos, FlatLit<?>... models) {
|
||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), models);
|
||||
@Override
|
||||
public boolean isInvalid() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> models) {
|
||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), models);
|
||||
protected void relight(BlockPos pos, FlatLit<?>... parts) {
|
||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
|
||||
}
|
||||
|
||||
protected void relight(int block, int sky, FlatLit<?>... models) {
|
||||
relight(block, sky, Arrays.stream(models));
|
||||
protected void relight(int block, int sky, FlatLit<?>... parts) {
|
||||
for (FlatLit<?> part : parts) {
|
||||
part.setLight(block, sky);
|
||||
}
|
||||
}
|
||||
|
||||
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> models) {
|
||||
models.forEach(model -> model.setBlockLight(block)
|
||||
.setSkyLight(sky));
|
||||
protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> parts) {
|
||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
|
||||
}
|
||||
|
||||
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> parts) {
|
||||
parts.forEach(model -> model.setLight(block, sky));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ import com.jozufozu.flywheel.Flywheel;
|
|||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
||||
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.lib.format.Formats;
|
||||
import com.jozufozu.flywheel.lib.material.Materials;
|
||||
|
@ -62,7 +62,7 @@ public class ModelUtil {
|
|||
long srcPtr = MemoryUtil.memAddress(src);
|
||||
long dstPtr = dst.ptr();
|
||||
|
||||
ReusableVertexList srcList = VertexListProvider.get(srcFormat).createVertexList();
|
||||
ReusableVertexList srcList = VertexListProviderRegistry.getProvider(srcFormat).createVertexList();
|
||||
ReusableVertexList dstList = dstVertexType.createVertexList();
|
||||
srcList.ptr(srcPtr);
|
||||
dstList.ptr(dstPtr);
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
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.struct.StructType;
|
||||
|
||||
|
|
|
@ -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.world.level.BlockAndTintGetter;
|
||||
|
@ -27,9 +29,13 @@ public interface FlatLit<D extends InstancedPart & FlatLit<D>> {
|
|||
*/
|
||||
D setSkyLight(int skyLight);
|
||||
|
||||
default D setLight(int blockLight, int skyLight) {
|
||||
return setBlockLight(blockLight)
|
||||
.setSkyLight(skyLight);
|
||||
}
|
||||
|
||||
default D updateLight(BlockAndTintGetter level, BlockPos pos) {
|
||||
return setBlockLight(level.getBrightness(LightLayer.BLOCK, pos))
|
||||
.setSkyLight(level.getBrightness(LightLayer.SKY, pos));
|
||||
return setLight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos));
|
||||
}
|
||||
|
||||
int getPackedLight();
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.Flywheel;
|
|||
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
|
||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||
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.MatrixUtil;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
@ -73,13 +73,13 @@ public class FlwShaderUniforms implements ShaderUniforms {
|
|||
}
|
||||
RenderContext context = event.getContext();
|
||||
|
||||
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level());
|
||||
Vec3i renderOrigin = InstancedRenderDispatcher.getRenderOrigin(context.level());
|
||||
Vec3 camera = context.camera()
|
||||
.getPosition();
|
||||
|
||||
var camX = (float) (camera.x - originCoordinate.getX());
|
||||
var camY = (float) (camera.y - originCoordinate.getY());
|
||||
var camZ = (float) (camera.z - originCoordinate.getZ());
|
||||
var camX = (float) (camera.x - renderOrigin.getX());
|
||||
var camY = (float) (camera.y - renderOrigin.getY());
|
||||
var camZ = (float) (camera.z - renderOrigin.getZ());
|
||||
|
||||
// don't want to mutate viewProjection
|
||||
var vp = context.viewProjection()
|
||||
|
|
|
@ -9,9 +9,9 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
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.lib.instance.InstancingControllerHelper;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
@ -29,7 +29,7 @@ public abstract class ClientLevelMixin implements ClientLevelExtension {
|
|||
|
||||
@Inject(method = "entitiesForRendering", at = @At("RETURN"), cancellable = true)
|
||||
private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
|
||||
if (BackendManager.isOn()) {
|
||||
if (BackendUtil.canUseInstancing((ClientLevel) (Object) this)) {
|
||||
Iterable<Entity> entities = cir.getReturnValue();
|
||||
ArrayList<Entity> filtered = Lists.newArrayList(entities);
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||
|
||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
@ -19,7 +19,7 @@ public class ChunkRebuildHooksMixin {
|
|||
@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) {
|
||||
if (BackendUtil.canUseInstancing(be.getLevel())) {
|
||||
if (InstancingControllerHelper.canInstance(be.getType()))
|
||||
if (InstancingControllerHelper.canInstance(be))
|
||||
InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be);
|
||||
|
||||
if (InstancingControllerHelper.shouldSkipRender(be))
|
||||
|
|
|
@ -7,8 +7,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
@ -22,10 +22,10 @@ public class InstanceAddMixin {
|
|||
|
||||
@Inject(method = "setBlockEntity",
|
||||
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) {
|
||||
if (level.isClientSide && BackendManager.isOn()) {
|
||||
InstancedRenderDispatcher.getBlockEntities(this.level)
|
||||
.add(be);
|
||||
private void flywheel$onBlockEntityAdded(BlockEntity blockEntity, CallbackInfo ci) {
|
||||
if (level.isClientSide && BackendUtil.canUseInstancing(level)) {
|
||||
InstancedRenderDispatcher.getBlockEntities(level)
|
||||
.add(blockEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
@ -22,8 +22,8 @@ public class InstanceRemoveMixin {
|
|||
|
||||
@Inject(at = @At("TAIL"), method = "setRemoved")
|
||||
private void flywheel$removeInstance(CallbackInfo ci) {
|
||||
if (level instanceof ClientLevel && BackendManager.isOn()) {
|
||||
InstancedRenderDispatcher.getBlockEntities(this.level)
|
||||
if (level instanceof ClientLevel && BackendUtil.canUseInstancing(level)) {
|
||||
InstancedRenderDispatcher.getBlockEntities(level)
|
||||
.remove((BlockEntity) (Object) this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
|||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
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")
|
||||
private void flywheel$checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
|
||||
if (!BackendManager.isOn()) {
|
||||
if (!BackendUtil.canUseInstancing(level)) {
|
||||
return;
|
||||
}
|
||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||
|
|
|
@ -78,14 +78,18 @@ public class WorldAttached<T> {
|
|||
*/
|
||||
@NotNull
|
||||
public T replace(LevelAccessor world, Consumer<T> finalizer) {
|
||||
T remove = attached.remove(world);
|
||||
|
||||
if (remove != null)
|
||||
finalizer.accept(remove);
|
||||
remove(world, finalizer);
|
||||
|
||||
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.
|
||||
*
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -24,18 +23,22 @@ import net.minecraft.util.Mth;
|
|||
import net.minecraft.world.level.block.entity.BellBlockEntity;
|
||||
|
||||
public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> implements DynamicInstance {
|
||||
|
||||
private static final SimpleLazyModel MODEL = new SimpleLazyModel(BellInstance::createBellModel, Materials.BELL);
|
||||
|
||||
private final OrientedPart bell;
|
||||
private OrientedPart bell;
|
||||
|
||||
private float lastRingTime = Float.NaN;
|
||||
|
||||
public BellInstance(InstanceContext ctx, BellBlockEntity blockEntity) {
|
||||
super(ctx, blockEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
bell = createBellInstance().setPivot(0.5f, 0.75f, 0.5f)
|
||||
.setPosition(getInstancePosition());
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -65,8 +68,8 @@ public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> i
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addCrumblingParts(List<InstancedPart> data) {
|
||||
Collections.addAll(data, bell);
|
||||
public List<InstancedPart> getCrumblingParts() {
|
||||
return List.of(bell);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,7 +78,7 @@ public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> i
|
|||
}
|
||||
|
||||
private OrientedPart createBellInstance() {
|
||||
return instancerManager.getInstancer(StructTypes.ORIENTED, MODEL, RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
return instancerProvider.instancer(StructTypes.ORIENTED, MODEL, RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||
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;
|
||||
|
||||
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> BASE = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createBaseModel(type, mat), Materials.CHEST));
|
||||
|
||||
private final OrientedPart body;
|
||||
private final TransformedPart lid;
|
||||
private OrientedPart body;
|
||||
private TransformedPart lid;
|
||||
|
||||
private final Float2FloatFunction lidProgress;
|
||||
private final TextureAtlasSprite sprite;
|
||||
@NotNull
|
||||
private final ChestType chestType;
|
||||
private final Quaternion baseRotation;
|
||||
private Float2FloatFunction lidProgress;
|
||||
private TextureAtlasSprite sprite;
|
||||
private ChestType chestType;
|
||||
private Quaternion baseRotation;
|
||||
|
||||
private float lastProgress = Float.NaN;
|
||||
|
||||
public ChestInstance(InstanceContext ctx, T blockEntity) {
|
||||
super(ctx, blockEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
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();
|
||||
|
||||
if (block instanceof AbstractChestBlock<?> chestBlock) {
|
||||
|
||||
float horizontalAngle = blockState.getValue(ChestBlock.FACING).toYRot();
|
||||
|
||||
baseRotation = Vector3f.YP.rotationDegrees(-horizontalAngle);
|
||||
|
@ -78,6 +75,8 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
|
|||
baseRotation = Quaternion.ONE;
|
||||
lidProgress = $ -> 0f;
|
||||
}
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,8 +111,8 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addCrumblingParts(List<InstancedPart> data) {
|
||||
Collections.addAll(data, body, lid);
|
||||
public List<InstancedPart> getCrumblingParts() {
|
||||
return List.of(body, lid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -123,12 +122,12 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -26,22 +26,26 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntityInstance<T> implements DynamicInstance, TickableInstance {
|
||||
|
||||
private static final SimpleLazyModel MODEL = new SimpleLazyModel(MinecartInstance::getBodyModel, Materials.MINECART);
|
||||
|
||||
private final PoseStack stack = new PoseStack();
|
||||
|
||||
private final TransformedPart body;
|
||||
private TransformedPart body;
|
||||
private TransformedPart contents;
|
||||
private BlockState blockState;
|
||||
private boolean active;
|
||||
|
||||
public MinecartInstance(InstanceContext ctx, T entity) {
|
||||
super(ctx, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
body = getBody();
|
||||
blockState = entity.getDisplayBlockState();
|
||||
contents = getContents();
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -167,12 +171,12 @@ public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntity
|
|||
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();
|
||||
}
|
||||
|
||||
private TransformedPart getBody() {
|
||||
return instancerManager.getInstancer(StructTypes.TRANSFORMED, MODEL, RenderStage.AFTER_ENTITIES)
|
||||
return instancerProvider.instancer(StructTypes.TRANSFORMED, MODEL, RenderStage.AFTER_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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> LID = Util.memoize(it -> new SimpleLazyModel(() -> makeLidModel(it), Materials.SHULKER));
|
||||
|
||||
private final TextureAtlasSprite texture;
|
||||
private TextureAtlasSprite texture;
|
||||
|
||||
private final TransformedPart base;
|
||||
private final TransformedPart lid;
|
||||
private TransformedPart base;
|
||||
private TransformedPart lid;
|
||||
private final PoseStack stack = new PoseStack();
|
||||
|
||||
private float lastProgress = Float.NaN;
|
||||
|
||||
public ShulkerBoxInstance(InstanceContext ctx, ShulkerBoxBlockEntity blockEntity) {
|
||||
super(ctx, blockEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
DyeColor color = blockEntity.getColor();
|
||||
if (color == null) {
|
||||
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite();
|
||||
|
@ -67,6 +69,8 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
|
|||
tstack.translateY(0.25);
|
||||
|
||||
lid = makeLidInstance().setTransform(stack);
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -91,8 +95,8 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addCrumblingParts(List<InstancedPart> data) {
|
||||
Collections.addAll(data, base, lid);
|
||||
public List<InstancedPart> getCrumblingParts() {
|
||||
return List.of(base, lid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -107,12 +111,12 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,11 @@ import org.joml.Vector3f;
|
|||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
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.controller.InstanceContext;
|
||||
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.MutableBox;
|
||||
import com.jozufozu.flywheel.lib.instance.AbstractInstance;
|
||||
|
@ -110,7 +110,7 @@ public class ExampleEffect implements Effect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<Instance> createInstances(InstanceContext ctx) {
|
||||
public Collection<EffectInstance<?>> createInstances(InstanceContext ctx) {
|
||||
effects.clear();
|
||||
boids.clear();
|
||||
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;
|
||||
TransformedPart instance;
|
||||
private TransformedPart instance;
|
||||
|
||||
public BoidInstance(InstanceContext ctx, Level level, Boid self) {
|
||||
super(ctx, level);
|
||||
|
@ -250,11 +250,13 @@ public class ExampleEffect implements Effect {
|
|||
|
||||
@Override
|
||||
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();
|
||||
|
||||
instance.setBlockLight(15)
|
||||
.setSkyLight(15);
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -297,7 +299,7 @@ public class ExampleEffect implements Effect {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean checkFrustum(FrustumIntersection frustum) {
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue