mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-13 05:35:01 +01:00
Instance Refactor II
This commit is contained in:
parent
e6248f502e
commit
6002bfafd9
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.apache.maven.artifact.versioning.ArtifactVersion;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
|
||||||
import com.jozufozu.flywheel.backend.Loader;
|
import com.jozufozu.flywheel.backend.Loader;
|
||||||
import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer;
|
import com.jozufozu.flywheel.backend.engine.batching.DrawBuffer;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
|
||||||
import com.jozufozu.flywheel.config.BackendArgument;
|
import com.jozufozu.flywheel.config.BackendArgument;
|
||||||
import com.jozufozu.flywheel.config.FlwCommands;
|
import com.jozufozu.flywheel.config.FlwCommands;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.handler.EntityWorldHandler;
|
import com.jozufozu.flywheel.handler.EntityWorldHandler;
|
||||||
import com.jozufozu.flywheel.handler.ForgeEvents;
|
import com.jozufozu.flywheel.handler.ForgeEvents;
|
||||||
|
import com.jozufozu.flywheel.impl.BackendManagerImpl;
|
||||||
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
||||||
import com.jozufozu.flywheel.impl.RegistryImpl;
|
import com.jozufozu.flywheel.impl.RegistryImpl;
|
||||||
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.lib.backend.Backends;
|
import com.jozufozu.flywheel.lib.backend.Backends;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
import com.jozufozu.flywheel.lib.format.Formats;
|
import com.jozufozu.flywheel.lib.format.Formats;
|
||||||
|
@ -115,7 +115,7 @@ public class Flywheel {
|
||||||
|
|
||||||
VanillaInstances.init();
|
VanillaInstances.init();
|
||||||
|
|
||||||
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManager::getBackendNameForCrashReport);
|
CrashReportCallables.registerCrashCallable("Flywheel Backend", BackendManagerImpl::getBackendNameForCrashReport);
|
||||||
|
|
||||||
// https://github.com/Jozufozu/Flywheel/issues/69
|
// https://github.com/Jozufozu/Flywheel/issues/69
|
||||||
// Weird issue with accessor loading.
|
// Weird issue with accessor loading.
|
||||||
|
@ -129,7 +129,7 @@ public class Flywheel {
|
||||||
RegistryImpl.freezeAll();
|
RegistryImpl.freezeAll();
|
||||||
IdRegistryImpl.freezeAll();
|
IdRegistryImpl.freezeAll();
|
||||||
|
|
||||||
ArgumentTypes.register(rl("backend").toString(), BackendArgument.class, new EmptyArgumentSerializer<>(BackendArgument::getInstance));
|
ArgumentTypes.register(rl("backend").toString(), BackendArgument.class, new EmptyArgumentSerializer<>(() -> BackendArgument.INSTANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ArtifactVersion getVersion() {
|
public static ArtifactVersion getVersion() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.jozufozu.flywheel.api.registry.IdRegistry;
|
||||||
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
import com.jozufozu.flywheel.impl.IdRegistryImpl;
|
||||||
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
|
||||||
public interface Backend {
|
public interface Backend {
|
||||||
static IdRegistry<Backend> REGISTRY = IdRegistryImpl.create();
|
static IdRegistry<Backend> REGISTRY = IdRegistryImpl.create();
|
||||||
|
@ -19,7 +20,7 @@ public interface Backend {
|
||||||
/**
|
/**
|
||||||
* Create a new engine instance.
|
* Create a new engine instance.
|
||||||
*/
|
*/
|
||||||
Engine createEngine();
|
Engine createEngine(LevelAccessor level);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a fallback backend in case this backend is not supported.
|
* Get a fallback backend in case this backend is not supported.
|
||||||
|
|
|
@ -13,13 +13,6 @@ public final class BackendManager {
|
||||||
return BackendManagerImpl.getBackend();
|
return BackendManagerImpl.getBackend();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a string describing the current backend.
|
|
||||||
*/
|
|
||||||
public static String getBackendNameForCrashReport() {
|
|
||||||
return BackendManagerImpl.getBackendNameForCrashReport();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isOn() {
|
public static boolean isOn() {
|
||||||
return BackendManagerImpl.isOn();
|
return BackendManagerImpl.isOn();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,30 @@ package com.jozufozu.flywheel.api.backend;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancerProvider;
|
import com.jozufozu.flywheel.api.instancer.InstancerProvider;
|
||||||
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
|
|
||||||
public interface Engine extends RenderDispatcher, InstancerProvider {
|
import net.minecraft.client.Camera;
|
||||||
void attachManagers(InstanceManager<?>... listener);
|
import net.minecraft.core.Vec3i;
|
||||||
|
|
||||||
|
public interface Engine extends InstancerProvider {
|
||||||
|
void beginFrame(TaskExecutor executor, RenderContext context);
|
||||||
|
|
||||||
|
void renderStage(TaskExecutor executor, RenderContext context, RenderStage stage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintain the render origin to be within a certain distance from the camera in all directions,
|
||||||
|
* preventing floating point precision issues at high coordinates.
|
||||||
|
*
|
||||||
|
* @return {@code true} if the render origin changed, {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
boolean updateRenderOrigin(Camera camera);
|
||||||
|
|
||||||
|
Vec3i renderOrigin();
|
||||||
|
|
||||||
void addDebugInfo(List<String> info);
|
void addDebugInfo(List<String> info);
|
||||||
|
|
||||||
|
void delete();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||||
|
|
||||||
|
@ -34,4 +36,13 @@ public interface DynamicInstance extends Instance {
|
||||||
default boolean decreaseFramerateWithDistance() {
|
default boolean decreaseFramerateWithDistance() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check this instance against a frustum.<p>
|
||||||
|
* An implementor may choose to return a constant to skip the frustum check.
|
||||||
|
*
|
||||||
|
* @param frustum A frustum intersection tester for the current frame.
|
||||||
|
* @return {@code true} if this instance should be considered for updates.
|
||||||
|
*/
|
||||||
|
boolean isVisible(FrustumIntersection frustum);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
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;
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
|
* A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
|
||||||
*/
|
*/
|
||||||
|
@ -33,15 +31,6 @@ public interface Instance {
|
||||||
*/
|
*/
|
||||||
boolean shouldReset();
|
boolean shouldReset();
|
||||||
|
|
||||||
/**
|
|
||||||
* Check this instance against a frustum.<p>
|
|
||||||
* An implementor may choose to return a constant to skip the frustum check.
|
|
||||||
*
|
|
||||||
* @param frustum A frustum intersection tester for the current frame.
|
|
||||||
* @return {@code true} if this instance should be considered for updates.
|
|
||||||
*/
|
|
||||||
boolean checkFrustum(FrustumIntersection frustum);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculate the distance squared between this instance and the given <em>world</em> position.
|
* Calculate the distance squared between this instance and the given <em>world</em> position.
|
||||||
*
|
*
|
||||||
|
@ -56,5 +45,4 @@ public interface Instance {
|
||||||
* Free any acquired resources.
|
* Free any acquired resources.
|
||||||
*/
|
*/
|
||||||
void delete();
|
void delete();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
*/
|
*/
|
||||||
public interface BlockEntityInstancingController<T extends BlockEntity> {
|
public interface BlockEntityInstancingController<T extends BlockEntity> {
|
||||||
/**
|
/**
|
||||||
* Given a block entity and an instancer manager, constructs an instance for the block entity.
|
* Given a block entity and context, constructs an instance for the block entity.
|
||||||
*
|
*
|
||||||
* @param ctx Context for creating an Instance.
|
* @param ctx Context for creating an Instance.
|
||||||
* @param blockEntity The block entity to construct an instance for.
|
* @param blockEntity The block entity to construct an instance for.
|
||||||
|
|
|
@ -10,7 +10,7 @@ import net.minecraft.world.entity.Entity;
|
||||||
*/
|
*/
|
||||||
public interface EntityInstancingController<T extends Entity> {
|
public interface EntityInstancingController<T extends Entity> {
|
||||||
/**
|
/**
|
||||||
* Given an entity and an instancer manager, constructs an instance for the entity.
|
* Given an entity and context, constructs an instance for the entity.
|
||||||
*
|
*
|
||||||
* @param ctx Context for creating an Instance.
|
* @param ctx Context for creating an Instance.
|
||||||
* @param entity The entity to construct an instance for.
|
* @param entity The entity to construct an instance for.
|
||||||
|
|
|
@ -12,5 +12,4 @@ import net.minecraft.core.Vec3i;
|
||||||
* All models render as if this position is (0, 0, 0).
|
* All models render as if this position is (0, 0, 0).
|
||||||
*/
|
*/
|
||||||
public record InstanceContext(InstancerProvider instancerProvider, Vec3i renderOrigin) {
|
public record InstanceContext(InstancerProvider instancerProvider, Vec3i renderOrigin) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.jozufozu.flywheel.api.instance.controller;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.impl.instance.InstancingControllerRegistryImpl;
|
import com.jozufozu.flywheel.impl.InstancingControllerRegistryImpl;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
|
|
@ -2,9 +2,11 @@ package com.jozufozu.flywheel.api.instance.effect;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.EffectInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
|
|
||||||
|
// TODO: add level getter?
|
||||||
|
// TODO: return single instance instead of many?
|
||||||
public interface Effect {
|
public interface Effect {
|
||||||
Collection<Instance> createInstances(InstanceContext ctx);
|
Collection<EffectInstance<?>> createInstances(InstanceContext ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,5 +10,5 @@ public interface InstancerProvider {
|
||||||
*
|
*
|
||||||
* @return An instancer for the given struct type, model, and render stage.
|
* @return An instancer for the given struct type, model, and render stage.
|
||||||
*/
|
*/
|
||||||
<D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage);
|
<D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,5 @@
|
||||||
package com.jozufozu.flywheel.api.vertex;
|
package com.jozufozu.flywheel.api.vertex;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.extension.VertexFormatExtension;
|
|
||||||
import com.jozufozu.flywheel.impl.vertex.InferredVertexListProviderImpl;
|
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
|
||||||
|
|
||||||
public interface VertexListProvider {
|
public interface VertexListProvider {
|
||||||
ReusableVertexList createVertexList();
|
ReusableVertexList createVertexList();
|
||||||
|
|
||||||
static VertexListProvider get(VertexFormat format) {
|
|
||||||
VertexFormatExtension extension = (VertexFormatExtension) format;
|
|
||||||
VertexListProvider provider = extension.flywheel$getVertexListProvider();
|
|
||||||
if (provider == null) {
|
|
||||||
provider = new InferredVertexListProviderImpl(format);
|
|
||||||
extension.flywheel$setVertexListProvider(provider);
|
|
||||||
}
|
|
||||||
return provider;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set(VertexFormat format, VertexListProvider provider) {
|
|
||||||
((VertexFormatExtension) format).flywheel$setVertexListProvider(provider);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 com.jozufozu.flywheel.backend.task.ParallelTaskExecutor;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
|
||||||
public class BackendUtil {
|
public class BackendUtil {
|
||||||
public static final boolean DUMP_SHADER_SOURCE = System.getProperty("flw.dumpShaderSource") != null;
|
|
||||||
|
|
||||||
private static ParallelTaskExecutor executor;
|
private static ParallelTaskExecutor executor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,7 +27,7 @@ public class BackendUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Contract("null -> false")
|
@Contract("null -> false")
|
||||||
public static boolean canUseInstancing(@Nullable Level level) {
|
public static boolean canUseInstancing(@Nullable LevelAccessor level) {
|
||||||
return BackendManager.isOn() && isFlywheelLevel(level);
|
return BackendManager.isOn() && isFlywheelLevel(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,11 +35,17 @@ public class BackendUtil {
|
||||||
* Used to avoid calling Flywheel functions on (fake) levels that don't specifically support it.
|
* Used to avoid calling Flywheel functions on (fake) levels that don't specifically support it.
|
||||||
*/
|
*/
|
||||||
public static boolean isFlywheelLevel(@Nullable LevelAccessor level) {
|
public static boolean isFlywheelLevel(@Nullable LevelAccessor level) {
|
||||||
if (level == null) return false;
|
if (level == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!level.isClientSide()) return false;
|
if (!level.isClientSide()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (level instanceof FlywheelLevel && ((FlywheelLevel) level).supportsFlywheel()) return true;
|
if (level instanceof FlywheelLevel flywheelLevel && flywheelLevel.supportsFlywheel()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return level == Minecraft.getInstance().level;
|
return level == Minecraft.getInstance().level;
|
||||||
}
|
}
|
||||||
|
@ -50,8 +53,4 @@ public class BackendUtil {
|
||||||
public static boolean isGameActive() {
|
public static boolean isGameActive() {
|
||||||
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
|
return !(Minecraft.getInstance().level == null || Minecraft.getInstance().player == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void reloadWorldRenderers() {
|
|
||||||
Minecraft.getInstance().levelRenderer.allChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ package com.jozufozu.flywheel.backend;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||||
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
|
||||||
import com.jozufozu.flywheel.glsl.ShaderSources;
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
import com.jozufozu.flywheel.glsl.error.ErrorReporter;
|
import com.jozufozu.flywheel.glsl.error.ErrorReporter;
|
||||||
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
@ -39,8 +39,8 @@ public class Loader implements ResourceManagerReloadListener {
|
||||||
FlwCompiler.INSTANCE = new FlwCompiler(sources);
|
FlwCompiler.INSTANCE = new FlwCompiler(sources);
|
||||||
|
|
||||||
ClientLevel level = Minecraft.getInstance().level;
|
ClientLevel level = Minecraft.getInstance().level;
|
||||||
if (BackendUtil.canUseInstancing(level)) {
|
if (level != null) {
|
||||||
InstancedRenderDispatcher.resetInstanceLevel(level);
|
InstancedRenderDispatcher.resetInstanceWorld(level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import org.jetbrains.annotations.NotNull;
|
||||||
import org.lwjgl.opengl.GL20;
|
import org.lwjgl.opengl.GL20;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
|
||||||
import com.jozufozu.flywheel.gl.GLSLVersion;
|
import com.jozufozu.flywheel.gl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.gl.shader.GlShader;
|
import com.jozufozu.flywheel.gl.shader.GlShader;
|
||||||
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
import com.jozufozu.flywheel.gl.shader.ShaderType;
|
||||||
|
@ -29,6 +28,8 @@ import net.minecraft.resources.ResourceLocation;
|
||||||
* and interprets/pretty prints any errors that occur.
|
* and interprets/pretty prints any errors that occur.
|
||||||
*/
|
*/
|
||||||
public class Compilation {
|
public class Compilation {
|
||||||
|
public static final boolean DUMP_SHADER_SOURCE = System.getProperty("flw.dumpShaderSource") != null;
|
||||||
|
|
||||||
private final List<SourceFile> files = new ArrayList<>();
|
private final List<SourceFile> files = new ArrayList<>();
|
||||||
private final List<ResourceLocation> componentNames = new ArrayList<>();
|
private final List<ResourceLocation> componentNames = new ArrayList<>();
|
||||||
private final StringBuilder generatedSource;
|
private final StringBuilder generatedSource;
|
||||||
|
@ -131,7 +132,7 @@ public class Compilation {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void dumpSource(String source, String fileName) {
|
private static void dumpSource(String source, String fileName) {
|
||||||
if (!BackendUtil.DUMP_SHADER_SOURCE) {
|
if (!DUMP_SHADER_SOURCE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.lwjgl.system.MemoryUtil;
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
import com.jozufozu.flywheel.api.vertex.VertexListProviderRegistry;
|
||||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public class BatchedMeshPool {
|
||||||
*/
|
*/
|
||||||
public BatchedMeshPool(VertexFormat vertexFormat) {
|
public BatchedMeshPool(VertexFormat vertexFormat) {
|
||||||
this.vertexFormat = vertexFormat;
|
this.vertexFormat = vertexFormat;
|
||||||
vertexList = VertexListProvider.get(vertexFormat).createVertexList();
|
vertexList = VertexListProviderRegistry.getProvider(vertexFormat).createVertexList();
|
||||||
growthMargin = vertexFormat.getVertexSize() * 32;
|
growthMargin = vertexFormat.getVertexSize() * 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
|
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
@ -21,11 +20,11 @@ import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class BatchingEngine implements Engine {
|
public class BatchingEngine implements Engine {
|
||||||
protected final BatchingTransformManager transformManager = new BatchingTransformManager();
|
private final BatchingTransformManager transformManager = new BatchingTransformManager();
|
||||||
protected final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
|
private final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage) {
|
public <D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage) {
|
||||||
return transformManager.getInstancer(type, model, stage);
|
return transformManager.getInstancer(type, model, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +41,7 @@ public class BatchingEngine implements Engine {
|
||||||
submitTasks(executor, stack.last(), context.level());
|
submitTasks(executor, stack.last(), context.level());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void submitTasks(TaskExecutor executor, PoseStack.Pose matrices, ClientLevel level) {
|
private void submitTasks(TaskExecutor executor, PoseStack.Pose matrices, ClientLevel level) {
|
||||||
for (var transformSetEntry : transformManager.getTransformSetsView().entrySet()) {
|
for (var transformSetEntry : transformManager.getTransformSetsView().entrySet()) {
|
||||||
var stage = transformSetEntry.getKey();
|
var stage = transformSetEntry.getKey();
|
||||||
var transformSet = transformSetEntry.getValue();
|
var transformSet = transformSetEntry.getValue();
|
||||||
|
@ -79,16 +78,10 @@ public class BatchingEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean maintainOriginCoordinate(Camera camera) {
|
public boolean updateRenderOrigin(Camera camera) {
|
||||||
// do nothing
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attachManagers(InstanceManager<?>... listener) {
|
|
||||||
// noop
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i renderOrigin() {
|
public Vec3i renderOrigin() {
|
||||||
return BlockPos.ZERO;
|
return BlockPos.ZERO;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexListProviderRegistry;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
@ -24,7 +25,7 @@ public class DrawBufferSet {
|
||||||
this.renderType = renderType;
|
this.renderType = renderType;
|
||||||
format = renderType.format();
|
format = renderType.format();
|
||||||
stride = format.getVertexSize();
|
stride = format.getVertexSize();
|
||||||
provider = VertexListProvider.get(format);
|
provider = VertexListProviderRegistry.getProvider(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DrawBuffer getBuffer(RenderStage stage) {
|
public DrawBuffer getBuffer(RenderStage stage) {
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.indirect;
|
package com.jozufozu.flywheel.backend.engine.indirect;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
|
@ -15,40 +12,29 @@ import com.jozufozu.flywheel.api.instancer.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
|
|
||||||
import com.jozufozu.flywheel.gl.GlStateTracker;
|
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.util.Mth;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class IndirectEngine implements Engine {
|
public class IndirectEngine implements Engine {
|
||||||
|
private final int sqrMaxOriginDistance;
|
||||||
|
|
||||||
protected final IndirectDrawManager drawManager = new IndirectDrawManager();
|
private final IndirectDrawManager drawManager = new IndirectDrawManager();
|
||||||
|
|
||||||
/**
|
private BlockPos renderOrigin = BlockPos.ZERO;
|
||||||
* The set of instance managers that are attached to this engine.
|
|
||||||
*/
|
|
||||||
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
|
||||||
|
|
||||||
protected final Context context;
|
public IndirectEngine(int maxOriginDistance) {
|
||||||
protected final int sqrMaxOriginDistance;
|
this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
|
||||||
|
|
||||||
protected BlockPos originCoordinate = BlockPos.ZERO;
|
|
||||||
|
|
||||||
public IndirectEngine(Context context, int sqrMaxOriginDistance) {
|
|
||||||
this.context = context;
|
|
||||||
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage) {
|
public <D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage) {
|
||||||
return drawManager.getInstancer(type, model, stage);
|
return drawManager.getInstancer(type, model, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +56,7 @@ public class IndirectEngine implements Engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setup() {
|
private void setup() {
|
||||||
GlTextureUnit.T2.makeActive();
|
GlTextureUnit.T2.makeActive();
|
||||||
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
||||||
|
|
||||||
|
@ -82,36 +68,25 @@ public class IndirectEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean maintainOriginCoordinate(Camera camera) {
|
public boolean updateRenderOrigin(Camera camera) {
|
||||||
Vec3 cameraPos = camera.getPosition();
|
Vec3 cameraPos = camera.getPosition();
|
||||||
|
double dx = renderOrigin.getX() - cameraPos.x;
|
||||||
|
double dy = renderOrigin.getY() - cameraPos.y;
|
||||||
|
double dz = renderOrigin.getZ() - cameraPos.z;
|
||||||
|
double distanceSqr = dx * dx + dy * dy + dz * dz;
|
||||||
|
|
||||||
double distanceSqr = Vec3.atLowerCornerOf(originCoordinate)
|
if (distanceSqr <= sqrMaxOriginDistance) {
|
||||||
.subtract(cameraPos)
|
return false;
|
||||||
.lengthSqr();
|
|
||||||
|
|
||||||
if (distanceSqr > sqrMaxOriginDistance) {
|
|
||||||
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shiftListeners(int cX, int cY, int cZ) {
|
|
||||||
originCoordinate = new BlockPos(cX, cY, cZ);
|
|
||||||
|
|
||||||
|
renderOrigin = new BlockPos(cameraPos);
|
||||||
drawManager.clearInstancers();
|
drawManager.clearInstancers();
|
||||||
|
return true;
|
||||||
instanceManagers.forEach(InstanceManager::onOriginShift);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attachManagers(InstanceManager<?>... listener) {
|
|
||||||
Collections.addAll(instanceManagers, listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i renderOrigin() {
|
public Vec3i renderOrigin() {
|
||||||
return originCoordinate;
|
return renderOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -122,6 +97,6 @@ public class IndirectEngine implements Engine {
|
||||||
@Override
|
@Override
|
||||||
public void addDebugInfo(List<String> info) {
|
public void addDebugInfo(List<String> info) {
|
||||||
info.add("GL46 Indirect");
|
info.add("GL46 Indirect");
|
||||||
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL32;
|
import org.lwjgl.opengl.GL32;
|
||||||
|
|
||||||
|
@ -17,42 +15,33 @@ import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
||||||
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
import com.jozufozu.flywheel.backend.engine.UniformBuffer;
|
||||||
import com.jozufozu.flywheel.backend.instancing.manager.InstanceManager;
|
|
||||||
import com.jozufozu.flywheel.gl.GlStateTracker;
|
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||||
import com.jozufozu.flywheel.lib.pipeline.Pipelines;
|
import com.jozufozu.flywheel.lib.pipeline.Pipelines;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.util.Mth;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class InstancingEngine implements Engine {
|
public class InstancingEngine implements Engine {
|
||||||
|
private final Context context;
|
||||||
|
private final int sqrMaxOriginDistance;
|
||||||
|
|
||||||
protected final InstancingDrawManager drawManager = new InstancingDrawManager();
|
private final InstancingDrawManager drawManager = new InstancingDrawManager();
|
||||||
|
|
||||||
/**
|
private BlockPos renderOrigin = BlockPos.ZERO;
|
||||||
* The set of instance managers that are attached to this engine.
|
|
||||||
*/
|
|
||||||
private final Set<InstanceManager<?>> instanceManagers = FlwUtil.createWeakHashSet();
|
|
||||||
|
|
||||||
protected final Context context;
|
public InstancingEngine(Context context, int maxOriginDistance) {
|
||||||
protected final int sqrMaxOriginDistance;
|
|
||||||
|
|
||||||
protected BlockPos originCoordinate = BlockPos.ZERO;
|
|
||||||
|
|
||||||
public InstancingEngine(Context context, int sqrMaxOriginDistance) {
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.sqrMaxOriginDistance = sqrMaxOriginDistance;
|
this.sqrMaxOriginDistance = maxOriginDistance * maxOriginDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <D extends InstancedPart> Instancer<D> getInstancer(StructType<D> type, Model model, RenderStage stage) {
|
public <D extends InstancedPart> Instancer<D> instancer(StructType<D> type, Model model, RenderStage stage) {
|
||||||
return drawManager.getInstancer(type, model, stage);
|
return drawManager.getInstancer(type, model, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +67,7 @@ public class InstancingEngine implements Engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setup() {
|
private void setup() {
|
||||||
GlTextureUnit.T2.makeActive();
|
GlTextureUnit.T2.makeActive();
|
||||||
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
Minecraft.getInstance().gameRenderer.lightTexture().turnOnLightLayer();
|
||||||
|
|
||||||
|
@ -89,7 +78,7 @@ public class InstancingEngine implements Engine {
|
||||||
RenderSystem.enableCull();
|
RenderSystem.enableCull();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void render(InstancingDrawManager.DrawSet drawSet) {
|
private void render(InstancingDrawManager.DrawSet drawSet) {
|
||||||
for (var entry : drawSet) {
|
for (var entry : drawSet) {
|
||||||
var shader = entry.getKey();
|
var shader = entry.getKey();
|
||||||
var drawCalls = entry.getValue();
|
var drawCalls = entry.getValue();
|
||||||
|
@ -112,7 +101,7 @@ public class InstancingEngine implements Engine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setup(ShaderState desc) {
|
private void setup(ShaderState desc) {
|
||||||
var vertexType = desc.vertex();
|
var vertexType = desc.vertex();
|
||||||
var structType = desc.instance();
|
var structType = desc.instance();
|
||||||
var material = desc.material();
|
var material = desc.material();
|
||||||
|
@ -127,36 +116,25 @@ public class InstancingEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean maintainOriginCoordinate(Camera camera) {
|
public boolean updateRenderOrigin(Camera camera) {
|
||||||
Vec3 cameraPos = camera.getPosition();
|
Vec3 cameraPos = camera.getPosition();
|
||||||
|
double dx = renderOrigin.getX() - cameraPos.x;
|
||||||
|
double dy = renderOrigin.getY() - cameraPos.y;
|
||||||
|
double dz = renderOrigin.getZ() - cameraPos.z;
|
||||||
|
double distanceSqr = dx * dx + dy * dy + dz * dz;
|
||||||
|
|
||||||
double distanceSqr = Vec3.atLowerCornerOf(originCoordinate)
|
if (distanceSqr <= sqrMaxOriginDistance) {
|
||||||
.subtract(cameraPos)
|
return false;
|
||||||
.lengthSqr();
|
|
||||||
|
|
||||||
if (distanceSqr > sqrMaxOriginDistance) {
|
|
||||||
shiftListeners(Mth.floor(cameraPos.x), Mth.floor(cameraPos.y), Mth.floor(cameraPos.z));
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shiftListeners(int cX, int cY, int cZ) {
|
|
||||||
originCoordinate = new BlockPos(cX, cY, cZ);
|
|
||||||
|
|
||||||
|
renderOrigin = new BlockPos(cameraPos);
|
||||||
drawManager.clearInstancers();
|
drawManager.clearInstancers();
|
||||||
|
return true;
|
||||||
instanceManagers.forEach(InstanceManager::onOriginShift);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void attachManagers(InstanceManager<?>... listener) {
|
|
||||||
Collections.addAll(instanceManagers, listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i renderOrigin() {
|
public Vec3i renderOrigin() {
|
||||||
return originCoordinate;
|
return renderOrigin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -167,6 +145,6 @@ public class InstancingEngine implements Engine {
|
||||||
@Override
|
@Override
|
||||||
public void addDebugInfo(List<String> info) {
|
public void addDebugInfo(List<String> info) {
|
||||||
info.add("GL33 Instanced Arrays");
|
info.add("GL33 Instanced Arrays");
|
||||||
info.add("Origin: " + originCoordinate.getX() + ", " + originCoordinate.getY() + ", " + originCoordinate.getZ());
|
info.add("Origin: " + renderOrigin.getX() + ", " + renderOrigin.getY() + ", " + renderOrigin.getZ());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.network.chat.TranslatableComponent;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public enum BackendArgument implements ArgumentType<Backend> {
|
public class BackendArgument implements ArgumentType<Backend> {
|
||||||
INSTANCE;
|
|
||||||
|
|
||||||
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds().stream().map(ResourceLocation::toString).toList();
|
private static final List<String> STRING_IDS = Backend.REGISTRY.getAllIds().stream().map(ResourceLocation::toString).toList();
|
||||||
|
|
||||||
private static final Dynamic2CommandExceptionType INVALID = new Dynamic2CommandExceptionType((found, constants) -> {
|
private static final Dynamic2CommandExceptionType INVALID = new Dynamic2CommandExceptionType((found, constants) -> {
|
||||||
|
@ -27,9 +25,7 @@ public enum BackendArgument implements ArgumentType<Backend> {
|
||||||
return new TranslatableComponent("commands.forge.arguments.enum.invalid", constants, found);
|
return new TranslatableComponent("commands.forge.arguments.enum.invalid", constants, found);
|
||||||
});
|
});
|
||||||
|
|
||||||
public static BackendArgument getInstance() {
|
public static final BackendArgument INSTANCE = new BackendArgument();
|
||||||
return INSTANCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Backend parse(StringReader reader) throws CommandSyntaxException {
|
public Backend parse(StringReader reader) throws CommandSyntaxException {
|
||||||
|
|
|
@ -3,10 +3,8 @@ package com.jozufozu.flywheel.config;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Backend;
|
import com.jozufozu.flywheel.api.backend.Backend;
|
||||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
|
||||||
import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
|
import com.jozufozu.flywheel.lib.uniform.FlwShaderUniforms;
|
||||||
import com.mojang.brigadier.Command;
|
import com.mojang.brigadier.Command;
|
||||||
import com.mojang.brigadier.CommandDispatcher;
|
|
||||||
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
|
||||||
|
@ -30,9 +28,9 @@ public class FlwCommands {
|
||||||
public static void registerClientCommands(RegisterClientCommandsEvent event) {
|
public static void registerClientCommands(RegisterClientCommandsEvent event) {
|
||||||
FlwConfig config = FlwConfig.get();
|
FlwConfig config = FlwConfig.get();
|
||||||
|
|
||||||
ConfigCommandBuilder commandBuilder = new ConfigCommandBuilder("flywheel");
|
LiteralArgumentBuilder<CommandSourceStack> command = Commands.literal("flywheel");
|
||||||
|
|
||||||
commandBuilder.addValue(config.client.backend, "backend", (builder, value) ->
|
addValue(command, config.client.backend, "backend", (builder, value) ->
|
||||||
builder
|
builder
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
LocalPlayer player = Minecraft.getInstance().player;
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
|
@ -68,12 +66,12 @@ public class FlwCommands {
|
||||||
Component message = backend.engineMessage();
|
Component message = backend.engineMessage();
|
||||||
player.displayClientMessage(message, false);
|
player.displayClientMessage(message, false);
|
||||||
|
|
||||||
BackendUtil.reloadWorldRenderers();
|
Minecraft.getInstance().levelRenderer.allChanged();
|
||||||
}
|
}
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
commandBuilder.addValue(config.client.limitUpdates, "limitUpdates", (builder, value) -> booleanValueCommand(builder, value,
|
addValue(command, config.client.limitUpdates, "limitUpdates", (builder, value) -> booleanValueCommand(builder, value,
|
||||||
(source, bool) -> {
|
(source, bool) -> {
|
||||||
LocalPlayer player = Minecraft.getInstance().player;
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
if (player == null) return;
|
if (player == null) return;
|
||||||
|
@ -88,12 +86,12 @@ public class FlwCommands {
|
||||||
Component text = boolToText(bool).append(new TextComponent(" update limiting.").withStyle(ChatFormatting.WHITE));
|
Component text = boolToText(bool).append(new TextComponent(" update limiting.").withStyle(ChatFormatting.WHITE));
|
||||||
player.displayClientMessage(text, false);
|
player.displayClientMessage(text, false);
|
||||||
|
|
||||||
BackendUtil.reloadWorldRenderers();
|
Minecraft.getInstance().levelRenderer.allChanged();
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
commandBuilder.command.then(Commands.literal("debugNormals"))
|
command.then(Commands.literal("debugNormals"))
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
LocalPlayer player = Minecraft.getInstance().player;
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
if (player == null) return 0;
|
if (player == null) return 0;
|
||||||
|
@ -103,7 +101,7 @@ public class FlwCommands {
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
});
|
});
|
||||||
|
|
||||||
commandBuilder.command.then(Commands.literal("debugCrumbling")
|
command.then(Commands.literal("debugCrumbling")
|
||||||
.then(Commands.argument("pos", BlockPosArgument.blockPos())
|
.then(Commands.argument("pos", BlockPosArgument.blockPos())
|
||||||
.then(Commands.argument("stage", IntegerArgumentType.integer(0, 9))
|
.then(Commands.argument("stage", IntegerArgumentType.integer(0, 9))
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
|
@ -122,7 +120,7 @@ public class FlwCommands {
|
||||||
return Command.SINGLE_SUCCESS;
|
return Command.SINGLE_SUCCESS;
|
||||||
}))));
|
}))));
|
||||||
|
|
||||||
commandBuilder.command.then(Commands.literal("debugFrustum")
|
command.then(Commands.literal("debugFrustum")
|
||||||
.then(Commands.literal("pause")
|
.then(Commands.literal("pause")
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
FlwShaderUniforms.FRUSTUM_PAUSED = true;
|
FlwShaderUniforms.FRUSTUM_PAUSED = true;
|
||||||
|
@ -139,10 +137,16 @@ public class FlwCommands {
|
||||||
return 1;
|
return 1;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
commandBuilder.build(event.getDispatcher());
|
event.getDispatcher().register(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void booleanValueCommand(LiteralArgumentBuilder<CommandSourceStack> builder, ConfigValue<Boolean> value, BiConsumer<CommandSourceStack, Boolean> displayAction, BiConsumer<CommandSourceStack, Boolean> setAction) {
|
private static <T extends ConfigValue<?>> void addValue(LiteralArgumentBuilder<CommandSourceStack> command, T value, String subcommand, BiConsumer<LiteralArgumentBuilder<CommandSourceStack>, T> consumer) {
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(subcommand);
|
||||||
|
consumer.accept(builder, value);
|
||||||
|
command.then(builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void booleanValueCommand(LiteralArgumentBuilder<CommandSourceStack> builder, ConfigValue<Boolean> value, BiConsumer<CommandSourceStack, Boolean> displayAction, BiConsumer<CommandSourceStack, Boolean> setAction) {
|
||||||
builder
|
builder
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
displayAction.accept(context.getSource(), value.get());
|
displayAction.accept(context.getSource(), value.get());
|
||||||
|
@ -165,22 +169,4 @@ public class FlwCommands {
|
||||||
public static MutableComponent boolToText(boolean b) {
|
public static MutableComponent boolToText(boolean b) {
|
||||||
return b ? new TextComponent("enabled").withStyle(ChatFormatting.DARK_GREEN) : new TextComponent("disabled").withStyle(ChatFormatting.RED);
|
return b ? new TextComponent("enabled").withStyle(ChatFormatting.DARK_GREEN) : new TextComponent("disabled").withStyle(ChatFormatting.RED);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ConfigCommandBuilder {
|
|
||||||
protected LiteralArgumentBuilder<CommandSourceStack> command;
|
|
||||||
|
|
||||||
public ConfigCommandBuilder(String baseLiteral) {
|
|
||||||
command = Commands.literal(baseLiteral);
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends ConfigValue<?>> void addValue(T value, String subcommand, BiConsumer<LiteralArgumentBuilder<CommandSourceStack>, T> consumer) {
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(subcommand);
|
|
||||||
consumer.accept(builder, value);
|
|
||||||
command.then(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void build(CommandDispatcher<CommandSourceStack> dispatcher) {
|
|
||||||
dispatcher.register(command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,34 @@
|
||||||
package com.jozufozu.flywheel.handler;
|
package com.jozufozu.flywheel.handler;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||||
import net.minecraftforge.event.entity.EntityLeaveWorldEvent;
|
import net.minecraftforge.event.entity.EntityLeaveWorldEvent;
|
||||||
|
|
||||||
public class EntityWorldHandler {
|
public class EntityWorldHandler {
|
||||||
public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
|
public static void onEntityJoinWorld(EntityJoinWorldEvent event) {
|
||||||
if (event.getWorld().isClientSide && BackendManager.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
|
Level level = event.getWorld();
|
||||||
.queueAdd(event.getEntity());
|
if (!level.isClientSide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BackendUtil.canUseInstancing(level)) {
|
||||||
|
InstancedRenderDispatcher.getEntities(level)
|
||||||
|
.queueAdd(event.getEntity());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
|
public static void onEntityLeaveWorld(EntityLeaveWorldEvent event) {
|
||||||
if (event.getWorld().isClientSide && BackendManager.isOn()) InstancedRenderDispatcher.getEntities(event.getWorld())
|
Level level = event.getWorld();
|
||||||
.remove(event.getEntity());
|
if (!level.isClientSide) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BackendUtil.canUseInstancing(level)) {
|
||||||
|
InstancedRenderDispatcher.getEntities(level)
|
||||||
|
.remove(event.getEntity());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import java.util.ArrayList;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
||||||
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
|
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
|
||||||
import com.jozufozu.flywheel.util.StringUtil;
|
import com.jozufozu.flywheel.util.StringUtil;
|
||||||
|
@ -16,14 +16,13 @@ import net.minecraftforge.event.TickEvent;
|
||||||
import net.minecraftforge.event.world.WorldEvent;
|
import net.minecraftforge.event.world.WorldEvent;
|
||||||
|
|
||||||
public class ForgeEvents {
|
public class ForgeEvents {
|
||||||
|
|
||||||
public static void addToDebugScreen(RenderGameOverlayEvent.Text event) {
|
public static void addToDebugScreen(RenderGameOverlayEvent.Text event) {
|
||||||
if (Minecraft.getInstance().options.renderDebug) {
|
if (Minecraft.getInstance().options.renderDebug) {
|
||||||
ArrayList<String> debug = event.getRight();
|
ArrayList<String> debug = event.getRight();
|
||||||
debug.add("");
|
debug.add("");
|
||||||
debug.add("Flywheel: " + Flywheel.getVersion());
|
debug.add("Flywheel: " + Flywheel.getVersion());
|
||||||
|
|
||||||
InstancedRenderDispatcher.getDebugString(debug);
|
InstancedRenderDispatcher.addDebugInfo(debug);
|
||||||
|
|
||||||
debug.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory()));
|
debug.add("Memory Usage: CPU: " + StringUtil.formatBytes(FlwMemoryTracker.getCPUMemory()) + ", GPU: " + StringUtil.formatBytes(FlwMemoryTracker.getGPUMemory()));
|
||||||
}
|
}
|
||||||
|
@ -39,5 +38,4 @@ public class ForgeEvents {
|
||||||
.tick();
|
.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,17 +18,6 @@ public final class BackendManagerImpl {
|
||||||
return backend;
|
return backend;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getBackendNameForCrashReport() {
|
|
||||||
if (backend == null) {
|
|
||||||
return "Uninitialized";
|
|
||||||
}
|
|
||||||
var backendId = Backend.REGISTRY.getId(backend);
|
|
||||||
if (backendId == null) {
|
|
||||||
return "Unregistered";
|
|
||||||
}
|
|
||||||
return backendId.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isOn() {
|
public static boolean isOn() {
|
||||||
return backend != null && backend != Backends.OFF;
|
return backend != null && backend != Backends.OFF;
|
||||||
}
|
}
|
||||||
|
@ -58,6 +47,17 @@ public final class BackendManagerImpl {
|
||||||
return Backends.INDIRECT;
|
return Backends.INDIRECT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getBackendNameForCrashReport() {
|
||||||
|
if (backend == null) {
|
||||||
|
return "Uninitialized";
|
||||||
|
}
|
||||||
|
var backendId = Backend.REGISTRY.getId(backend);
|
||||||
|
if (backendId == null) {
|
||||||
|
return "Unregistered";
|
||||||
|
}
|
||||||
|
return backendId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
private BackendManagerImpl() {
|
private BackendManagerImpl() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.impl.instance;
|
package com.jozufozu.flywheel.impl;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
|
||||||
|
//TODO: Add freezing
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public final class InstancingControllerRegistryImpl {
|
public final class InstancingControllerRegistryImpl {
|
||||||
@Nullable
|
@Nullable
|
|
@ -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;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -8,11 +8,10 @@ import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
|
import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstancingControllerRegistry;
|
|
||||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.storage.One2OneStorage;
|
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||||
import com.jozufozu.flywheel.backend.instancing.storage.Storage;
|
import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
|
||||||
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper;
|
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||||
|
@ -46,7 +45,7 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InstancingControllerHelper.canInstance(blockEntity.getType())) {
|
if (!InstancingControllerHelper.canInstance(blockEntity)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,17 +80,17 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
protected Instance createRaw(BlockEntity obj) {
|
protected Instance createRaw(BlockEntity obj) {
|
||||||
var controller = InstancingControllerRegistry.getController(InstancingControllerHelper.getType(obj));
|
var controller = InstancingControllerHelper.getController(obj);
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var out = controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
|
var instance = controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
|
||||||
|
|
||||||
BlockPos blockPos = obj.getBlockPos();
|
BlockPos blockPos = obj.getBlockPos();
|
||||||
posLookup.put(blockPos.asLong(), out);
|
posLookup.put(blockPos.asLong(), instance);
|
||||||
|
|
||||||
return out;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -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 org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstancingControllerRegistry;
|
|
||||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.storage.One2OneStorage;
|
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||||
import com.jozufozu.flywheel.backend.instancing.storage.Storage;
|
import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
|
||||||
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper;
|
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
@ -32,7 +31,7 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InstancingControllerHelper.canInstance(entity.getType())) {
|
if (!InstancingControllerHelper.canInstance(entity)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,10 +48,11 @@ public class EntityInstanceManager extends InstanceManager<Entity> {
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
protected Instance createRaw(Entity obj) {
|
protected Instance createRaw(Entity obj) {
|
||||||
var controller = InstancingControllerRegistry.getController(InstancingControllerHelper.getType(obj));
|
var controller = InstancingControllerHelper.getController(obj);
|
||||||
if (controller == null) {
|
if (controller == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
|
return controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,7 +1,5 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.manager;
|
package com.jozufozu.flywheel.impl.instancing.manager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -9,28 +7,26 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.instancing.ratelimit.BandedPrimeLimiter;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.ratelimit.DistanceUpdateLimiter;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.ratelimit.NonLimiter;
|
|
||||||
import com.jozufozu.flywheel.backend.instancing.storage.Storage;
|
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
|
import com.jozufozu.flywheel.impl.instancing.ratelimit.BandedPrimeLimiter;
|
||||||
|
import com.jozufozu.flywheel.impl.instancing.ratelimit.DistanceUpdateLimiter;
|
||||||
|
import com.jozufozu.flywheel.impl.instancing.ratelimit.NonLimiter;
|
||||||
|
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
|
||||||
|
|
||||||
public abstract class InstanceManager<T> {
|
public abstract class InstanceManager<T> {
|
||||||
private final Set<T> queuedAdditions = new HashSet<>(64);
|
private final Set<T> queuedAdditions = new HashSet<>(64);
|
||||||
private final Set<T> queuedUpdates = new HashSet<>(64);
|
private final Set<T> queuedUpdates = new HashSet<>(64);
|
||||||
|
|
||||||
protected DistanceUpdateLimiter frameLimiter;
|
|
||||||
protected DistanceUpdateLimiter tickLimiter;
|
protected DistanceUpdateLimiter tickLimiter;
|
||||||
|
protected DistanceUpdateLimiter frameLimiter;
|
||||||
|
|
||||||
public InstanceManager() {
|
public InstanceManager() {
|
||||||
frameLimiter = createUpdateLimiter();
|
|
||||||
tickLimiter = createUpdateLimiter();
|
tickLimiter = createUpdateLimiter();
|
||||||
|
frameLimiter = createUpdateLimiter();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract Storage<T> getStorage();
|
protected abstract Storage<T> getStorage();
|
||||||
|
@ -60,22 +56,18 @@ public abstract class InstanceManager<T> {
|
||||||
* @return The object count.
|
* @return The object count.
|
||||||
*/
|
*/
|
||||||
public int getInstanceCount() {
|
public int getInstanceCount() {
|
||||||
return getStorage().getInstanceCount();
|
return getStorage().getAllInstances().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(T obj) {
|
public void add(T obj) {
|
||||||
if (!BackendManager.isOn()) return;
|
if (!canCreateInstance(obj)) {
|
||||||
|
|
||||||
if (canCreateInstance(obj)) {
|
|
||||||
getStorage().add(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void queueAdd(T obj) {
|
|
||||||
if (!BackendManager.isOn()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStorage().add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueAdd(T obj) {
|
||||||
if (!canCreateInstance(obj)) {
|
if (!canCreateInstance(obj)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -85,38 +77,26 @@ public abstract class InstanceManager<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queueAddAll(Collection<? extends T> objects) {
|
|
||||||
if (!BackendManager.isOn() || objects.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (queuedAdditions) {
|
|
||||||
queuedAdditions.addAll(objects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the instance associated with an object.
|
* Update the instance associated with an object.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* By default this is the only hook an IInstance has to change its internal state. This is the lowest frequency
|
* By default this is the only hook an {@link Instance} has to change its internal state. This is the lowest frequency
|
||||||
* update hook IInstance gets. For more frequent updates, see {@link TickableInstance} and
|
* update hook {@link Instance} gets. For more frequent updates, see {@link TickableInstance} and
|
||||||
* {@link DynamicInstance}.
|
* {@link DynamicInstance}.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param obj the object to update.
|
* @param obj the object to update.
|
||||||
*/
|
*/
|
||||||
public void update(T obj) {
|
public void update(T obj) {
|
||||||
if (!BackendManager.isOn()) return;
|
if (!canCreateInstance(obj)) {
|
||||||
|
return;
|
||||||
if (canCreateInstance(obj)) {
|
|
||||||
getStorage().update(obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getStorage().update(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queueUpdate(T obj) {
|
public void queueUpdate(T obj) {
|
||||||
if (!BackendManager.isOn()) return;
|
|
||||||
|
|
||||||
if (!canCreateInstance(obj)) {
|
if (!canCreateInstance(obj)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -127,12 +107,10 @@ public abstract class InstanceManager<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(T obj) {
|
public void remove(T obj) {
|
||||||
if (!BackendManager.isOn()) return;
|
|
||||||
|
|
||||||
getStorage().remove(obj);
|
getStorage().remove(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onOriginShift() {
|
public void recreateAll() {
|
||||||
getStorage().recreateAll();
|
getStorage().recreateAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,21 +118,15 @@ public abstract class InstanceManager<T> {
|
||||||
getStorage().invalidate();
|
getStorage().invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void delete() {
|
|
||||||
for (Instance instance : getStorage().getAllInstances()) {
|
|
||||||
instance.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void processQueuedAdditions() {
|
protected void processQueuedAdditions() {
|
||||||
if (queuedAdditions.isEmpty()) {
|
if (queuedAdditions.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<T> queued;
|
List<T> queued;
|
||||||
|
|
||||||
synchronized (queuedAdditions) {
|
synchronized (queuedAdditions) {
|
||||||
queued = new ArrayList<>(queuedAdditions);
|
queued = List.copyOf(queuedAdditions);
|
||||||
queuedAdditions.clear();
|
queuedAdditions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,14 +136,18 @@ public abstract class InstanceManager<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processQueuedUpdates() {
|
protected void processQueuedUpdates() {
|
||||||
ArrayList<T> queued;
|
if (queuedUpdates.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<T> queued;
|
||||||
|
|
||||||
synchronized (queuedUpdates) {
|
synchronized (queuedUpdates) {
|
||||||
queued = new ArrayList<>(queuedUpdates);
|
queued = List.copyOf(queuedUpdates);
|
||||||
queuedUpdates.clear();
|
queuedUpdates.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queued.size() > 0) {
|
if (!queued.isEmpty()) {
|
||||||
queued.forEach(getStorage()::update);
|
queued.forEach(getStorage()::update);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -187,40 +163,34 @@ public abstract class InstanceManager<T> {
|
||||||
*/
|
*/
|
||||||
public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) {
|
public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) {
|
||||||
tickLimiter.tick();
|
tickLimiter.tick();
|
||||||
|
processQueuedAdditions();
|
||||||
processQueuedUpdates();
|
processQueuedUpdates();
|
||||||
|
|
||||||
var instances = getStorage().getInstancesForTicking();
|
var instances = getStorage().getTickableInstances();
|
||||||
distributeWork(executor, instances, instance -> tickInstance(instance, cameraX, cameraY, cameraZ));
|
distributeWork(executor, instances, instance -> tickInstance(instance, cameraX, cameraY, cameraZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void tickInstance(TickableInstance instance, double cX, double cY, double cZ) {
|
protected void tickInstance(TickableInstance instance, double cameraX, double cameraY, double cameraZ) {
|
||||||
if (!instance.decreaseTickRateWithDistance()) {
|
if (!instance.decreaseTickRateWithDistance() || tickLimiter.shouldUpdate(instance.distanceSquared(cameraX, cameraY, cameraZ))) {
|
||||||
instance.tick();
|
instance.tick();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var dsq = instance.distanceSquared(cX, cY, cZ);
|
|
||||||
|
|
||||||
if (!tickLimiter.shouldUpdate(dsq)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
instance.tick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginFrame(TaskExecutor executor, RenderContext context) {
|
public void beginFrame(TaskExecutor executor, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
||||||
frameLimiter.tick();
|
frameLimiter.tick();
|
||||||
processQueuedAdditions();
|
processQueuedAdditions();
|
||||||
|
processQueuedUpdates();
|
||||||
|
|
||||||
var cameraPos = context.camera()
|
var instances = getStorage().getDynamicInstances();
|
||||||
.getPosition();
|
distributeWork(executor, instances, instance -> updateInstance(instance, cameraX, cameraY, cameraZ, frustum));
|
||||||
double cX = cameraPos.x;
|
}
|
||||||
double cY = cameraPos.y;
|
|
||||||
double cZ = cameraPos.z;
|
|
||||||
FrustumIntersection culler = context.culler();
|
|
||||||
|
|
||||||
var instances = getStorage().getInstancesForUpdate();
|
protected void updateInstance(DynamicInstance instance, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
||||||
distributeWork(executor, instances, instance -> updateInstance(instance, culler, cX, cY, cZ));
|
if (!instance.decreaseFramerateWithDistance() || frameLimiter.shouldUpdate(instance.distanceSquared(cameraX, cameraY, cameraZ))) {
|
||||||
|
if (instance.isVisible(frustum)) {
|
||||||
|
instance.beginFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <I> void distributeWork(TaskExecutor executor, List<I> instances, Consumer<I> action) {
|
private static <I> void distributeWork(TaskExecutor executor, List<I> instances, Consumer<I> action) {
|
||||||
|
@ -239,19 +209,4 @@ public abstract class InstanceManager<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateInstance(DynamicInstance instance, FrustumIntersection frustum, double cX, double cY, double cZ) {
|
|
||||||
if (!instance.decreaseFramerateWithDistance()) {
|
|
||||||
instance.beginFrame();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frameLimiter.shouldUpdate(instance.distanceSquared(cX, cY, cZ))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance.checkFrustum(frustum)) {
|
|
||||||
instance.beginFrame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.ratelimit;
|
package com.jozufozu.flywheel.impl.instancing.ratelimit;
|
||||||
|
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.ratelimit;
|
package com.jozufozu.flywheel.impl.instancing.ratelimit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for rate-limiting updates based on an object's distance from the camera.
|
* Interface for rate-limiting updates based on an object's distance from the camera.
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.ratelimit;
|
package com.jozufozu.flywheel.impl.instancing.ratelimit;
|
||||||
|
|
||||||
public class NonLimiter implements DistanceUpdateLimiter {
|
public class NonLimiter implements DistanceUpdateLimiter {
|
||||||
@Override
|
@Override
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.storage;
|
package com.jozufozu.flywheel.impl.instancing.storage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -18,12 +18,12 @@ public abstract class AbstractStorage<T> implements Storage<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<TickableInstance> getInstancesForTicking() {
|
public List<TickableInstance> getTickableInstances() {
|
||||||
return tickableInstances;
|
return tickableInstances;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DynamicInstance> getInstancesForUpdate() {
|
public List<DynamicInstance> getDynamicInstances() {
|
||||||
return dynamicInstances;
|
return dynamicInstances;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -9,23 +10,17 @@ import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
|
||||||
public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
||||||
private final Map<T, Instance> instances;
|
private final Map<T, Instance> instances = new HashMap<>();
|
||||||
|
|
||||||
public One2OneStorage(Engine engine) {
|
public One2OneStorage(Engine engine) {
|
||||||
super(engine);
|
super(engine);
|
||||||
this.instances = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<Instance> getAllInstances() {
|
public Collection<Instance> getAllInstances() {
|
||||||
return instances.values();
|
return instances.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getInstanceCount() {
|
|
||||||
return instances.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(T obj) {
|
public void add(T obj) {
|
||||||
Instance instance = instances.get(obj);
|
Instance instance = instances.get(obj);
|
||||||
|
@ -43,9 +38,9 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
instance.delete();
|
|
||||||
dynamicInstances.remove(instance);
|
|
||||||
tickableInstances.remove(instance);
|
tickableInstances.remove(instance);
|
||||||
|
dynamicInstances.remove(instance);
|
||||||
|
instance.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -69,8 +64,8 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void recreateAll() {
|
public void recreateAll() {
|
||||||
dynamicInstances.clear();
|
|
||||||
tickableInstances.clear();
|
tickableInstances.clear();
|
||||||
|
dynamicInstances.clear();
|
||||||
instances.replaceAll((obj, instance) -> {
|
instances.replaceAll((obj, instance) -> {
|
||||||
instance.delete();
|
instance.delete();
|
||||||
|
|
||||||
|
@ -86,10 +81,10 @@ public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invalidate() {
|
public void invalidate() {
|
||||||
instances.values().forEach(Instance::delete);
|
|
||||||
instances.clear();
|
|
||||||
tickableInstances.clear();
|
tickableInstances.clear();
|
||||||
dynamicInstances.clear();
|
dynamicInstances.clear();
|
||||||
|
instances.values().forEach(Instance::delete);
|
||||||
|
instances.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void create(T obj) {
|
private void create(T obj) {
|
|
@ -1,5 +1,6 @@
|
||||||
package com.jozufozu.flywheel.backend.instancing.storage;
|
package com.jozufozu.flywheel.impl.instancing.storage;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||||
|
@ -7,13 +8,11 @@ import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||||
|
|
||||||
public interface Storage<T> {
|
public interface Storage<T> {
|
||||||
Iterable<Instance> getAllInstances();
|
Collection<Instance> getAllInstances();
|
||||||
|
|
||||||
int getInstanceCount();
|
List<TickableInstance> getTickableInstances();
|
||||||
|
|
||||||
List<TickableInstance> getInstancesForTicking();
|
List<DynamicInstance> getDynamicInstances();
|
||||||
|
|
||||||
List<DynamicInstance> getInstancesForUpdate();
|
|
||||||
|
|
||||||
void add(T obj);
|
void add(T obj);
|
||||||
|
|
|
@ -16,7 +16,7 @@ import net.minecraft.network.chat.TextComponent;
|
||||||
public class Backends {
|
public class Backends {
|
||||||
public static final Backend OFF = SimpleBackend.builder()
|
public static final Backend OFF = SimpleBackend.builder()
|
||||||
.engineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED))
|
.engineMessage(new TextComponent("Disabled Flywheel").withStyle(ChatFormatting.RED))
|
||||||
.engineSupplier(() -> {
|
.engineFactory(level -> {
|
||||||
throw new IllegalStateException("Cannot create engine when backend is off.");
|
throw new IllegalStateException("Cannot create engine when backend is off.");
|
||||||
})
|
})
|
||||||
.fallback(() -> Backends.OFF)
|
.fallback(() -> Backends.OFF)
|
||||||
|
@ -28,7 +28,7 @@ public class Backends {
|
||||||
*/
|
*/
|
||||||
public static final Backend BATCHING = SimpleBackend.builder()
|
public static final Backend BATCHING = SimpleBackend.builder()
|
||||||
.engineMessage(new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN))
|
.engineMessage(new TextComponent("Using Batching Engine").withStyle(ChatFormatting.GREEN))
|
||||||
.engineSupplier(BatchingEngine::new)
|
.engineFactory(level -> new BatchingEngine())
|
||||||
.fallback(() -> Backends.OFF)
|
.fallback(() -> Backends.OFF)
|
||||||
.supported(() -> !ShadersModHandler.isShaderPackInUse())
|
.supported(() -> !ShadersModHandler.isShaderPackInUse())
|
||||||
.register(Flywheel.rl("batching"));
|
.register(Flywheel.rl("batching"));
|
||||||
|
@ -38,7 +38,7 @@ public class Backends {
|
||||||
*/
|
*/
|
||||||
public static final Backend INSTANCING = SimpleBackend.builder()
|
public static final Backend INSTANCING = SimpleBackend.builder()
|
||||||
.engineMessage(new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN))
|
.engineMessage(new TextComponent("Using Instancing Engine").withStyle(ChatFormatting.GREEN))
|
||||||
.engineSupplier(() -> new InstancingEngine(Contexts.WORLD, 100 * 100))
|
.engineFactory(level -> new InstancingEngine(Contexts.WORLD, 100))
|
||||||
.fallback(() -> Backends.BATCHING)
|
.fallback(() -> Backends.BATCHING)
|
||||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||||
.instancedArraysSupported())
|
.instancedArraysSupported())
|
||||||
|
@ -50,7 +50,7 @@ public class Backends {
|
||||||
*/
|
*/
|
||||||
public static final Backend INDIRECT = SimpleBackend.builder()
|
public static final Backend INDIRECT = SimpleBackend.builder()
|
||||||
.engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
|
.engineMessage(new TextComponent("Using Indirect Engine").withStyle(ChatFormatting.GREEN))
|
||||||
.engineSupplier(() -> new IndirectEngine(Contexts.WORLD, 100 * 100))
|
.engineFactory(level -> new IndirectEngine(100))
|
||||||
.fallback(() -> Backends.INSTANCING)
|
.fallback(() -> Backends.INSTANCING)
|
||||||
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
.supported(() -> !ShadersModHandler.isShaderPackInUse() && GlCompat.getInstance()
|
||||||
.supportsIndirect())
|
.supportsIndirect())
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.jozufozu.flywheel.lib.backend;
|
package com.jozufozu.flywheel.lib.backend;
|
||||||
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
@ -11,17 +12,18 @@ import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
|
||||||
public class SimpleBackend implements Backend {
|
public class SimpleBackend implements Backend {
|
||||||
private final Component engineMessage;
|
private final Component engineMessage;
|
||||||
private final Supplier<Engine> engineSupplier;
|
private final Function<LevelAccessor, Engine> engineFactory;
|
||||||
private final Supplier<Backend> fallback;
|
private final Supplier<Backend> fallback;
|
||||||
private final BooleanSupplier isSupported;
|
private final BooleanSupplier isSupported;
|
||||||
private final Pipeline pipelineShader;
|
private final Pipeline pipelineShader;
|
||||||
|
|
||||||
public SimpleBackend(Component engineMessage, Supplier<Engine> engineSupplier, Supplier<Backend> fallback, BooleanSupplier isSupported, @Nullable Pipeline pipelineShader) {
|
public SimpleBackend(Component engineMessage, Function<LevelAccessor, Engine> engineFactory, Supplier<Backend> fallback, BooleanSupplier isSupported, @Nullable Pipeline pipelineShader) {
|
||||||
this.engineMessage = engineMessage;
|
this.engineMessage = engineMessage;
|
||||||
this.engineSupplier = engineSupplier;
|
this.engineFactory = engineFactory;
|
||||||
this.fallback = fallback;
|
this.fallback = fallback;
|
||||||
this.isSupported = isSupported;
|
this.isSupported = isSupported;
|
||||||
this.pipelineShader = pipelineShader;
|
this.pipelineShader = pipelineShader;
|
||||||
|
@ -37,8 +39,8 @@ public class SimpleBackend implements Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Engine createEngine() {
|
public Engine createEngine(LevelAccessor level) {
|
||||||
return engineSupplier.get();
|
return engineFactory.apply(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -63,7 +65,7 @@ public class SimpleBackend implements Backend {
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
private Component engineMessage;
|
private Component engineMessage;
|
||||||
private Supplier<Engine> engineSupplier;
|
private Function<LevelAccessor, Engine> engineFactory;
|
||||||
private Supplier<Backend> fallback;
|
private Supplier<Backend> fallback;
|
||||||
private BooleanSupplier isSupported;
|
private BooleanSupplier isSupported;
|
||||||
private Pipeline pipelineShader;
|
private Pipeline pipelineShader;
|
||||||
|
@ -73,8 +75,8 @@ public class SimpleBackend implements Backend {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder engineSupplier(Supplier<Engine> engineSupplier) {
|
public Builder engineFactory(Function<LevelAccessor, Engine> engineFactory) {
|
||||||
this.engineSupplier = engineSupplier;
|
this.engineFactory = engineFactory;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +96,7 @@ public class SimpleBackend implements Backend {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Backend register(ResourceLocation id) {
|
public Backend register(ResourceLocation id) {
|
||||||
return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineMessage, engineSupplier, fallback, isSupported, pipelineShader));
|
return Backend.REGISTRY.registerAndGet(id, new SimpleBackend(engineMessage, engineFactory, fallback, isSupported, pipelineShader));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,12 @@
|
||||||
package com.jozufozu.flywheel.lib.instance;
|
package com.jozufozu.flywheel.lib.instance;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
|
import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.impl.instancing.manager.BlockEntityInstanceManager;
|
||||||
import com.jozufozu.flywheel.backend.instancing.manager.BlockEntityInstanceManager;
|
|
||||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
||||||
import com.jozufozu.flywheel.lib.box.MutableBox;
|
import com.jozufozu.flywheel.lib.box.MutableBox;
|
||||||
|
|
||||||
|
@ -30,13 +26,12 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||||
* </ul>
|
* </ul>
|
||||||
* See the interfaces' documentation for more information about each one.
|
* See the interfaces' documentation for more information about each one.
|
||||||
*
|
*
|
||||||
* <br> Implementing one or more of these will give a {@link AbstractBlockEntityInstance} access
|
* <br> Implementing one or more of these will give an {@link AbstractBlockEntityInstance} access
|
||||||
* to more interesting and regular points within a tick or a frame.
|
* to more interesting and regular points within a tick or a frame.
|
||||||
*
|
*
|
||||||
* @param <T> The type of {@link BlockEntity} your class is an instance of.
|
* @param <T> The type of {@link BlockEntity} your class is an instance of.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends AbstractInstance implements BlockEntityInstance<T> {
|
public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends AbstractInstance implements BlockEntityInstance<T> {
|
||||||
|
|
||||||
protected final T blockEntity;
|
protected final T blockEntity;
|
||||||
protected final BlockPos pos;
|
protected final BlockPos pos;
|
||||||
protected final BlockPos instancePos;
|
protected final BlockPos instancePos;
|
||||||
|
@ -50,17 +45,6 @@ public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends
|
||||||
this.instancePos = pos.subtract(renderOrigin);
|
this.instancePos = pos.subtract(renderOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<InstancedPart> getCrumblingParts() {
|
|
||||||
var out = new ArrayList<InstancedPart>();
|
|
||||||
addCrumblingParts(out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addCrumblingParts(List<InstancedPart> data) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Just before {@link #update()} would be called, {@code shouldReset()} is checked.
|
* Just before {@link #update()} would be called, {@code shouldReset()} is checked.
|
||||||
* If this function returns {@code true}, then this instance will be {@link #delete removed},
|
* If this function returns {@code true}, then this instance will be {@link #delete removed},
|
||||||
|
@ -69,10 +53,21 @@ public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends
|
||||||
*
|
*
|
||||||
* @return {@code true} if this instance should be discarded and refreshed.
|
* @return {@code true} if this instance should be discarded and refreshed.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean shouldReset() {
|
public boolean shouldReset() {
|
||||||
return blockEntity.getBlockState() != blockState;
|
return blockEntity.getBlockState() != blockState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double distanceSquared(double x, double y, double z) {
|
||||||
|
return pos.distToCenterSqr(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableBox getVolume() {
|
||||||
|
return MutableBox.from(pos);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In order to accommodate for floating point precision errors at high coordinates,
|
* In order to accommodate for floating point precision errors at high coordinates,
|
||||||
* {@link BlockEntityInstanceManager}s are allowed to arbitrarily adjust the origin, and
|
* {@link BlockEntityInstanceManager}s are allowed to arbitrarily adjust the origin, and
|
||||||
|
@ -85,18 +80,7 @@ public abstract class AbstractBlockEntityInstance<T extends BlockEntity> extends
|
||||||
return pos.subtract(renderOrigin);
|
return pos.subtract(renderOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isVisible(FrustumIntersection frustum) {
|
||||||
public ImmutableBox getVolume() {
|
|
||||||
return MutableBox.from(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean checkFrustum(FrustumIntersection frustum) {
|
|
||||||
return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
|
return frustum.testAab(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public double distanceSquared(double x, double y, double z) {
|
|
||||||
return pos.distToCenterSqr(x, y, z);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,19 +6,19 @@ import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.EntityInstance;
|
import com.jozufozu.flywheel.api.instance.EntityInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
import com.jozufozu.flywheel.backend.instancing.manager.BlockEntityInstanceManager;
|
import com.jozufozu.flywheel.impl.instancing.manager.BlockEntityInstanceManager;
|
||||||
|
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
||||||
import com.jozufozu.flywheel.lib.box.MutableBox;
|
import com.jozufozu.flywheel.lib.box.MutableBox;
|
||||||
import com.jozufozu.flywheel.lib.light.TickingLightListener;
|
import com.jozufozu.flywheel.lib.light.TickingLightListener;
|
||||||
import com.mojang.math.Vector3f;
|
import com.mojang.math.Vector3f;
|
||||||
|
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The layer between a {@link BlockEntity} and the Flywheel backend.
|
* The layer between an {@link Entity} and the Flywheel backend.
|
||||||
* *
|
* *
|
||||||
* <br><br> There are a few additional features that overriding classes can opt in to:
|
* <br><br> There are a few additional features that overriding classes can opt in to:
|
||||||
* <ul>
|
* <ul>
|
||||||
|
@ -27,13 +27,12 @@ import net.minecraft.world.phys.Vec3;
|
||||||
* </ul>
|
* </ul>
|
||||||
* See the interfaces' documentation for more information about each one.
|
* See the interfaces' documentation for more information about each one.
|
||||||
*
|
*
|
||||||
* <br> Implementing one or more of these will give a {@link AbstractEntityInstance} access
|
* <br> Implementing one or more of these will give an {@link AbstractEntityInstance} access
|
||||||
* to more interesting and regular points within a tick or a frame.
|
* to more interesting and regular points within a tick or a frame.
|
||||||
*
|
*
|
||||||
* @param <E> The type of {@link Entity} your class is an instance of.
|
* @param <E> The type of {@link Entity} your class is an instance of.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractEntityInstance<E extends Entity> extends AbstractInstance implements EntityInstance<E>, TickingLightListener {
|
public abstract class AbstractEntityInstance<E extends Entity> extends AbstractInstance implements EntityInstance<E>, TickingLightListener {
|
||||||
|
|
||||||
protected final E entity;
|
protected final E entity;
|
||||||
protected final MutableBox bounds;
|
protected final MutableBox bounds;
|
||||||
|
|
||||||
|
@ -44,7 +43,12 @@ public abstract class AbstractEntityInstance<E extends Entity> extends AbstractI
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MutableBox getVolume() {
|
public double distanceSquared(double x, double y, double z) {
|
||||||
|
return entity.distanceToSqr(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ImmutableBox getVolume() {
|
||||||
return bounds;
|
return bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,14 +91,8 @@ public abstract class AbstractEntityInstance<E extends Entity> extends AbstractI
|
||||||
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()), (float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()), (float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
|
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()), (float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()), (float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean isVisible(FrustumIntersection frustum) {
|
||||||
public boolean checkFrustum(FrustumIntersection frustum) {
|
|
||||||
AABB aabb = entity.getBoundingBox();
|
AABB aabb = entity.getBoundingBox();
|
||||||
return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
|
return frustum.testAab((float) aabb.minX, (float) aabb.minY, (float) aabb.minZ, (float) aabb.maxX, (float) aabb.maxY, (float) aabb.maxZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public double distanceSquared(double x, double y, double z) {
|
|
||||||
return entity.distanceToSqr(x, y, z);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
package com.jozufozu.flywheel.lib.instance;
|
package com.jozufozu.flywheel.lib.instance;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
import com.jozufozu.flywheel.api.instancer.FlatLit;
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancerProvider;
|
import com.jozufozu.flywheel.api.instancer.InstancerProvider;
|
||||||
import com.jozufozu.flywheel.lib.light.LightListener;
|
import com.jozufozu.flywheel.lib.light.LightListener;
|
||||||
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
||||||
|
import com.jozufozu.flywheel.lib.struct.FlatLit;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.SectionPos;
|
import net.minecraft.core.SectionPos;
|
||||||
|
@ -17,23 +16,22 @@ import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.LightLayer;
|
import net.minecraft.world.level.LightLayer;
|
||||||
|
|
||||||
public abstract class AbstractInstance implements Instance, LightListener {
|
public abstract class AbstractInstance implements Instance, LightListener {
|
||||||
public final Level level;
|
protected final InstancerProvider instancerProvider;
|
||||||
public final Vec3i renderOrigin;
|
protected final Vec3i renderOrigin;
|
||||||
|
protected final Level level;
|
||||||
|
|
||||||
protected final InstancerProvider instancerManager;
|
|
||||||
protected boolean deleted = false;
|
protected boolean deleted = false;
|
||||||
|
|
||||||
public AbstractInstance(InstanceContext ctx, Level level) {
|
public AbstractInstance(InstanceContext ctx, Level level) {
|
||||||
this.instancerManager = ctx.instancerProvider();
|
this.instancerProvider = ctx.instancerProvider();
|
||||||
this.renderOrigin = ctx.renderOrigin();
|
this.renderOrigin = ctx.renderOrigin();
|
||||||
this.level = level;
|
this.level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
|
LightUpdater.get(level).addListener(this);
|
||||||
updateLight();
|
updateLight();
|
||||||
LightUpdater.get(level)
|
|
||||||
.addListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,31 +63,31 @@ public abstract class AbstractInstance implements Instance, LightListener {
|
||||||
deleted = true;
|
deleted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInvalid() {
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLightUpdate(LightLayer type, SectionPos pos) {
|
public void onLightUpdate(LightLayer type, SectionPos pos) {
|
||||||
updateLight();
|
updateLight();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void relight(BlockPos pos, FlatLit<?>... models) {
|
@Override
|
||||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), models);
|
public boolean isInvalid() {
|
||||||
|
return deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> models) {
|
protected void relight(BlockPos pos, FlatLit<?>... parts) {
|
||||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), models);
|
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void relight(int block, int sky, FlatLit<?>... models) {
|
protected void relight(int block, int sky, FlatLit<?>... parts) {
|
||||||
relight(block, sky, Arrays.stream(models));
|
for (FlatLit<?> part : parts) {
|
||||||
|
part.setLight(block, sky);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> models) {
|
protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> parts) {
|
||||||
models.forEach(model -> model.setBlockLight(block)
|
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
|
||||||
.setSkyLight(sky));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> parts) {
|
||||||
|
parts.forEach(model -> model.setLight(block, sky));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.material.Material;
|
||||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexList;
|
import com.jozufozu.flywheel.api.vertex.VertexList;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexListProvider;
|
import com.jozufozu.flywheel.api.vertex.VertexListProviderRegistry;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.lib.format.Formats;
|
import com.jozufozu.flywheel.lib.format.Formats;
|
||||||
import com.jozufozu.flywheel.lib.material.Materials;
|
import com.jozufozu.flywheel.lib.material.Materials;
|
||||||
|
@ -62,7 +62,7 @@ public class ModelUtil {
|
||||||
long srcPtr = MemoryUtil.memAddress(src);
|
long srcPtr = MemoryUtil.memAddress(src);
|
||||||
long dstPtr = dst.ptr();
|
long dstPtr = dst.ptr();
|
||||||
|
|
||||||
ReusableVertexList srcList = VertexListProvider.get(srcFormat).createVertexList();
|
ReusableVertexList srcList = VertexListProviderRegistry.getProvider(srcFormat).createVertexList();
|
||||||
ReusableVertexList dstList = dstVertexType.createVertexList();
|
ReusableVertexList dstList = dstVertexType.createVertexList();
|
||||||
srcList.ptr(srcPtr);
|
srcList.ptr(srcPtr);
|
||||||
dstList.ptr(dstPtr);
|
dstList.ptr(dstPtr);
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.jozufozu.flywheel.lib.struct;
|
package com.jozufozu.flywheel.lib.struct;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.FlatLit;
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.struct.StructType;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
package com.jozufozu.flywheel.api.instancer;
|
package com.jozufozu.flywheel.lib.struct;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.instancer.InstancedPart;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.level.BlockAndTintGetter;
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
|
@ -27,9 +29,13 @@ public interface FlatLit<D extends InstancedPart & FlatLit<D>> {
|
||||||
*/
|
*/
|
||||||
D setSkyLight(int skyLight);
|
D setSkyLight(int skyLight);
|
||||||
|
|
||||||
|
default D setLight(int blockLight, int skyLight) {
|
||||||
|
return setBlockLight(blockLight)
|
||||||
|
.setSkyLight(skyLight);
|
||||||
|
}
|
||||||
|
|
||||||
default D updateLight(BlockAndTintGetter level, BlockPos pos) {
|
default D updateLight(BlockAndTintGetter level, BlockPos pos) {
|
||||||
return setBlockLight(level.getBrightness(LightLayer.BLOCK, pos))
|
return setLight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos));
|
||||||
.setSkyLight(level.getBrightness(LightLayer.SKY, pos));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int getPackedLight();
|
int getPackedLight();
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
|
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
|
||||||
import com.jozufozu.flywheel.api.event.RenderContext;
|
import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.jozufozu.flywheel.util.MatrixUtil;
|
import com.jozufozu.flywheel.util.MatrixUtil;
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
@ -73,13 +73,13 @@ public class FlwShaderUniforms implements ShaderUniforms {
|
||||||
}
|
}
|
||||||
RenderContext context = event.getContext();
|
RenderContext context = event.getContext();
|
||||||
|
|
||||||
Vec3i originCoordinate = InstancedRenderDispatcher.getOriginCoordinate(context.level());
|
Vec3i renderOrigin = InstancedRenderDispatcher.getRenderOrigin(context.level());
|
||||||
Vec3 camera = context.camera()
|
Vec3 camera = context.camera()
|
||||||
.getPosition();
|
.getPosition();
|
||||||
|
|
||||||
var camX = (float) (camera.x - originCoordinate.getX());
|
var camX = (float) (camera.x - renderOrigin.getX());
|
||||||
var camY = (float) (camera.y - originCoordinate.getY());
|
var camY = (float) (camera.y - renderOrigin.getY());
|
||||||
var camZ = (float) (camera.z - originCoordinate.getZ());
|
var camZ = (float) (camera.z - renderOrigin.getZ());
|
||||||
|
|
||||||
// don't want to mutate viewProjection
|
// don't want to mutate viewProjection
|
||||||
var vp = context.viewProjection()
|
var vp = context.viewProjection()
|
||||||
|
|
|
@ -9,9 +9,9 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
||||||
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper;
|
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
@ -29,7 +29,7 @@ public abstract class ClientLevelMixin implements ClientLevelExtension {
|
||||||
|
|
||||||
@Inject(method = "entitiesForRendering", at = @At("RETURN"), cancellable = true)
|
@Inject(method = "entitiesForRendering", at = @At("RETURN"), cancellable = true)
|
||||||
private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
|
private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
|
||||||
if (BackendManager.isOn()) {
|
if (BackendUtil.canUseInstancing((ClientLevel) (Object) this)) {
|
||||||
Iterable<Entity> entities = cir.getReturnValue();
|
Iterable<Entity> entities = cir.getReturnValue();
|
||||||
ArrayList<Entity> filtered = Lists.newArrayList(entities);
|
ArrayList<Entity> filtered = Lists.newArrayList(entities);
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,8 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.backend.BackendUtil;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.lib.instance.InstancingControllerHelper;
|
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
@ -19,7 +19,7 @@ public class ChunkRebuildHooksMixin {
|
||||||
@Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "handleBlockEntity", at = @At("HEAD"), cancellable = true)
|
||||||
private <E extends BlockEntity> void flywheel$addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set<BlockEntity> set, E be, CallbackInfo ci) {
|
private <E extends BlockEntity> void flywheel$addAndFilterBEs(ChunkRenderDispatcher.CompiledChunk compiledChunk, Set<BlockEntity> set, E be, CallbackInfo ci) {
|
||||||
if (BackendUtil.canUseInstancing(be.getLevel())) {
|
if (BackendUtil.canUseInstancing(be.getLevel())) {
|
||||||
if (InstancingControllerHelper.canInstance(be.getType()))
|
if (InstancingControllerHelper.canInstance(be))
|
||||||
InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be);
|
InstancedRenderDispatcher.getBlockEntities(be.getLevel()).queueAdd(be);
|
||||||
|
|
||||||
if (InstancingControllerHelper.shouldSkipRender(be))
|
if (InstancingControllerHelper.shouldSkipRender(be))
|
||||||
|
|
|
@ -7,8 +7,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
@ -22,10 +22,10 @@ public class InstanceAddMixin {
|
||||||
|
|
||||||
@Inject(method = "setBlockEntity",
|
@Inject(method = "setBlockEntity",
|
||||||
at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"))
|
at = @At(value = "INVOKE_ASSIGN", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"))
|
||||||
private void flywheel$onBlockEntityAdded(BlockEntity be, CallbackInfo ci) {
|
private void flywheel$onBlockEntityAdded(BlockEntity blockEntity, CallbackInfo ci) {
|
||||||
if (level.isClientSide && BackendManager.isOn()) {
|
if (level.isClientSide && BackendUtil.canUseInstancing(level)) {
|
||||||
InstancedRenderDispatcher.getBlockEntities(this.level)
|
InstancedRenderDispatcher.getBlockEntities(level)
|
||||||
.add(be);
|
.add(blockEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
@ -22,8 +22,8 @@ public class InstanceRemoveMixin {
|
||||||
|
|
||||||
@Inject(at = @At("TAIL"), method = "setRemoved")
|
@Inject(at = @At("TAIL"), method = "setRemoved")
|
||||||
private void flywheel$removeInstance(CallbackInfo ci) {
|
private void flywheel$removeInstance(CallbackInfo ci) {
|
||||||
if (level instanceof ClientLevel && BackendManager.isOn()) {
|
if (level instanceof ClientLevel && BackendUtil.canUseInstancing(level)) {
|
||||||
InstancedRenderDispatcher.getBlockEntities(this.level)
|
InstancedRenderDispatcher.getBlockEntities(level)
|
||||||
.remove((BlockEntity) (Object) this);
|
.remove((BlockEntity) (Object) this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.BackendManager;
|
import com.jozufozu.flywheel.backend.BackendUtil;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
import net.minecraft.client.renderer.LevelRenderer;
|
import net.minecraft.client.renderer.LevelRenderer;
|
||||||
|
@ -25,7 +25,7 @@ public class InstanceUpdateMixin {
|
||||||
*/
|
*/
|
||||||
@Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V")
|
@Inject(at = @At("TAIL"), method = "setBlockDirty(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/block/state/BlockState;)V")
|
||||||
private void flywheel$checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
|
private void flywheel$checkUpdate(BlockPos pos, BlockState lastState, BlockState newState, CallbackInfo ci) {
|
||||||
if (!BackendManager.isOn()) {
|
if (!BackendUtil.canUseInstancing(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
BlockEntity blockEntity = level.getBlockEntity(pos);
|
BlockEntity blockEntity = level.getBlockEntity(pos);
|
||||||
|
|
|
@ -78,14 +78,18 @@ public class WorldAttached<T> {
|
||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public T replace(LevelAccessor world, Consumer<T> finalizer) {
|
public T replace(LevelAccessor world, Consumer<T> finalizer) {
|
||||||
T remove = attached.remove(world);
|
remove(world, finalizer);
|
||||||
|
|
||||||
if (remove != null)
|
|
||||||
finalizer.accept(remove);
|
|
||||||
|
|
||||||
return get(world);
|
return get(world);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void remove(LevelAccessor world, Consumer<T> finalizer) {
|
||||||
|
T removed = attached.remove(world);
|
||||||
|
|
||||||
|
if (removed != null)
|
||||||
|
finalizer.accept(removed);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes all entries after calling a function on them.
|
* Deletes all entries after calling a function on them.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.jozufozu.flywheel.vanilla;
|
package com.jozufozu.flywheel.vanilla;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
@ -24,18 +23,22 @@ import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.level.block.entity.BellBlockEntity;
|
import net.minecraft.world.level.block.entity.BellBlockEntity;
|
||||||
|
|
||||||
public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> implements DynamicInstance {
|
public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> implements DynamicInstance {
|
||||||
|
|
||||||
private static final SimpleLazyModel MODEL = new SimpleLazyModel(BellInstance::createBellModel, Materials.BELL);
|
private static final SimpleLazyModel MODEL = new SimpleLazyModel(BellInstance::createBellModel, Materials.BELL);
|
||||||
|
|
||||||
private final OrientedPart bell;
|
private OrientedPart bell;
|
||||||
|
|
||||||
private float lastRingTime = Float.NaN;
|
private float lastRingTime = Float.NaN;
|
||||||
|
|
||||||
public BellInstance(InstanceContext ctx, BellBlockEntity blockEntity) {
|
public BellInstance(InstanceContext ctx, BellBlockEntity blockEntity) {
|
||||||
super(ctx, blockEntity);
|
super(ctx, blockEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
bell = createBellInstance().setPivot(0.5f, 0.75f, 0.5f)
|
bell = createBellInstance().setPivot(0.5f, 0.75f, 0.5f)
|
||||||
.setPosition(getInstancePosition());
|
.setPosition(getInstancePosition());
|
||||||
|
|
||||||
|
super.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,8 +68,8 @@ public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> i
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCrumblingParts(List<InstancedPart> data) {
|
public List<InstancedPart> getCrumblingParts() {
|
||||||
Collections.addAll(data, bell);
|
return List.of(bell);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,7 +78,7 @@ public class BellInstance extends AbstractBlockEntityInstance<BellBlockEntity> i
|
||||||
}
|
}
|
||||||
|
|
||||||
private OrientedPart createBellInstance() {
|
private OrientedPart createBellInstance() {
|
||||||
return instancerManager.getInstancer(StructTypes.ORIENTED, MODEL, RenderStage.AFTER_BLOCK_ENTITIES)
|
return instancerProvider.instancer(StructTypes.ORIENTED, MODEL, RenderStage.AFTER_BLOCK_ENTITIES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,9 @@
|
||||||
package com.jozufozu.flywheel.vanilla;
|
package com.jozufozu.flywheel.vanilla;
|
||||||
|
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
|
@ -36,24 +33,25 @@ import net.minecraft.world.level.block.entity.LidBlockEntity;
|
||||||
import net.minecraft.world.level.block.state.properties.ChestType;
|
import net.minecraft.world.level.block.state.properties.ChestType;
|
||||||
|
|
||||||
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends AbstractBlockEntityInstance<T> implements DynamicInstance {
|
public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends AbstractBlockEntityInstance<T> implements DynamicInstance {
|
||||||
|
|
||||||
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createLidModel(type, mat), Materials.CHEST));
|
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createLidModel(type, mat), Materials.CHEST));
|
||||||
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createBaseModel(type, mat), Materials.CHEST));
|
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createBaseModel(type, mat), Materials.CHEST));
|
||||||
|
|
||||||
private final OrientedPart body;
|
private OrientedPart body;
|
||||||
private final TransformedPart lid;
|
private TransformedPart lid;
|
||||||
|
|
||||||
private final Float2FloatFunction lidProgress;
|
private Float2FloatFunction lidProgress;
|
||||||
private final TextureAtlasSprite sprite;
|
private TextureAtlasSprite sprite;
|
||||||
@NotNull
|
private ChestType chestType;
|
||||||
private final ChestType chestType;
|
private Quaternion baseRotation;
|
||||||
private final Quaternion baseRotation;
|
|
||||||
|
|
||||||
private float lastProgress = Float.NaN;
|
private float lastProgress = Float.NaN;
|
||||||
|
|
||||||
public ChestInstance(InstanceContext ctx, T blockEntity) {
|
public ChestInstance(InstanceContext ctx, T blockEntity) {
|
||||||
super(ctx, blockEntity);
|
super(ctx, blockEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
Block block = blockState.getBlock();
|
Block block = blockState.getBlock();
|
||||||
|
|
||||||
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
|
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
|
||||||
|
@ -64,7 +62,6 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
|
||||||
lid = lidInstance();
|
lid = lidInstance();
|
||||||
|
|
||||||
if (block instanceof AbstractChestBlock<?> chestBlock) {
|
if (block instanceof AbstractChestBlock<?> chestBlock) {
|
||||||
|
|
||||||
float horizontalAngle = blockState.getValue(ChestBlock.FACING).toYRot();
|
float horizontalAngle = blockState.getValue(ChestBlock.FACING).toYRot();
|
||||||
|
|
||||||
baseRotation = Vector3f.YP.rotationDegrees(-horizontalAngle);
|
baseRotation = Vector3f.YP.rotationDegrees(-horizontalAngle);
|
||||||
|
@ -78,6 +75,8 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
|
||||||
baseRotation = Quaternion.ONE;
|
baseRotation = Quaternion.ONE;
|
||||||
lidProgress = $ -> 0f;
|
lidProgress = $ -> 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
super.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -112,8 +111,8 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCrumblingParts(List<InstancedPart> data) {
|
public List<InstancedPart> getCrumblingParts() {
|
||||||
Collections.addAll(data, body, lid);
|
return List.of(body, lid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -123,12 +122,12 @@ public class ChestInstance<T extends BlockEntity & LidBlockEntity> extends Abstr
|
||||||
}
|
}
|
||||||
|
|
||||||
private OrientedPart baseInstance() {
|
private OrientedPart baseInstance() {
|
||||||
return instancerManager.getInstancer(StructTypes.ORIENTED, BASE.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
|
return instancerProvider.instancer(StructTypes.ORIENTED, BASE.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransformedPart lidInstance() {
|
private TransformedPart lidInstance() {
|
||||||
return instancerManager.getInstancer(StructTypes.TRANSFORMED, LID.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
|
return instancerProvider.instancer(StructTypes.TRANSFORMED, LID.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,22 +26,26 @@ import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntityInstance<T> implements DynamicInstance, TickableInstance {
|
public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntityInstance<T> implements DynamicInstance, TickableInstance {
|
||||||
|
|
||||||
private static final SimpleLazyModel MODEL = new SimpleLazyModel(MinecartInstance::getBodyModel, Materials.MINECART);
|
private static final SimpleLazyModel MODEL = new SimpleLazyModel(MinecartInstance::getBodyModel, Materials.MINECART);
|
||||||
|
|
||||||
private final PoseStack stack = new PoseStack();
|
private final PoseStack stack = new PoseStack();
|
||||||
|
|
||||||
private final TransformedPart body;
|
private TransformedPart body;
|
||||||
private TransformedPart contents;
|
private TransformedPart contents;
|
||||||
private BlockState blockState;
|
private BlockState blockState;
|
||||||
private boolean active;
|
private boolean active;
|
||||||
|
|
||||||
public MinecartInstance(InstanceContext ctx, T entity) {
|
public MinecartInstance(InstanceContext ctx, T entity) {
|
||||||
super(ctx, entity);
|
super(ctx, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
body = getBody();
|
body = getBody();
|
||||||
blockState = entity.getDisplayBlockState();
|
blockState = entity.getDisplayBlockState();
|
||||||
contents = getContents();
|
contents = getContents();
|
||||||
|
|
||||||
|
super.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -167,12 +171,12 @@ public class MinecartInstance<T extends AbstractMinecart> extends AbstractEntity
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return instancerManager.getInstancer(StructTypes.TRANSFORMED, Models.block(blockState), RenderStage.AFTER_ENTITIES)
|
return instancerProvider.instancer(StructTypes.TRANSFORMED, Models.block(blockState), RenderStage.AFTER_ENTITIES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransformedPart getBody() {
|
private TransformedPart getBody() {
|
||||||
return instancerManager.getInstancer(StructTypes.TRANSFORMED, MODEL, RenderStage.AFTER_ENTITIES)
|
return instancerProvider.instancer(StructTypes.TRANSFORMED, MODEL, RenderStage.AFTER_ENTITIES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package com.jozufozu.flywheel.vanilla;
|
package com.jozufozu.flywheel.vanilla;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@ -33,17 +32,20 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
|
||||||
private static final Function<TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize(it -> new SimpleLazyModel(() -> makeBaseModel(it), Materials.SHULKER));
|
private static final Function<TextureAtlasSprite, SimpleLazyModel> BASE = Util.memoize(it -> new SimpleLazyModel(() -> makeBaseModel(it), Materials.SHULKER));
|
||||||
private static final Function<TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize(it -> new SimpleLazyModel(() -> makeLidModel(it), Materials.SHULKER));
|
private static final Function<TextureAtlasSprite, SimpleLazyModel> LID = Util.memoize(it -> new SimpleLazyModel(() -> makeLidModel(it), Materials.SHULKER));
|
||||||
|
|
||||||
private final TextureAtlasSprite texture;
|
private TextureAtlasSprite texture;
|
||||||
|
|
||||||
private final TransformedPart base;
|
private TransformedPart base;
|
||||||
private final TransformedPart lid;
|
private TransformedPart lid;
|
||||||
private final PoseStack stack = new PoseStack();
|
private final PoseStack stack = new PoseStack();
|
||||||
|
|
||||||
private float lastProgress = Float.NaN;
|
private float lastProgress = Float.NaN;
|
||||||
|
|
||||||
public ShulkerBoxInstance(InstanceContext ctx, ShulkerBoxBlockEntity blockEntity) {
|
public ShulkerBoxInstance(InstanceContext ctx, ShulkerBoxBlockEntity blockEntity) {
|
||||||
super(ctx, blockEntity);
|
super(ctx, blockEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init() {
|
||||||
DyeColor color = blockEntity.getColor();
|
DyeColor color = blockEntity.getColor();
|
||||||
if (color == null) {
|
if (color == null) {
|
||||||
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite();
|
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite();
|
||||||
|
@ -67,6 +69,8 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
|
||||||
tstack.translateY(0.25);
|
tstack.translateY(0.25);
|
||||||
|
|
||||||
lid = makeLidInstance().setTransform(stack);
|
lid = makeLidInstance().setTransform(stack);
|
||||||
|
|
||||||
|
super.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,8 +95,8 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCrumblingParts(List<InstancedPart> data) {
|
public List<InstancedPart> getCrumblingParts() {
|
||||||
Collections.addAll(data, base, lid);
|
return List.of(base, lid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -107,12 +111,12 @@ public class ShulkerBoxInstance extends AbstractBlockEntityInstance<ShulkerBoxBl
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransformedPart makeBaseInstance() {
|
private TransformedPart makeBaseInstance() {
|
||||||
return instancerManager.getInstancer(StructTypes.TRANSFORMED, BASE.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
return instancerProvider.instancer(StructTypes.TRANSFORMED, BASE.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransformedPart makeLidInstance() {
|
private TransformedPart makeLidInstance() {
|
||||||
return instancerManager.getInstancer(StructTypes.TRANSFORMED, LID.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
return instancerProvider.instancer(StructTypes.TRANSFORMED, LID.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,11 +11,11 @@ import org.joml.Vector3f;
|
||||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.instance.EffectInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
||||||
import com.jozufozu.flywheel.api.instance.effect.Effect;
|
import com.jozufozu.flywheel.api.instance.effect.Effect;
|
||||||
import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
import com.jozufozu.flywheel.lib.box.ImmutableBox;
|
||||||
import com.jozufozu.flywheel.lib.box.MutableBox;
|
import com.jozufozu.flywheel.lib.box.MutableBox;
|
||||||
import com.jozufozu.flywheel.lib.instance.AbstractInstance;
|
import com.jozufozu.flywheel.lib.instance.AbstractInstance;
|
||||||
|
@ -110,7 +110,7 @@ public class ExampleEffect implements Effect {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<Instance> createInstances(InstanceContext ctx) {
|
public Collection<EffectInstance<?>> createInstances(InstanceContext ctx) {
|
||||||
effects.clear();
|
effects.clear();
|
||||||
boids.clear();
|
boids.clear();
|
||||||
for (int i = 0; i < INSTANCE_COUNT; i++) {
|
for (int i = 0; i < INSTANCE_COUNT; i++) {
|
||||||
|
@ -238,10 +238,10 @@ public class ExampleEffect implements Effect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BoidInstance extends AbstractInstance implements DynamicInstance, TickableInstance {
|
public class BoidInstance extends AbstractInstance implements EffectInstance<ExampleEffect>, DynamicInstance, TickableInstance {
|
||||||
|
|
||||||
private final Boid self;
|
private final Boid self;
|
||||||
TransformedPart instance;
|
private TransformedPart instance;
|
||||||
|
|
||||||
public BoidInstance(InstanceContext ctx, Level level, Boid self) {
|
public BoidInstance(InstanceContext ctx, Level level, Boid self) {
|
||||||
super(ctx, level);
|
super(ctx, level);
|
||||||
|
@ -250,11 +250,13 @@ public class ExampleEffect implements Effect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public void init() {
|
||||||
instance = instancerManager.getInstancer(StructTypes.TRANSFORMED, Models.block(Blocks.SHROOMLIGHT.defaultBlockState()), RenderStage.AFTER_PARTICLES)
|
instance = instancerProvider.instancer(StructTypes.TRANSFORMED, Models.block(Blocks.SHROOMLIGHT.defaultBlockState()), RenderStage.AFTER_PARTICLES)
|
||||||
.createInstance();
|
.createInstance();
|
||||||
|
|
||||||
instance.setBlockLight(15)
|
instance.setBlockLight(15)
|
||||||
.setSkyLight(15);
|
.setSkyLight(15);
|
||||||
|
|
||||||
|
super.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -297,7 +299,7 @@ public class ExampleEffect implements Effect {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean checkFrustum(FrustumIntersection frustum) {
|
public boolean isVisible(FrustumIntersection frustum) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue