mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-27 20:24:39 +01:00
Flywheel's Visualized Rendering
- Rename Instance -> Visual (and all related elements) - Rename InstancePart -> Instance (and all related elements) - Move classes in package lib.format -> lib.vertex - Rename Formats -> VertexTypes - Remove SimpleLazyModel#setMaterial - Remove unnecessary newlines in some files
This commit is contained in:
parent
6648751ef7
commit
3b62a4d721
151 changed files with 2056 additions and 2106 deletions
|
@ -15,18 +15,18 @@ import com.jozufozu.flywheel.handler.ForgeEvents;
|
||||||
import com.jozufozu.flywheel.impl.BackendManagerImpl;
|
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.impl.visualization.VisualizedRenderDispatcher;
|
||||||
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.instance.InstanceTypes;
|
||||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||||
import com.jozufozu.flywheel.lib.material.Materials;
|
import com.jozufozu.flywheel.lib.material.Materials;
|
||||||
import com.jozufozu.flywheel.lib.model.Models;
|
import com.jozufozu.flywheel.lib.model.Models;
|
||||||
import com.jozufozu.flywheel.lib.model.PartialModel;
|
import com.jozufozu.flywheel.lib.model.PartialModel;
|
||||||
import com.jozufozu.flywheel.lib.struct.StructTypes;
|
|
||||||
import com.jozufozu.flywheel.lib.util.QuadConverter;
|
import com.jozufozu.flywheel.lib.util.QuadConverter;
|
||||||
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
||||||
|
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||||
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
|
import com.jozufozu.flywheel.mixin.PausedPartialTickAccessor;
|
||||||
import com.jozufozu.flywheel.vanilla.VanillaInstances;
|
import com.jozufozu.flywheel.vanilla.VanillaVisuals;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypes;
|
import net.minecraft.commands.synchronization.ArgumentTypes;
|
||||||
|
@ -82,9 +82,9 @@ public class Flywheel {
|
||||||
forgeEventBus.addListener(Models::onReloadRenderers);
|
forgeEventBus.addListener(Models::onReloadRenderers);
|
||||||
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
|
forgeEventBus.addListener(DrawBuffer::onReloadRenderers);
|
||||||
|
|
||||||
forgeEventBus.addListener(InstancedRenderDispatcher::onRenderStage);
|
forgeEventBus.addListener(VisualizedRenderDispatcher::onRenderStage);
|
||||||
forgeEventBus.addListener(InstancedRenderDispatcher::onBeginFrame);
|
forgeEventBus.addListener(VisualizedRenderDispatcher::onBeginFrame);
|
||||||
forgeEventBus.addListener(InstancedRenderDispatcher::tick);
|
forgeEventBus.addListener(VisualizedRenderDispatcher::tick);
|
||||||
|
|
||||||
forgeEventBus.addListener(EntityWorldHandler::onEntityJoinWorld);
|
forgeEventBus.addListener(EntityWorldHandler::onEntityJoinWorld);
|
||||||
forgeEventBus.addListener(EntityWorldHandler::onEntityLeaveWorld);
|
forgeEventBus.addListener(EntityWorldHandler::onEntityLeaveWorld);
|
||||||
|
@ -107,14 +107,14 @@ public class Flywheel {
|
||||||
|
|
||||||
ShadersModHandler.init();
|
ShadersModHandler.init();
|
||||||
|
|
||||||
Formats.init();
|
VertexTypes.init();
|
||||||
StructTypes.init();
|
InstanceTypes.init();
|
||||||
Materials.init();
|
Materials.init();
|
||||||
Contexts.init();
|
Contexts.init();
|
||||||
|
|
||||||
MaterialIndices.init();
|
MaterialIndices.init();
|
||||||
|
|
||||||
VanillaInstances.init();
|
VanillaVisuals.init();
|
||||||
|
|
||||||
// https://github.com/Jozufozu/Flywheel/issues/69
|
// https://github.com/Jozufozu/Flywheel/issues/69
|
||||||
// Weird issue with accessor loading.
|
// Weird issue with accessor loading.
|
||||||
|
|
|
@ -4,7 +4,7 @@ import java.util.List;
|
||||||
|
|
||||||
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.InstancerProvider;
|
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
|
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
|
|
|
@ -14,7 +14,6 @@ import net.minecraft.client.renderer.RenderBuffers;
|
||||||
|
|
||||||
public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection,
|
public record RenderContext(LevelRenderer renderer, ClientLevel level, PoseStack stack, Matrix4f viewProjection,
|
||||||
Matrix4f projection, RenderBuffers buffers, Camera camera, FrustumIntersection culler) {
|
Matrix4f projection, RenderBuffers buffers, Camera camera, FrustumIntersection culler) {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static Matrix4f createViewProjection(PoseStack view, Matrix4f projection) {
|
public static Matrix4f createViewProjection(PoseStack view, Matrix4f projection) {
|
||||||
var viewProjection = projection.copy();
|
var viewProjection = projection.copy();
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
|
|
||||||
public interface BlockEntityInstance<T extends BlockEntity> extends Instance {
|
|
||||||
List<InstancePart> getCrumblingParts();
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.effect.Effect;
|
|
||||||
|
|
||||||
public interface EffectInstance<T extends Effect> extends Instance {
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
|
|
||||||
public interface EntityInstance<T extends Entity> extends Instance {
|
|
||||||
}
|
|
|
@ -1,48 +1,8 @@
|
||||||
package com.jozufozu.flywheel.api.instance;
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
/**
|
|
||||||
* A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
|
|
||||||
*/
|
|
||||||
public interface Instance {
|
public interface Instance {
|
||||||
|
InstanceType<?> type();
|
||||||
|
|
||||||
/**
|
@Deprecated
|
||||||
* Initialize parts here.
|
Instance copy(InstanceHandle handle);
|
||||||
*/
|
|
||||||
void init();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
|
|
||||||
*
|
|
||||||
* <br><br> If your animations are complex or more CPU driven, see {@link DynamicInstance} or {@link TickableInstance}.
|
|
||||||
*/
|
|
||||||
void update();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When an instance is reset, the instance is deleted and re-created.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* Just before {@link #update()} would be called, {@code shouldReset()} is checked.
|
|
||||||
* If this function returns {@code true}, then this instance will be {@link #delete deleted},
|
|
||||||
* and another instance will be constructed to replace it. This allows for more sane resource
|
|
||||||
* acquisition compared to trying to update everything within the lifetime of an instance.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @return {@code true} if this instance should be discarded and refreshed.
|
|
||||||
*/
|
|
||||||
boolean shouldReset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the distance squared between this instance and the given <em>world</em> position.
|
|
||||||
*
|
|
||||||
* @param x The x coordinate.
|
|
||||||
* @param y The y coordinate.
|
|
||||||
* @param z The z coordinate.
|
|
||||||
* @return The distance squared between this instance and the given position.
|
|
||||||
*/
|
|
||||||
double distanceSquared(double x, double y, double z);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free any acquired resources.
|
|
||||||
*/
|
|
||||||
void delete();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
|
public interface InstanceHandle {
|
||||||
|
void setChanged();
|
||||||
|
|
||||||
|
void setDeleted();
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.layout.BufferLayout;
|
||||||
|
import com.jozufozu.flywheel.api.registry.Registry;
|
||||||
|
import com.jozufozu.flywheel.impl.RegistryImpl;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An InstanceType contains metadata for a specific instance that Flywheel can interface with.
|
||||||
|
*
|
||||||
|
* @param <I> The java representation of the instance.
|
||||||
|
*/
|
||||||
|
public interface InstanceType<I extends Instance> {
|
||||||
|
static Registry<InstanceType<?>> REGISTRY = RegistryImpl.create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param handle A handle that allows you to mark the instance as dirty or deleted.
|
||||||
|
* @return A new, zeroed instance of I.
|
||||||
|
*/
|
||||||
|
I create(InstanceHandle handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The layout of I when buffered.
|
||||||
|
*/
|
||||||
|
BufferLayout getLayout();
|
||||||
|
|
||||||
|
InstanceWriter<I> getWriter();
|
||||||
|
|
||||||
|
ResourceLocation instanceShader();
|
||||||
|
|
||||||
|
InstanceVertexTransformer<I> getVertexTransformer();
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||||
|
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
|
||||||
|
public interface InstanceVertexTransformer<I extends Instance> {
|
||||||
|
void transform(MutableVertexList vertexList, I instance, ClientLevel level);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* InstanceWriters can quickly consume many instances and write them to some memory address.
|
||||||
|
*/
|
||||||
|
public interface InstanceWriter<I extends Instance> {
|
||||||
|
/**
|
||||||
|
* Write the given instance to the given memory address.
|
||||||
|
*/
|
||||||
|
void write(final long ptr, final I instance);
|
||||||
|
}
|
|
@ -1,37 +1,35 @@
|
||||||
package com.jozufozu.flywheel.api.instancer;
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instancer is how you interact with an instanced model.
|
* An instancer is how you interact with an instanced model.
|
||||||
* <p>
|
* <p>
|
||||||
* Instanced models can have many copies, and on most systems it's very fast to draw all of the copies at once.
|
* Instanced models can have many copies, and on most systems it's very fast to draw all of the copies at once.
|
||||||
* There is no limit to how many copies an instanced model can have.
|
* There is no limit to how many copies an instanced model can have.
|
||||||
* Each copy is represented by an InstanceData object.
|
* Each copy is represented by an Instance object.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* When you call {@link #createInstance()} you are given an InstanceData object that you can manipulate however
|
* When you call {@link #createInstance()} you are given an Instance object that you can manipulate however
|
||||||
* you want. The changes you make to the InstanceData object are automatically made visible, and persistent.
|
* you want. The changes you make to the Instance object are automatically made visible, and persistent.
|
||||||
* Changing the position of your InstanceData object every frame means that that copy of the model will be in a
|
* Changing the position of your Instance object every frame means that that copy of the model will be in a
|
||||||
* different position in the world each frame. Setting the position of your InstanceData once and not touching it
|
* different position in the world each frame. Setting the position of your Instance once and not touching it
|
||||||
* again means that your model will be in the same position in the world every frame. This persistence is useful
|
* again means that your model will be in the same position in the world every frame. This persistence is useful
|
||||||
* because it means the properties of your model don't have to be re-evaluated every frame.
|
* because it means the properties of your model don't have to be re-evaluated every frame.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param <P> the data that represents a copy of the instanced model.
|
* @param <I> the data that represents a copy of the instanced model.
|
||||||
*/
|
*/
|
||||||
public interface Instancer<P extends InstancePart> {
|
public interface Instancer<I extends Instance> {
|
||||||
/**
|
/**
|
||||||
* @return a handle to a new copy of this model.
|
* @return a handle to a new copy of this model.
|
||||||
*/
|
*/
|
||||||
P createInstance();
|
I createInstance();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Populate arr with new instances of this model.
|
* Populate arr with new instances of this model.
|
||||||
*
|
*
|
||||||
* @param arr An array to fill.
|
* @param arr An array to fill.
|
||||||
*/
|
*/
|
||||||
default void createInstances(P[] arr) {
|
default void createInstances(I[] arr) {
|
||||||
for (int i = 0; i < arr.length; i++) {
|
for (int i = 0; i < arr.length; i++) {
|
||||||
arr[i] = createInstance();
|
arr[i] = createInstance();
|
||||||
}
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.jozufozu.flywheel.api.instance;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
|
|
||||||
|
public interface InstancerProvider {
|
||||||
|
/**
|
||||||
|
* Get an instancer for the given instance type, model, and render stage. Calling this method twice with the same arguments will return the same instancer.
|
||||||
|
*
|
||||||
|
* @return An instancer for the given instance type, model, and render stage.
|
||||||
|
*/
|
||||||
|
<I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage);
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance.controller;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.BlockEntityInstance;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An instancing controller that will be keyed to a block entity type.
|
|
||||||
* @param <T> The block entity type.
|
|
||||||
*/
|
|
||||||
public interface BlockEntityInstancingController<T extends BlockEntity> {
|
|
||||||
/**
|
|
||||||
* Given a block entity and context, constructs an instance for the block entity.
|
|
||||||
*
|
|
||||||
* @param ctx Context for creating an Instance.
|
|
||||||
* @param blockEntity The block entity to construct an instance for.
|
|
||||||
* @return The instance.
|
|
||||||
*/
|
|
||||||
BlockEntityInstance<? super T> createInstance(InstanceContext ctx, T blockEntity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given block entity should not be rendered normally.
|
|
||||||
* @param blockEntity The block entity to check.
|
|
||||||
* @return {@code true} if the block entity should not be rendered normally, {@code false} if it should.
|
|
||||||
*/
|
|
||||||
boolean shouldSkipRender(T blockEntity);
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance.controller;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.EntityInstance;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An instancing controller that will be keyed to an entity type.
|
|
||||||
* @param <T> The entity type.
|
|
||||||
*/
|
|
||||||
public interface EntityInstancingController<T extends Entity> {
|
|
||||||
/**
|
|
||||||
* Given an entity and context, constructs an instance for the entity.
|
|
||||||
*
|
|
||||||
* @param ctx Context for creating an Instance.
|
|
||||||
* @param entity The entity to construct an instance for.
|
|
||||||
* @return The instance.
|
|
||||||
*/
|
|
||||||
EntityInstance<? super T> createInstance(InstanceContext ctx, T entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the given entity should not render normally.
|
|
||||||
* @param entity The entity to check.
|
|
||||||
* @return {@code true} if the entity should not render normally, {@code false} if it should.
|
|
||||||
*/
|
|
||||||
boolean shouldSkipRender(T entity);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance.controller;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancerProvider;
|
|
||||||
|
|
||||||
import net.minecraft.core.Vec3i;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A context object passed on Instance creation.
|
|
||||||
*
|
|
||||||
* @param instancerProvider The {@link InstancerProvider} that the instance can use to get instancers to render models.
|
|
||||||
* @param renderOrigin The origin of the renderer as a world position.
|
|
||||||
* All models render as if this position is (0, 0, 0).
|
|
||||||
*/
|
|
||||||
public record InstanceContext(InstancerProvider instancerProvider, Vec3i renderOrigin) {
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance.controller;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.impl.InstancingControllerRegistryImpl;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A utility class for registering and retrieving {@code InstancingController}s.
|
|
||||||
*/
|
|
||||||
public final class InstancingControllerRegistry {
|
|
||||||
/**
|
|
||||||
* Gets the instancing controller for the given block entity type, if one exists.
|
|
||||||
* @param type The block entity type to get the instancing controller for.
|
|
||||||
* @param <T> The type of the block entity.
|
|
||||||
* @return The instancing controller for the given block entity type, or {@code null} if none exists.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static <T extends BlockEntity> BlockEntityInstancingController<? super T> getController(BlockEntityType<T> type) {
|
|
||||||
return InstancingControllerRegistryImpl.getController(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the instancing controller for the given entity type, if one exists.
|
|
||||||
* @param type The entity type to get the instancing controller for.
|
|
||||||
* @param <T> The type of the entity.
|
|
||||||
* @return The instancing controller for the given entity type, or {@code null} if none exists.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public static <T extends Entity> EntityInstancingController<? super T> getController(EntityType<T> type) {
|
|
||||||
return InstancingControllerRegistryImpl.getController(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the instancing controller for the given block entity type.
|
|
||||||
* @param type The block entity type to set the instancing controller for.
|
|
||||||
* @param instancingController The instancing controller to set.
|
|
||||||
* @param <T> The type of the block entity.
|
|
||||||
*/
|
|
||||||
public static <T extends BlockEntity> void setController(BlockEntityType<T> type, BlockEntityInstancingController<? super T> instancingController) {
|
|
||||||
InstancingControllerRegistryImpl.setController(type, instancingController);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the instancing controller for the given entity type.
|
|
||||||
* @param type The entity type to set the instancing controller for.
|
|
||||||
* @param instancingController The instancing controller to set.
|
|
||||||
* @param <T> The type of the entity.
|
|
||||||
*/
|
|
||||||
public static <T extends Entity> void setController(EntityType<T> type, EntityInstancingController<? super T> instancingController) {
|
|
||||||
InstancingControllerRegistryImpl.setController(type, instancingController);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstancingControllerRegistry() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instance.effect;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.EffectInstance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
|
||||||
|
|
||||||
// TODO: add level getter?
|
|
||||||
// TODO: return single instance instead of many?
|
|
||||||
public interface Effect {
|
|
||||||
Collection<EffectInstance<?>> createInstances(InstanceContext ctx);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.instancer;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
|
|
||||||
public interface InstancerProvider {
|
|
||||||
/**
|
|
||||||
* Get an instancer for the given struct type, model, and render stage. Calling this method twice with the same arguments will return the same instancer.
|
|
||||||
*
|
|
||||||
* @return An instancer for the given struct type, model, and render stage.
|
|
||||||
*/
|
|
||||||
<P extends InstancePart> Instancer<P> instancer(StructType<P> type, Model model, RenderStage stage);
|
|
||||||
}
|
|
|
@ -7,13 +7,13 @@ import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.gl.array.VertexAttribute;
|
import com.jozufozu.flywheel.gl.array.VertexAttribute;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classic Vertex Format struct with a clever name.
|
* Classic Vertex Format with a clever name.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* Used for vertices and instances. Describes the layout of a datatype in a buffer object.
|
* Used for vertices and instances. Describes the layout of a datatype in a buffer object.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @see com.jozufozu.flywheel.api.struct.StructType
|
* @see com.jozufozu.flywheel.api.instance.InstanceType
|
||||||
* @see VertexType
|
* @see VertexType
|
||||||
*/
|
*/
|
||||||
public class BufferLayout {
|
public class BufferLayout {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.jozufozu.flywheel.api.pipeline;
|
package com.jozufozu.flywheel.api.pipeline;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.gl.GLSLVersion;
|
import com.jozufozu.flywheel.gl.GLSLVersion;
|
||||||
import com.jozufozu.flywheel.glsl.ShaderSources;
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
|
@ -9,7 +9,6 @@ import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public interface Pipeline {
|
public interface Pipeline {
|
||||||
|
|
||||||
GLSLVersion glslVersion();
|
GLSLVersion glslVersion();
|
||||||
|
|
||||||
ResourceLocation vertexShader();
|
ResourceLocation vertexShader();
|
||||||
|
@ -17,12 +16,12 @@ public interface Pipeline {
|
||||||
ResourceLocation fragmentShader();
|
ResourceLocation fragmentShader();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the source component necessary to convert a packed {@link StructType} into its shader representation.
|
* Generate the source component necessary to convert a packed {@link InstanceType} into its shader representation.
|
||||||
*
|
*
|
||||||
* @return A source component defining functions that unpack a representation of the given struct type.
|
* @return A source component defining functions that unpack a representation of the given instance type.
|
||||||
*/
|
*/
|
||||||
SourceComponent assemble(InstanceAssemblerContext context);
|
SourceComponent assemble(InstanceAssemblerContext context);
|
||||||
|
|
||||||
record InstanceAssemblerContext(ShaderSources sources, VertexType vertexType, StructType<?> structType) {
|
record InstanceAssemblerContext(ShaderSources sources, VertexType vertexType, InstanceType<?> instanceType) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.struct;
|
|
||||||
|
|
||||||
public interface Handle {
|
|
||||||
void setChanged();
|
|
||||||
|
|
||||||
void setDeleted();
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.struct;
|
|
||||||
|
|
||||||
public interface InstancePart {
|
|
||||||
StructType<?> type();
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
InstancePart copy(Handle handle);
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.struct;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.layout.BufferLayout;
|
|
||||||
import com.jozufozu.flywheel.api.registry.Registry;
|
|
||||||
import com.jozufozu.flywheel.impl.RegistryImpl;
|
|
||||||
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A StructType contains metadata for a specific instance struct that Flywheel can interface with.
|
|
||||||
*
|
|
||||||
* @param <P> The java representation of the instance struct.
|
|
||||||
*/
|
|
||||||
public interface StructType<P extends InstancePart> {
|
|
||||||
static Registry<StructType<?>> REGISTRY = RegistryImpl.create();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param handle A handle that allows you to mark the instance as dirty or deleted.
|
|
||||||
* @return A new, zeroed instance of S.
|
|
||||||
*/
|
|
||||||
P create(Handle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The layout of S when buffered.
|
|
||||||
*/
|
|
||||||
BufferLayout getLayout();
|
|
||||||
|
|
||||||
StructWriter<P> getWriter();
|
|
||||||
|
|
||||||
ResourceLocation instanceShader();
|
|
||||||
|
|
||||||
StructVertexTransformer<P> getVertexTransformer();
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.struct;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
|
||||||
|
|
||||||
public interface StructVertexTransformer<P extends InstancePart> {
|
|
||||||
void transform(MutableVertexList vertexList, P struct, ClientLevel level);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
package com.jozufozu.flywheel.api.struct;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* StructWriters can quickly consume many instances and write them to some memory address.
|
|
||||||
*/
|
|
||||||
public interface StructWriter<P extends InstancePart> {
|
|
||||||
/**
|
|
||||||
* Write the given struct to the given memory address.
|
|
||||||
*/
|
|
||||||
void write(final long ptr, final P struct);
|
|
||||||
}
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
|
public interface BlockEntityVisual<T extends BlockEntity> extends Visual {
|
||||||
|
List<Instance> getCrumblingInstances();
|
||||||
|
}
|
|
@ -1,48 +1,48 @@
|
||||||
package com.jozufozu.flywheel.api.instance;
|
package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface giving {@link Instance}s a hook to have a function called at
|
* An interface giving {@link Visual}s a hook to have a function called at
|
||||||
* the start of a frame. By implementing {@link DynamicInstance}, an {@link Instance}
|
* the start of a frame. By implementing {@link DynamicVisual}, an {@link Visual}
|
||||||
* can animate its models in ways that could not be easily achieved by shader attribute
|
* can animate its models in ways that could not be easily achieved by shader attribute
|
||||||
* parameterization.
|
* parameterization.
|
||||||
*
|
*
|
||||||
* <br><br> If your goal is offloading work to shaders, but you're unsure exactly how you need
|
* <br><br> If your goal is offloading work to shaders, but you're unsure exactly how you need
|
||||||
* to parameterize the instances, you're encouraged to implement this for prototyping.
|
* to parameterize the instances, you're encouraged to implement this for prototyping.
|
||||||
*/
|
*/
|
||||||
public interface DynamicInstance extends Instance {
|
public interface DynamicVisual extends Visual {
|
||||||
/**
|
/**
|
||||||
* Called every frame, and after initialization.
|
* Called every frame, and after initialization.
|
||||||
* <br>
|
* <br>
|
||||||
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside this instance.
|
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside this visual.
|
||||||
* <br>
|
* <br>
|
||||||
* {@link Instancer}/{@link InstancePart} creation/acquisition is safe here.
|
* {@link Instancer}/{@link Instance} creation/acquisition is safe here.
|
||||||
*/
|
*/
|
||||||
void beginFrame();
|
void beginFrame();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* As a further optimization, dynamic instances that are far away are ticked less often.
|
* As a further optimization, dynamic visuals that are far away are updated less often.
|
||||||
* This behavior can be disabled by returning false.
|
* This behavior can be disabled by returning false.
|
||||||
*
|
*
|
||||||
* <br> You might want to opt out of this if you want your animations to remain smooth
|
* <br> You might want to opt out of this if you want your animations to remain smooth
|
||||||
* even when far away from the camera. It is recommended to keep this as is, however.
|
* even when far away from the camera. It is recommended to keep this as is, however.
|
||||||
*
|
*
|
||||||
* @return {@code true} if your instance should be slow ticked.
|
* @return {@code true} if your visual should be slow updated.
|
||||||
*/
|
*/
|
||||||
default boolean decreaseFramerateWithDistance() {
|
default boolean decreaseFramerateWithDistance() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check this instance against a frustum.<p>
|
* Check this visual against a frustum.<p>
|
||||||
* An implementor may choose to return a constant to skip the frustum check.
|
* An implementor may choose to return a constant to skip the frustum check.
|
||||||
*
|
*
|
||||||
* @param frustum A frustum intersection tester for the current frame.
|
* @param frustum A frustum intersection tester for the current frame.
|
||||||
* @return {@code true} if this instance should be considered for updates.
|
* @return {@code true} if this visual should be considered for updates.
|
||||||
*/
|
*/
|
||||||
boolean isVisible(FrustumIntersection frustum);
|
boolean isVisible(FrustumIntersection frustum);
|
||||||
}
|
}
|
11
src/main/java/com/jozufozu/flywheel/api/visual/Effect.java
Normal file
11
src/main/java/com/jozufozu/flywheel/api/visual/Effect.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
|
|
||||||
|
// TODO: add level getter?
|
||||||
|
// TODO: return single visual instead of many?
|
||||||
|
public interface Effect {
|
||||||
|
Collection<EffectVisual<?>> createVisuals(VisualizationContext ctx);
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
|
public interface EffectVisual<T extends Effect> extends Visual {
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
|
||||||
|
public interface EntityVisual<T extends Entity> extends Visual {
|
||||||
|
}
|
|
@ -1,17 +1,17 @@
|
||||||
package com.jozufozu.flywheel.api.instance;
|
package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface giving {@link Instance}s a hook to have a function called at
|
* An interface giving {@link Visual}s a hook to have a function called at
|
||||||
* the end of every tick. By implementing {@link TickableInstance}, an {@link Instance}
|
* the end of every tick. By implementing {@link TickableVisual}, an {@link Visual}
|
||||||
* can update frequently, but not every frame.
|
* can update frequently, but not every frame.
|
||||||
* <br> There are a few cases in which this should be considered over {@link DynamicInstance}:
|
* <br> There are a few cases in which this should be considered over {@link DynamicVisual}:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>
|
* <li>
|
||||||
* You'd like to change something about the instance every now and then.
|
* You'd like to change something about the visual every now and then.
|
||||||
* eg. adding or removing parts, snapping to a different rotation, etc.
|
* eg. adding or removing instances, snapping to a different rotation, etc.
|
||||||
* </li>
|
* </li>
|
||||||
* <li>
|
* <li>
|
||||||
* Your BlockEntity does animate, but the animation doesn't have
|
* Your BlockEntity does animate, but the animation doesn't have
|
||||||
|
@ -19,24 +19,23 @@ import com.jozufozu.flywheel.api.struct.InstancePart;
|
||||||
* </li>
|
* </li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public interface TickableInstance extends Instance {
|
public interface TickableVisual extends Visual {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called every tick, and after initialization.<p>
|
* Called every tick, and after initialization.<p>
|
||||||
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this instance
|
* <em>DISPATCHED IN PARALLEL</em>, don't attempt to mutate anything outside of this visual
|
||||||
* without proper synchronization.<p>
|
* without proper synchronization.<p>
|
||||||
* {@link Instancer}/{@link InstancePart} creation/acquisition is safe here.
|
* {@link Instancer}/{@link Instance} creation/acquisition is safe here.
|
||||||
*/
|
*/
|
||||||
void tick();
|
void tick();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* As a further optimization, tickable instances that are far away are ticked less often.
|
* As a further optimization, tickable visuals that are far away are ticked less often.
|
||||||
* This behavior can be disabled by returning false.
|
* This behavior can be disabled by returning false.
|
||||||
*
|
*
|
||||||
* <br> You might want to opt out of this if you want your animations to remain smooth
|
* <br> You might want to opt out of this if you want your animations to remain smooth
|
||||||
* even when far away from the camera. It is recommended to keep this as is, however.
|
* even when far away from the camera. It is recommended to keep this as is, however.
|
||||||
*
|
*
|
||||||
* @return {@code true} if your instance should be slow ticked.
|
* @return {@code true} if your visual should be slow ticked.
|
||||||
*/
|
*/
|
||||||
default boolean decreaseTickRateWithDistance() {
|
default boolean decreaseTickRateWithDistance() {
|
||||||
return true;
|
return true;
|
47
src/main/java/com/jozufozu/flywheel/api/visual/Visual.java
Normal file
47
src/main/java/com/jozufozu/flywheel/api/visual/Visual.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package com.jozufozu.flywheel.api.visual;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A general interface providing information about any type of thing that could use Flywheel's visualized rendering.
|
||||||
|
*/
|
||||||
|
public interface Visual {
|
||||||
|
/**
|
||||||
|
* Initialize instances here.
|
||||||
|
*/
|
||||||
|
void init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update instances here. Good for when instances don't change very often and when animations are GPU based.
|
||||||
|
*
|
||||||
|
* <br><br> If your animations are complex or more CPU driven, see {@link DynamicVisual} or {@link TickableVisual}.
|
||||||
|
*/
|
||||||
|
void update();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a visual is reset, the visual is deleted and re-created.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Just before {@link #update()} would be called, {@code shouldReset()} is checked.
|
||||||
|
* If this function returns {@code true}, then this visual will be {@link #delete deleted},
|
||||||
|
* and another visual will be constructed to replace it. This allows for more sane resource
|
||||||
|
* acquisition compared to trying to update everything within the lifetime of a visual.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return {@code true} if this visual should be discarded and refreshed.
|
||||||
|
*/
|
||||||
|
boolean shouldReset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the distance squared between this visual and the given <em>world</em> position.
|
||||||
|
*
|
||||||
|
* @param x The x coordinate.
|
||||||
|
* @param y The y coordinate.
|
||||||
|
* @param z The z coordinate.
|
||||||
|
* @return The distance squared between this visual and the given position.
|
||||||
|
*/
|
||||||
|
double distanceSquared(double x, double y, double z);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free any acquired resources.
|
||||||
|
*/
|
||||||
|
void delete();
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visual.BlockEntityVisual;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A visualizer that will be keyed to a block entity type.
|
||||||
|
* @param <T> The block entity type.
|
||||||
|
*/
|
||||||
|
public interface BlockEntityVisualizer<T extends BlockEntity> {
|
||||||
|
/**
|
||||||
|
* Given a block entity and context, constructs a visual for the block entity.
|
||||||
|
*
|
||||||
|
* @param ctx Context for creating a visual.
|
||||||
|
* @param blockEntity The block entity to construct a visual for.
|
||||||
|
* @return The visual.
|
||||||
|
*/
|
||||||
|
BlockEntityVisual<? super T> createVisual(VisualizationContext ctx, T blockEntity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given block entity should not be rendered normally.
|
||||||
|
* @param blockEntity The block entity to check.
|
||||||
|
* @return {@code true} if the block entity should not be rendered normally, {@code false} if it should.
|
||||||
|
*/
|
||||||
|
boolean shouldSkipRender(T blockEntity);
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visual.EntityVisual;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A visualizer that will be keyed to an entity type.
|
||||||
|
* @param <T> The entity type.
|
||||||
|
*/
|
||||||
|
public interface EntityVisualizer<T extends Entity> {
|
||||||
|
/**
|
||||||
|
* Given an entity and context, constructs a visual for the entity.
|
||||||
|
*
|
||||||
|
* @param ctx Context for creating a visual.
|
||||||
|
* @param entity The entity to construct a visual for.
|
||||||
|
* @return The visual.
|
||||||
|
*/
|
||||||
|
EntityVisual<? super T> createVisual(VisualizationContext ctx, T entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given entity should not render normally.
|
||||||
|
* @param entity The entity to check.
|
||||||
|
* @return {@code true} if the entity should not render normally, {@code false} if it should.
|
||||||
|
*/
|
||||||
|
boolean shouldSkipRender(T entity);
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||||
|
|
||||||
|
import net.minecraft.core.Vec3i;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A context object passed on visual creation.
|
||||||
|
*
|
||||||
|
* @param instancerProvider The {@link InstancerProvider} that the visual can use to get instancers to render models.
|
||||||
|
* @param renderOrigin The origin of the renderer as a world position.
|
||||||
|
* All models render as if this position is (0, 0, 0).
|
||||||
|
*/
|
||||||
|
public record VisualizationContext(InstancerProvider instancerProvider, Vec3i renderOrigin) {
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.jozufozu.flywheel.api.visualization;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.impl.VisualizerRegistryImpl;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class for registering and retrieving {@code Visualizer}s.
|
||||||
|
*/
|
||||||
|
public final class VisualizerRegistry {
|
||||||
|
/**
|
||||||
|
* Gets the visualizer for the given block entity type, if one exists.
|
||||||
|
* @param type The block entity type to get the visualizer for.
|
||||||
|
* @param <T> The type of the block entity.
|
||||||
|
* @return The visualizer for the given block entity type, or {@code null} if none exists.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static <T extends BlockEntity> BlockEntityVisualizer<? super T> getVisualizer(BlockEntityType<T> type) {
|
||||||
|
return VisualizerRegistryImpl.getVisualizer(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the visualizer for the given entity type, if one exists.
|
||||||
|
* @param type The entity type to get the visualizer for.
|
||||||
|
* @param <T> The type of the entity.
|
||||||
|
* @return The visualizer for the given entity type, or {@code null} if none exists.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static <T extends Entity> EntityVisualizer<? super T> getVisualizer(EntityType<T> type) {
|
||||||
|
return VisualizerRegistryImpl.getVisualizer(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the visualizer for the given block entity type.
|
||||||
|
* @param type The block entity type to set the visualizer for.
|
||||||
|
* @param visualizer The visualizer to set.
|
||||||
|
* @param <T> The type of the block entity.
|
||||||
|
*/
|
||||||
|
public static <T extends BlockEntity> void setVisualizer(BlockEntityType<T> type, BlockEntityVisualizer<? super T> visualizer) {
|
||||||
|
VisualizerRegistryImpl.setVisualizer(type, visualizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the visualizer for the given entity type.
|
||||||
|
* @param type The entity type to set the visualizer for.
|
||||||
|
* @param visualizer The visualizer to set.
|
||||||
|
* @param <T> The type of the entity.
|
||||||
|
*/
|
||||||
|
public static <T extends Entity> void setVisualizer(EntityType<T> type, EntityVisualizer<? super T> visualizer) {
|
||||||
|
VisualizerRegistryImpl.setVisualizer(type, visualizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualizerRegistry() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
package com.jozufozu.flywheel.backend.compile;
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
|
||||||
public record CullingContext(StructType<?> structType) {
|
public record CullingContext(InstanceType<?> instanceType) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,13 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
|
||||||
public class CullingContextSet {
|
public class CullingContextSet {
|
||||||
static CullingContextSet create() {
|
static CullingContextSet create() {
|
||||||
var builder = new CullingContextSet();
|
var builder = new CullingContextSet();
|
||||||
for (StructType<?> structType : StructType.REGISTRY) {
|
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
|
||||||
builder.add(structType);
|
builder.add(instanceType);
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,8 @@ public class CullingContextSet {
|
||||||
return contexts.size();
|
return contexts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(StructType<?> structType) {
|
private void add(InstanceType<?> instanceType) {
|
||||||
var ctx = new CullingContext(structType);
|
var ctx = new CullingContext(instanceType);
|
||||||
|
|
||||||
contexts.add(ctx);
|
contexts.add(ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,8 @@ import java.util.stream.Collectors;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
import com.jozufozu.flywheel.api.uniform.ShaderUniforms;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.Pipelines;
|
import com.jozufozu.flywheel.backend.Pipelines;
|
||||||
|
@ -45,7 +45,7 @@ public class FlwCompiler {
|
||||||
|
|
||||||
final ShaderCompiler shaderCompiler;
|
final ShaderCompiler shaderCompiler;
|
||||||
final Map<PipelineContext, GlProgram> pipelinePrograms = new HashMap<>();
|
final Map<PipelineContext, GlProgram> pipelinePrograms = new HashMap<>();
|
||||||
final Map<StructType<?>, GlProgram> cullingPrograms = new HashMap<>();
|
final Map<InstanceType<?>, GlProgram> cullingPrograms = new HashMap<>();
|
||||||
final List<FailedCompilation> errors = new ArrayList<>();
|
final List<FailedCompilation> errors = new ArrayList<>();
|
||||||
|
|
||||||
public FlwCompiler(ShaderSources sources) {
|
public FlwCompiler(ShaderSources sources) {
|
||||||
|
@ -101,7 +101,7 @@ public class FlwCompiler {
|
||||||
|
|
||||||
private void finish() {
|
private void finish() {
|
||||||
long compileEnd = System.nanoTime();
|
long compileEnd = System.nanoTime();
|
||||||
int programCount = pipelineContexts.size() + StructType.REGISTRY.getAll().size();
|
int programCount = pipelineContexts.size() + InstanceType.REGISTRY.getAll().size();
|
||||||
int shaderCount = shaderCompiler.shaderCount();
|
int shaderCount = shaderCompiler.shaderCount();
|
||||||
int errorCount = errors.size();
|
int errorCount = errors.size();
|
||||||
var elapsed = StringUtil.formatTime(compileEnd - compileStart);
|
var elapsed = StringUtil.formatTime(compileEnd - compileStart);
|
||||||
|
@ -124,12 +124,12 @@ public class FlwCompiler {
|
||||||
shaderCompiler.delete();
|
shaderCompiler.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlProgram getPipelineProgram(VertexType vertexType, StructType<?> structType, Context contextShader, Pipeline pipelineShader) {
|
public GlProgram getPipelineProgram(VertexType vertexType, InstanceType<?> instanceType, Context contextShader, Pipeline pipelineShader) {
|
||||||
return pipelinePrograms.get(new PipelineContext(vertexType, structType, contextShader, pipelineShader));
|
return pipelinePrograms.get(new PipelineContext(vertexType, instanceType, contextShader, pipelineShader));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlProgram getCullingProgram(StructType<?> structType) {
|
public GlProgram getCullingProgram(InstanceType<?> instanceType) {
|
||||||
return cullingPrograms.get(structType);
|
return cullingPrograms.get(instanceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compilePipelineContext(PipelineContext ctx) {
|
private void compilePipelineContext(PipelineContext ctx) {
|
||||||
|
@ -150,14 +150,14 @@ public class FlwCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void compileComputeCuller(CullingContext ctx) {
|
private void compileComputeCuller(CullingContext ctx) {
|
||||||
var computeComponents = getComputeComponents(ctx.structType());
|
var computeComponents = getComputeComponents(ctx.instanceType());
|
||||||
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
|
var result = shaderCompiler.compile(GLSLVersion.V460, ShaderType.COMPUTE, computeComponents);
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cullingPrograms.put(ctx.structType(), link(result.handle()));
|
cullingPrograms.put(ctx.instanceType(), link(result.handle()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GlProgram link(int... shaders) {
|
private GlProgram link(int... shaders) {
|
||||||
|
@ -172,11 +172,11 @@ public class FlwCompiler {
|
||||||
|
|
||||||
private ImmutableList<SourceComponent> getVertexComponents(PipelineContext ctx) {
|
private ImmutableList<SourceComponent> getVertexComponents(PipelineContext ctx) {
|
||||||
var instanceAssembly = ctx.pipelineShader()
|
var instanceAssembly = ctx.pipelineShader()
|
||||||
.assemble(new Pipeline.InstanceAssemblerContext(sources, ctx.vertexType(), ctx.structType()));
|
.assemble(new Pipeline.InstanceAssemblerContext(sources, ctx.vertexType(), ctx.instanceType()));
|
||||||
|
|
||||||
var layout = sources.find(ctx.vertexType()
|
var layout = sources.find(ctx.vertexType()
|
||||||
.layoutShader());
|
.layoutShader());
|
||||||
var instance = sources.find(ctx.structType()
|
var instance = sources.find(ctx.instanceType()
|
||||||
.instanceShader());
|
.instanceShader());
|
||||||
var context = sources.find(ctx.contextShader()
|
var context = sources.find(ctx.contextShader()
|
||||||
.vertexShader());
|
.vertexShader());
|
||||||
|
@ -194,9 +194,9 @@ public class FlwCompiler {
|
||||||
return ImmutableList.of(uniformComponent, fragmentMaterialComponent, context, pipeline);
|
return ImmutableList.of(uniformComponent, fragmentMaterialComponent, context, pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableList<SourceComponent> getComputeComponents(StructType<?> structType) {
|
private ImmutableList<SourceComponent> getComputeComponents(InstanceType<?> instanceType) {
|
||||||
var instanceAssembly = new IndirectComponent(sources, structType);
|
var instanceAssembly = new IndirectComponent(sources, instanceType);
|
||||||
var instance = sources.find(structType.instanceShader());
|
var instance = sources.find(instanceType.instanceShader());
|
||||||
var pipeline = sources.find(Pipelines.Files.INDIRECT_CULL);
|
var pipeline = sources.find(Pipelines.Files.INDIRECT_CULL);
|
||||||
|
|
||||||
return ImmutableList.of(uniformComponent, instanceAssembly, instance, pipeline);
|
return ImmutableList.of(uniformComponent, instanceAssembly, instance, pipeline);
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
package com.jozufozu.flywheel.backend.compile;
|
package com.jozufozu.flywheel.backend.compile;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the entire context of a program's usage.
|
* Represents the entire context of a program's usage.
|
||||||
*
|
*
|
||||||
* @param vertexType The vertexType the program should be adapted for.
|
* @param vertexType The vertexType the program should be adapted for.
|
||||||
* @param structType The instance shader to use.
|
* @param instanceType The instance shader to use.
|
||||||
* @param contextShader The context shader to use.
|
* @param contextShader The context shader to use.
|
||||||
*/
|
*/
|
||||||
public record PipelineContext(VertexType vertexType, StructType<?> structType, Context contextShader,
|
public record PipelineContext(VertexType vertexType, InstanceType<?> instanceType, Context contextShader,
|
||||||
Pipeline pipelineShader) {
|
Pipeline pipelineShader) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Backend;
|
import com.jozufozu.flywheel.api.backend.Backend;
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
import com.jozufozu.flywheel.api.context.Context;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
|
|
||||||
|
@ -24,9 +24,9 @@ public class PipelineContextSet {
|
||||||
static PipelineContextSet create() {
|
static PipelineContextSet create() {
|
||||||
var builder = new PipelineContextSet();
|
var builder = new PipelineContextSet();
|
||||||
for (Pipeline pipelineShader : availablePipelineShaders()) {
|
for (Pipeline pipelineShader : availablePipelineShaders()) {
|
||||||
for (StructType<?> structType : StructType.REGISTRY) {
|
for (InstanceType<?> instanceType : InstanceType.REGISTRY) {
|
||||||
for (VertexType vertexType : VertexType.REGISTRY) {
|
for (VertexType vertexType : VertexType.REGISTRY) {
|
||||||
builder.add(vertexType, structType, Contexts.WORLD, pipelineShader);
|
builder.add(vertexType, instanceType, Contexts.WORLD, pipelineShader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,8 +50,8 @@ public class PipelineContextSet {
|
||||||
return contexts.size();
|
return contexts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(VertexType vertexType, StructType<?> structType, Context world, Pipeline pipelineShader) {
|
private void add(VertexType vertexType, InstanceType<?> instanceType, Context world, Pipeline pipelineShader) {
|
||||||
var ctx = new PipelineContext(vertexType, structType, world, pipelineShader);
|
var ctx = new PipelineContext(vertexType, instanceType, world, pipelineShader);
|
||||||
|
|
||||||
contexts.add(ctx);
|
contexts.add(ctx);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,45 +3,42 @@ package com.jozufozu.flywheel.backend.engine;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
|
|
||||||
public abstract class AbstractInstancer<P extends InstancePart> implements Instancer<P> {
|
public abstract class AbstractInstancer<I extends Instance> implements Instancer<I> {
|
||||||
public final StructType<P> type;
|
public final InstanceType<I> type;
|
||||||
|
|
||||||
// Lock for all instance data, only needs to be used in methods that may run on the TaskExecutor.
|
// Lock for all instances, only needs to be used in methods that may run on the TaskExecutor.
|
||||||
protected final Object lock = new Object();
|
protected final Object lock = new Object();
|
||||||
protected final ArrayList<P> data = new ArrayList<>();
|
protected final ArrayList<I> instances = new ArrayList<>();
|
||||||
protected final ArrayList<HandleImpl> handles = new ArrayList<>();
|
protected final ArrayList<InstanceHandleImpl> handles = new ArrayList<>();
|
||||||
|
|
||||||
// TODO: atomic bitset?
|
// TODO: atomic bitset?
|
||||||
protected final BitSet changed = new BitSet();
|
protected final BitSet changed = new BitSet();
|
||||||
protected final BitSet deleted = new BitSet();
|
protected final BitSet deleted = new BitSet();
|
||||||
|
|
||||||
protected AbstractInstancer(StructType<P> type) {
|
protected AbstractInstancer(InstanceType<I> type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a handle to a new copy of this model.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public P createInstance() {
|
public I createInstance() {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
var i = data.size();
|
var i = instances.size();
|
||||||
var handle = new HandleImpl(this, i);
|
var handle = new InstanceHandleImpl(this, i);
|
||||||
P instanceData = type.create(handle);
|
I instance = type.create(handle);
|
||||||
|
|
||||||
data.add(instanceData);
|
instances.add(instance);
|
||||||
handles.add(handle);
|
handles.add(handle);
|
||||||
changed.set(i);
|
changed.set(i);
|
||||||
return instanceData;
|
return instance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getInstanceCount() {
|
public int getInstanceCount() {
|
||||||
return data.size();
|
return instances.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyDirty(int index) {
|
public void notifyDirty(int index) {
|
||||||
|
@ -68,7 +65,7 @@ public abstract class AbstractInstancer<P extends InstancePart> implements Insta
|
||||||
}
|
}
|
||||||
|
|
||||||
// Figure out which elements are to be removed.
|
// Figure out which elements are to be removed.
|
||||||
final int oldSize = this.data.size();
|
final int oldSize = this.instances.size();
|
||||||
int removeCount = deleted.cardinality();
|
int removeCount = deleted.cardinality();
|
||||||
|
|
||||||
final int newSize = oldSize - removeCount;
|
final int newSize = oldSize - removeCount;
|
||||||
|
@ -79,10 +76,10 @@ public abstract class AbstractInstancer<P extends InstancePart> implements Insta
|
||||||
|
|
||||||
if (i != j) {
|
if (i != j) {
|
||||||
var handle = handles.get(i);
|
var handle = handles.get(i);
|
||||||
P element = data.get(i);
|
I instance = instances.get(i);
|
||||||
|
|
||||||
handles.set(j, handle);
|
handles.set(j, handle);
|
||||||
data.set(j, element);
|
instances.set(j, instance);
|
||||||
|
|
||||||
handle.setIndex(j);
|
handle.setIndex(j);
|
||||||
changed.set(j);
|
changed.set(j);
|
||||||
|
@ -90,18 +87,18 @@ public abstract class AbstractInstancer<P extends InstancePart> implements Insta
|
||||||
}
|
}
|
||||||
|
|
||||||
deleted.clear();
|
deleted.clear();
|
||||||
data.subList(newSize, oldSize)
|
instances.subList(newSize, oldSize)
|
||||||
.clear();
|
.clear();
|
||||||
handles.subList(newSize, oldSize)
|
handles.subList(newSize, oldSize)
|
||||||
.clear();
|
.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear all instance data without freeing resources.
|
* Clear all instances without freeing resources.
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
public void clear() {
|
||||||
handles.forEach(HandleImpl::clear);
|
handles.forEach(InstanceHandleImpl::clear);
|
||||||
data.clear();
|
instances.clear();
|
||||||
handles.clear();
|
handles.clear();
|
||||||
changed.clear();
|
changed.clear();
|
||||||
deleted.clear();
|
deleted.clear();
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package com.jozufozu.flywheel.backend.engine;
|
package com.jozufozu.flywheel.backend.engine;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.Handle;
|
import com.jozufozu.flywheel.api.instance.InstanceHandle;
|
||||||
|
|
||||||
public class HandleImpl implements Handle {
|
public class InstanceHandleImpl implements InstanceHandle {
|
||||||
private final AbstractInstancer<?> instancer;
|
private final AbstractInstancer<?> instancer;
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
public HandleImpl(AbstractInstancer<?> instancer, int index) {
|
public InstanceHandleImpl(AbstractInstancer<?> instancer, int index) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
package com.jozufozu.flywheel.backend.engine;
|
package com.jozufozu.flywheel.backend.engine;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
|
|
||||||
public record InstancerKey<P extends InstancePart>(StructType<P> type, Model model, RenderStage stage) {
|
public record InstancerKey<I extends Instance>(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ import java.util.List;
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
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.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
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.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
@ -24,7 +24,7 @@ public class BatchingEngine implements Engine {
|
||||||
private final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
|
private final BatchingDrawTracker drawTracker = new BatchingDrawTracker();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <P extends InstancePart> Instancer<P> instancer(StructType<P> type, Model model, RenderStage stage) {
|
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
return transformManager.getInstancer(type, model, stage);
|
return transformManager.getInstancer(type, model, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,11 @@ import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
|
@ -42,9 +42,9 @@ public class BatchingTransformManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <P extends InstancePart> Instancer<P> getInstancer(StructType<P> type, Model model, RenderStage stage) {
|
public <I extends Instance> Instancer<I> getInstancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
InstancerKey<P> key = new InstancerKey<>(type, model, stage);
|
InstancerKey<I> key = new InstancerKey<>(type, model, stage);
|
||||||
CPUInstancer<P> instancer = (CPUInstancer<P>) instancers.get(key);
|
CPUInstancer<I> instancer = (CPUInstancer<I>) instancers.get(key);
|
||||||
if (instancer == null) {
|
if (instancer == null) {
|
||||||
instancer = new CPUInstancer<>(type);
|
instancer = new CPUInstancer<>(type);
|
||||||
instancers.put(key, instancer);
|
instancers.put(key, instancer);
|
||||||
|
|
|
@ -2,21 +2,21 @@ package com.jozufozu.flywheel.backend.engine.batching;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||||
|
|
||||||
public class CPUInstancer<P extends InstancePart> extends AbstractInstancer<P> {
|
public class CPUInstancer<I extends Instance> extends AbstractInstancer<I> {
|
||||||
public CPUInstancer(StructType<P> type) {
|
public CPUInstancer(InstanceType<I> type) {
|
||||||
super(type);
|
super(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<P> getRange(int start, int end) {
|
public List<I> getRange(int start, int end) {
|
||||||
return data.subList(start, end);
|
return instances.subList(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<P> getAll() {
|
public List<I> getAll() {
|
||||||
return data;
|
return instances;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
|
|
|
@ -2,9 +2,9 @@ package com.jozufozu.flywheel.backend.engine.batching;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceVertexTransformer;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructVertexTransformer;
|
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
||||||
|
@ -15,15 +15,15 @@ import com.mojang.math.Matrix4f;
|
||||||
|
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
|
||||||
public class TransformCall<P extends InstancePart> {
|
public class TransformCall<I extends Instance> {
|
||||||
private final CPUInstancer<P> instancer;
|
private final CPUInstancer<I> instancer;
|
||||||
private final Material material;
|
private final Material material;
|
||||||
private final BatchedMeshPool.BufferedMesh mesh;
|
private final BatchedMeshPool.BufferedMesh mesh;
|
||||||
|
|
||||||
private final int meshVertexCount;
|
private final int meshVertexCount;
|
||||||
private final int meshByteSize;
|
private final int meshByteSize;
|
||||||
|
|
||||||
public TransformCall(CPUInstancer<P> instancer, Material material, BatchedMeshPool.BufferedMesh mesh) {
|
public TransformCall(CPUInstancer<I> instancer, Material material, BatchedMeshPool.BufferedMesh mesh) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
|
@ -64,18 +64,18 @@ public class TransformCall<P extends InstancePart> {
|
||||||
transformList(vertexList, instancer.getAll(), matrices, level);
|
transformList(vertexList, instancer.getAll(), matrices, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void transformList(ReusableVertexList vertexList, List<P> parts, PoseStack.Pose matrices, ClientLevel level) {
|
public void transformList(ReusableVertexList vertexList, List<I> instances, PoseStack.Pose matrices, ClientLevel level) {
|
||||||
long anchorPtr = vertexList.ptr();
|
long anchorPtr = vertexList.ptr();
|
||||||
int totalVertexCount = vertexList.vertexCount();
|
int totalVertexCount = vertexList.vertexCount();
|
||||||
|
|
||||||
vertexList.vertexCount(meshVertexCount);
|
vertexList.vertexCount(meshVertexCount);
|
||||||
|
|
||||||
StructVertexTransformer<P> structVertexTransformer = instancer.type.getVertexTransformer();
|
InstanceVertexTransformer<I> instanceVertexTransformer = instancer.type.getVertexTransformer();
|
||||||
|
|
||||||
for (P p : parts) {
|
for (I instance : instances) {
|
||||||
mesh.copyTo(vertexList.ptr());
|
mesh.copyTo(vertexList.ptr());
|
||||||
|
|
||||||
structVertexTransformer.transform(vertexList, p, level);
|
instanceVertexTransformer.transform(vertexList, instance, level);
|
||||||
|
|
||||||
vertexList.ptr(vertexList.ptr() + meshByteSize);
|
vertexList.ptr(vertexList.ptr() + meshByteSize);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,9 @@ import java.util.List;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.layout.LayoutItem;
|
import com.jozufozu.flywheel.api.layout.LayoutItem;
|
||||||
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
import com.jozufozu.flywheel.api.pipeline.Pipeline;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.backend.Pipelines;
|
import com.jozufozu.flywheel.backend.Pipelines;
|
||||||
import com.jozufozu.flywheel.glsl.ShaderSources;
|
import com.jozufozu.flywheel.glsl.ShaderSources;
|
||||||
import com.jozufozu.flywheel.glsl.SourceComponent;
|
import com.jozufozu.flywheel.glsl.SourceComponent;
|
||||||
|
@ -30,11 +30,11 @@ public class IndirectComponent implements SourceComponent {
|
||||||
private final ImmutableList<SourceFile> included;
|
private final ImmutableList<SourceFile> included;
|
||||||
|
|
||||||
public IndirectComponent(Pipeline.InstanceAssemblerContext ctx) {
|
public IndirectComponent(Pipeline.InstanceAssemblerContext ctx) {
|
||||||
this(ctx.sources(), ctx.structType());
|
this(ctx.sources(), ctx.instanceType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndirectComponent(ShaderSources sources, StructType<?> structType) {
|
public IndirectComponent(ShaderSources sources, InstanceType<?> instanceType) {
|
||||||
this.layoutItems = structType.getLayout().layoutItems;
|
this.layoutItems = instanceType.getLayout().layoutItems;
|
||||||
included = ImmutableList.of(sources.find(Pipelines.Files.UTIL_TYPES));
|
included = ImmutableList.of(sources.find(Pipelines.Files.UTIL_TYPES));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ import static org.lwjgl.opengl.GL45.glVertexArrayElementBuffer;
|
||||||
import static org.lwjgl.opengl.GL45.glVertexArrayVertexBuffer;
|
import static org.lwjgl.opengl.GL45.glVertexArrayVertexBuffer;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.Pipelines;
|
import com.jozufozu.flywheel.backend.Pipelines;
|
||||||
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
||||||
|
@ -22,14 +22,14 @@ import com.jozufozu.flywheel.gl.shader.GlProgram;
|
||||||
import com.jozufozu.flywheel.lib.context.Contexts;
|
import com.jozufozu.flywheel.lib.context.Contexts;
|
||||||
import com.jozufozu.flywheel.lib.util.QuadConverter;
|
import com.jozufozu.flywheel.lib.util.QuadConverter;
|
||||||
|
|
||||||
public class IndirectCullingGroup<P extends InstancePart> {
|
public class IndirectCullingGroup<I extends Instance> {
|
||||||
|
|
||||||
private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
|
private static final int BARRIER_BITS = GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT;
|
||||||
|
|
||||||
final GlProgram compute;
|
final GlProgram compute;
|
||||||
final GlProgram draw;
|
final GlProgram draw;
|
||||||
private final VertexType vertexType;
|
private final VertexType vertexType;
|
||||||
private final long objectStride;
|
private final long instanceStride;
|
||||||
|
|
||||||
final IndirectBuffers buffers;
|
final IndirectBuffers buffers;
|
||||||
|
|
||||||
|
@ -38,18 +38,18 @@ public class IndirectCullingGroup<P extends InstancePart> {
|
||||||
|
|
||||||
int vertexArray;
|
int vertexArray;
|
||||||
|
|
||||||
final IndirectDrawSet<P> drawSet = new IndirectDrawSet<>();
|
final IndirectDrawSet<I> drawSet = new IndirectDrawSet<>();
|
||||||
|
|
||||||
private boolean hasCulledThisFrame;
|
private boolean hasCulledThisFrame;
|
||||||
private boolean needsMemoryBarrier;
|
private boolean needsMemoryBarrier;
|
||||||
private int instanceCountThisFrame;
|
private int instanceCountThisFrame;
|
||||||
|
|
||||||
IndirectCullingGroup(StructType<P> structType, VertexType vertexType) {
|
IndirectCullingGroup(InstanceType<I> instanceType, VertexType vertexType) {
|
||||||
this.vertexType = vertexType;
|
this.vertexType = vertexType;
|
||||||
|
|
||||||
objectStride = structType.getLayout()
|
instanceStride = instanceType.getLayout()
|
||||||
.getStride();
|
.getStride();
|
||||||
buffers = new IndirectBuffers(objectStride);
|
buffers = new IndirectBuffers(instanceStride);
|
||||||
buffers.createBuffers();
|
buffers.createBuffers();
|
||||||
buffers.createObjectStorage(128);
|
buffers.createObjectStorage(128);
|
||||||
buffers.createDrawStorage(2);
|
buffers.createDrawStorage(2);
|
||||||
|
@ -62,8 +62,8 @@ public class IndirectCullingGroup<P extends InstancePart> {
|
||||||
.quads2Tris(2048).glBuffer;
|
.quads2Tris(2048).glBuffer;
|
||||||
setupVertexArray();
|
setupVertexArray();
|
||||||
|
|
||||||
compute = FlwCompiler.INSTANCE.getCullingProgram(structType);
|
compute = FlwCompiler.INSTANCE.getCullingProgram(instanceType);
|
||||||
draw = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, Contexts.WORLD, Pipelines.INDIRECT);
|
draw = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, instanceType, Contexts.WORLD, Pipelines.INDIRECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupVertexArray() {
|
private void setupVertexArray() {
|
||||||
|
@ -110,7 +110,7 @@ public class IndirectCullingGroup<P extends InstancePart> {
|
||||||
|
|
||||||
buffers.updateCounts(instanceCountThisFrame, drawSet.size());
|
buffers.updateCounts(instanceCountThisFrame, drawSet.size());
|
||||||
meshPool.flush();
|
meshPool.flush();
|
||||||
uploadInstanceData();
|
uploadInstances();
|
||||||
uploadIndirectCommands();
|
uploadIndirectCommands();
|
||||||
|
|
||||||
UniformBuffer.syncAndBind(compute);
|
UniformBuffer.syncAndBind(compute);
|
||||||
|
@ -143,7 +143,7 @@ public class IndirectCullingGroup<P extends InstancePart> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void uploadInstanceData() {
|
private void uploadInstances() {
|
||||||
long objectPtr = buffers.objectPtr;
|
long objectPtr = buffers.objectPtr;
|
||||||
long batchIDPtr = buffers.batchPtr;
|
long batchIDPtr = buffers.batchPtr;
|
||||||
|
|
||||||
|
@ -153,7 +153,7 @@ public class IndirectCullingGroup<P extends InstancePart> {
|
||||||
.getInstanceCount();
|
.getInstanceCount();
|
||||||
batch.writeObjects(objectPtr, batchIDPtr, i);
|
batch.writeObjects(objectPtr, batchIDPtr, i);
|
||||||
|
|
||||||
objectPtr += instanceCount * objectStride;
|
objectPtr += instanceCount * instanceStride;
|
||||||
batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE;
|
batchIDPtr += instanceCount * IndirectBuffers.INT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,12 @@ package com.jozufozu.flywheel.backend.engine.indirect;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||||
|
|
||||||
public class IndirectDraw<P extends InstancePart> {
|
public class IndirectDraw<I extends Instance> {
|
||||||
private final IndirectInstancer<P> instancer;
|
private final IndirectInstancer<I> instancer;
|
||||||
private final IndirectMeshPool.BufferedMesh mesh;
|
private final IndirectMeshPool.BufferedMesh mesh;
|
||||||
private final Material material;
|
private final Material material;
|
||||||
private final RenderStage stage;
|
private final RenderStage stage;
|
||||||
|
@ -19,7 +19,7 @@ public class IndirectDraw<P extends InstancePart> {
|
||||||
private int baseInstance = -1;
|
private int baseInstance = -1;
|
||||||
private boolean needsFullWrite = true;
|
private boolean needsFullWrite = true;
|
||||||
|
|
||||||
public IndirectDraw(IndirectInstancer<P> instancer, Material material, IndirectMeshPool.BufferedMesh mesh, RenderStage stage) {
|
public IndirectDraw(IndirectInstancer<I> instancer, Material material, IndirectMeshPool.BufferedMesh mesh, RenderStage stage) {
|
||||||
this.instancer = instancer;
|
this.instancer = instancer;
|
||||||
this.material = material;
|
this.material = material;
|
||||||
this.mesh = mesh;
|
this.mesh = mesh;
|
||||||
|
@ -29,7 +29,7 @@ public class IndirectDraw<P extends InstancePart> {
|
||||||
this.fragmentMaterialID = MaterialIndices.getFragmentShaderIndex(material);
|
this.fragmentMaterialID = MaterialIndices.getFragmentShaderIndex(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndirectInstancer<P> instancer() {
|
public IndirectInstancer<I> instancer() {
|
||||||
return instancer;
|
return instancer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
import com.jozufozu.flywheel.util.Pair;
|
import com.jozufozu.flywheel.util.Pair;
|
||||||
|
@ -18,12 +18,12 @@ public class IndirectDrawManager {
|
||||||
private final Map<InstancerKey<?>, IndirectInstancer<?>> instancers = new HashMap<>();
|
private final Map<InstancerKey<?>, IndirectInstancer<?>> instancers = new HashMap<>();
|
||||||
private final List<UninitializedInstancer> uninitializedInstancers = new ArrayList<>();
|
private final List<UninitializedInstancer> uninitializedInstancers = new ArrayList<>();
|
||||||
private final List<IndirectInstancer<?>> initializedInstancers = new ArrayList<>();
|
private final List<IndirectInstancer<?>> initializedInstancers = new ArrayList<>();
|
||||||
public final Map<Pair<StructType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>();
|
public final Map<Pair<InstanceType<?>, VertexType>, IndirectCullingGroup<?>> renderLists = new HashMap<>();
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <P extends InstancePart> Instancer<P> getInstancer(StructType<P> type, Model model, RenderStage stage) {
|
public <I extends Instance> Instancer<I> getInstancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
InstancerKey<P> key = new InstancerKey<>(type, model, stage);
|
InstancerKey<I> key = new InstancerKey<>(type, model, stage);
|
||||||
IndirectInstancer<P> instancer = (IndirectInstancer<P>) instancers.get(key);
|
IndirectInstancer<I> instancer = (IndirectInstancer<I>) instancers.get(key);
|
||||||
if (instancer == null) {
|
if (instancer == null) {
|
||||||
instancer = new IndirectInstancer<>(type);
|
instancer = new IndirectInstancer<>(type);
|
||||||
instancers.put(key, instancer);
|
instancers.put(key, instancer);
|
||||||
|
@ -58,13 +58,13 @@ public class IndirectDrawManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private <P extends InstancePart> void add(IndirectInstancer<P> instancer, Model model, RenderStage stage) {
|
private <I extends Instance> void add(IndirectInstancer<I> instancer, Model model, RenderStage stage) {
|
||||||
var meshes = model.getMeshes();
|
var meshes = model.getMeshes();
|
||||||
for (var entry : meshes.entrySet()) {
|
for (var entry : meshes.entrySet()) {
|
||||||
var material = entry.getKey();
|
var material = entry.getKey();
|
||||||
var mesh = entry.getValue();
|
var mesh = entry.getValue();
|
||||||
|
|
||||||
var indirectList = (IndirectCullingGroup<P>) renderLists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
|
var indirectList = (IndirectCullingGroup<I>) renderLists.computeIfAbsent(Pair.of(instancer.type, mesh.getVertexType()), p -> new IndirectCullingGroup<>(p.first(), p.second()));
|
||||||
|
|
||||||
indirectList.drawSet.add(instancer, material, stage, indirectList.meshPool.alloc(mesh));
|
indirectList.drawSet.add(instancer, material, stage, indirectList.meshPool.alloc(mesh));
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,13 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||||
|
|
||||||
public class IndirectDrawSet<P extends InstancePart> {
|
public class IndirectDrawSet<I extends Instance> {
|
||||||
|
|
||||||
final List<IndirectDraw<P>> indirectDraws = new ArrayList<>();
|
final List<IndirectDraw<I>> indirectDraws = new ArrayList<>();
|
||||||
|
|
||||||
final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
final Map<RenderStage, List<MultiDraw>> multiDraws = new EnumMap<>(RenderStage.class);
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ public class IndirectDrawSet<P extends InstancePart> {
|
||||||
return indirectDraws.size();
|
return indirectDraws.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(IndirectInstancer<P> instancer, Material material, RenderStage stage, IndirectMeshPool.BufferedMesh bufferedMesh) {
|
public void add(IndirectInstancer<I> instancer, Material material, RenderStage stage, IndirectMeshPool.BufferedMesh bufferedMesh) {
|
||||||
indirectDraws.add(new IndirectDraw<>(instancer, material, bufferedMesh, stage));
|
indirectDraws.add(new IndirectDraw<>(instancer, material, bufferedMesh, stage));
|
||||||
determineMultiDraws();
|
determineMultiDraws();
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public class IndirectDrawSet<P extends InstancePart> {
|
||||||
// TODO: Better material equality. Really we only need to bin by the results of the setup method.
|
// TODO: Better material equality. Really we only need to bin by the results of the setup method.
|
||||||
multiDraws.clear();
|
multiDraws.clear();
|
||||||
// sort by stage, then material
|
// sort by stage, then material
|
||||||
indirectDraws.sort(Comparator.comparing(IndirectDraw<P>::stage)
|
indirectDraws.sort(Comparator.comparing(IndirectDraw<I>::stage)
|
||||||
.thenComparing(draw -> MaterialIndices.getMaterialIndex(draw.material())));
|
.thenComparing(draw -> MaterialIndices.getMaterialIndex(draw.material())));
|
||||||
|
|
||||||
for (int start = 0, i = 0; i < indirectDraws.size(); i++) {
|
for (int start = 0, i = 0; i < indirectDraws.size(); i++) {
|
||||||
|
|
|
@ -7,10 +7,10 @@ import org.lwjgl.opengl.GL32;
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
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.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
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.gl.GlStateTracker;
|
import com.jozufozu.flywheel.gl.GlStateTracker;
|
||||||
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
import com.jozufozu.flywheel.gl.GlTextureUnit;
|
||||||
|
@ -34,7 +34,7 @@ public class IndirectEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <P extends InstancePart> Instancer<P> instancer(StructType<P> type, Model model, RenderStage stage) {
|
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
return drawManager.getInstancer(type, model, stage);
|
return drawManager.getInstancer(type, model, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,15 @@ package com.jozufozu.flywheel.backend.engine.indirect;
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.struct.StructWriter;
|
import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||||
|
|
||||||
public class IndirectInstancer<P extends InstancePart> extends AbstractInstancer<P> {
|
public class IndirectInstancer<I extends Instance> extends AbstractInstancer<I> {
|
||||||
private final long instanceStride;
|
private final long instanceStride;
|
||||||
|
|
||||||
public IndirectInstancer(StructType<P> type) {
|
public IndirectInstancer(InstanceType<I> type) {
|
||||||
super(type);
|
super(type);
|
||||||
this.instanceStride = type.getLayout()
|
this.instanceStride = type.getLayout()
|
||||||
.getStride();
|
.getStride();
|
||||||
|
@ -21,11 +21,11 @@ public class IndirectInstancer<P extends InstancePart> extends AbstractInstancer
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeSparse(long objectPtr, long batchIDPtr, int batchID) {
|
public void writeSparse(long objectPtr, long batchIDPtr, int batchID) {
|
||||||
int count = data.size();
|
int count = instances.size();
|
||||||
StructWriter<P> writer = type.getWriter();
|
InstanceWriter<I> writer = type.getWriter();
|
||||||
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
|
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
|
||||||
// write object
|
// write object
|
||||||
writer.write(objectPtr + instanceStride * i, data.get(i));
|
writer.write(objectPtr + instanceStride * i, instances.get(i));
|
||||||
|
|
||||||
// write batchID
|
// write batchID
|
||||||
MemoryUtil.memPutInt(batchIDPtr + IndirectBuffers.INT_SIZE * i, batchID);
|
MemoryUtil.memPutInt(batchIDPtr + IndirectBuffers.INT_SIZE * i, batchID);
|
||||||
|
@ -34,8 +34,8 @@ public class IndirectInstancer<P extends InstancePart> extends AbstractInstancer
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeFull(long objectPtr, long batchIDPtr, int batchID) {
|
public void writeFull(long objectPtr, long batchIDPtr, int batchID) {
|
||||||
StructWriter<P> writer = type.getWriter();
|
InstanceWriter<I> writer = type.getWriter();
|
||||||
for (var object : data) {
|
for (I object : instances) {
|
||||||
// write object
|
// write object
|
||||||
writer.write(objectPtr, object);
|
writer.write(objectPtr, object);
|
||||||
objectPtr += instanceStride;
|
objectPtr += instanceStride;
|
||||||
|
|
|
@ -4,10 +4,10 @@ import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
||||||
import com.jozufozu.flywheel.api.layout.BufferLayout;
|
import com.jozufozu.flywheel.api.layout.BufferLayout;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructWriter;
|
|
||||||
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
import com.jozufozu.flywheel.backend.engine.AbstractInstancer;
|
||||||
import com.jozufozu.flywheel.gl.array.GlVertexArray;
|
import com.jozufozu.flywheel.gl.array.GlVertexArray;
|
||||||
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
|
import com.jozufozu.flywheel.gl.buffer.GlBuffer;
|
||||||
|
@ -15,14 +15,14 @@ import com.jozufozu.flywheel.gl.buffer.GlBufferType;
|
||||||
import com.jozufozu.flywheel.gl.buffer.GlBufferUsage;
|
import com.jozufozu.flywheel.gl.buffer.GlBufferUsage;
|
||||||
import com.jozufozu.flywheel.gl.buffer.MappedBuffer;
|
import com.jozufozu.flywheel.gl.buffer.MappedBuffer;
|
||||||
|
|
||||||
public class GPUInstancer<P extends InstancePart> extends AbstractInstancer<P> {
|
public class GPUInstancer<I extends Instance> extends AbstractInstancer<I> {
|
||||||
private final BufferLayout instanceFormat;
|
private final BufferLayout instanceFormat;
|
||||||
private final int instanceStride;
|
private final int instanceStride;
|
||||||
|
|
||||||
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
private final Set<GlVertexArray> boundTo = new HashSet<>();
|
||||||
private GlBuffer vbo;
|
private GlBuffer vbo;
|
||||||
|
|
||||||
public GPUInstancer(StructType<P> type) {
|
public GPUInstancer(InstanceType<I> type) {
|
||||||
super(type);
|
super(type);
|
||||||
instanceFormat = type.getLayout();
|
instanceFormat = type.getLayout();
|
||||||
instanceStride = instanceFormat.getStride();
|
instanceStride = instanceFormat.getStride();
|
||||||
|
@ -52,7 +52,7 @@ public class GPUInstancer<P extends InstancePart> extends AbstractInstancer<P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureBufferCapacity() {
|
private void ensureBufferCapacity() {
|
||||||
int count = data.size();
|
int count = instances.size();
|
||||||
int byteSize = instanceStride * count;
|
int byteSize = instanceStride * count;
|
||||||
if (vbo.ensureCapacity(byteSize)) {
|
if (vbo.ensureCapacity(byteSize)) {
|
||||||
// The vbo has moved, so we need to re-bind attributes
|
// The vbo has moved, so we need to re-bind attributes
|
||||||
|
@ -65,7 +65,7 @@ public class GPUInstancer<P extends InstancePart> extends AbstractInstancer<P> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = data.size();
|
int count = instances.size();
|
||||||
long clearStart = instanceStride * (long) count;
|
long clearStart = instanceStride * (long) count;
|
||||||
long clearLength = vbo.getSize() - clearStart;
|
long clearLength = vbo.getSize() - clearStart;
|
||||||
|
|
||||||
|
@ -73,10 +73,10 @@ public class GPUInstancer<P extends InstancePart> extends AbstractInstancer<P> {
|
||||||
buf.clear(clearStart, clearLength);
|
buf.clear(clearStart, clearLength);
|
||||||
|
|
||||||
long ptr = buf.getPtr();
|
long ptr = buf.getPtr();
|
||||||
StructWriter<P> writer = type.getWriter();
|
InstanceWriter<I> writer = type.getWriter();
|
||||||
|
|
||||||
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
|
for (int i = changed.nextSetBit(0); i >= 0 && i < count; i = changed.nextSetBit(i + 1)) {
|
||||||
writer.write(ptr + instanceStride * i, data.get(i));
|
writer.write(ptr + instanceStride * i, instances.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
changed.clear();
|
changed.clear();
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class InstancedArraysComponent implements SourceComponent {
|
||||||
private final int baseIndex;
|
private final int baseIndex;
|
||||||
|
|
||||||
public InstancedArraysComponent(Pipeline.InstanceAssemblerContext ctx) {
|
public InstancedArraysComponent(Pipeline.InstanceAssemblerContext ctx) {
|
||||||
this.layoutItems = ctx.structType()
|
this.layoutItems = ctx.instanceType()
|
||||||
.getLayout().layoutItems;
|
.getLayout().layoutItems;
|
||||||
this.baseIndex = ctx.vertexType()
|
this.baseIndex = ctx.vertexType()
|
||||||
.getLayout()
|
.getLayout()
|
||||||
|
|
|
@ -14,11 +14,11 @@ import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||||
import com.jozufozu.flywheel.api.instancer.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Mesh;
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
import com.jozufozu.flywheel.backend.engine.InstancerKey;
|
||||||
|
|
||||||
|
@ -34,9 +34,9 @@ public class InstancingDrawManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <P extends InstancePart> Instancer<P> getInstancer(StructType<P> type, Model model, RenderStage stage) {
|
public <I extends Instance> Instancer<I> getInstancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
InstancerKey<P> key = new InstancerKey<>(type, model, stage);
|
InstancerKey<I> key = new InstancerKey<>(type, model, stage);
|
||||||
GPUInstancer<P> instancer = (GPUInstancer<P>) instancers.get(key);
|
GPUInstancer<I> instancer = (GPUInstancer<I>) instancers.get(key);
|
||||||
if (instancer == null) {
|
if (instancer == null) {
|
||||||
instancer = new GPUInstancer<>(type);
|
instancer = new GPUInstancer<>(type);
|
||||||
instancers.put(key, instancer);
|
instancers.put(key, instancer);
|
||||||
|
|
|
@ -8,10 +8,10 @@ import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
import com.jozufozu.flywheel.api.context.Context;
|
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.Instancer;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
import com.jozufozu.flywheel.api.instance.Instancer;
|
||||||
import com.jozufozu.flywheel.api.model.Model;
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
|
||||||
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.Pipelines;
|
import com.jozufozu.flywheel.backend.Pipelines;
|
||||||
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
import com.jozufozu.flywheel.backend.compile.FlwCompiler;
|
||||||
|
@ -41,7 +41,7 @@ public class InstancingEngine implements Engine {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <P extends InstancePart> Instancer<P> instancer(StructType<P> type, Model model, RenderStage stage) {
|
public <I extends Instance> Instancer<I> instancer(InstanceType<I> type, Model model, RenderStage stage) {
|
||||||
return drawManager.getInstancer(type, model, stage);
|
return drawManager.getInstancer(type, model, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,9 +104,9 @@ public class InstancingEngine implements Engine {
|
||||||
private void setup(ShaderState desc) {
|
private void setup(ShaderState desc) {
|
||||||
var material = desc.material();
|
var material = desc.material();
|
||||||
var vertexType = desc.vertexType();
|
var vertexType = desc.vertexType();
|
||||||
var structType = desc.instanceType();
|
var instanceType = desc.instanceType();
|
||||||
|
|
||||||
var program = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, structType, context, Pipelines.INSTANCED_ARRAYS);
|
var program = FlwCompiler.INSTANCE.getPipelineProgram(vertexType, instanceType, context, Pipelines.INSTANCED_ARRAYS);
|
||||||
UniformBuffer.syncAndBind(program);
|
UniformBuffer.syncAndBind(program);
|
||||||
|
|
||||||
var uniformLocation = program.getUniformLocation("_flw_materialID_instancing");
|
var uniformLocation = program.getUniformLocation("_flw_materialID_instancing");
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package com.jozufozu.flywheel.backend.engine.instancing;
|
package com.jozufozu.flywheel.backend.engine.instancing;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
|
||||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||||
|
|
||||||
public record ShaderState(Material material, VertexType vertexType, StructType<?> instanceType) {
|
public record ShaderState(Material material, VertexType vertexType, InstanceType<?> instanceType) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ package com.jozufozu.flywheel.extension;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.controller.BlockEntityInstancingController;
|
import com.jozufozu.flywheel.api.visualization.BlockEntityVisualizer;
|
||||||
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
|
||||||
public interface BlockEntityTypeExtension<T extends BlockEntity> {
|
public interface BlockEntityTypeExtension<T extends BlockEntity> {
|
||||||
@Nullable
|
@Nullable
|
||||||
BlockEntityInstancingController<? super T> flywheel$getInstancingController();
|
BlockEntityVisualizer<? super T> flywheel$getVisualizer();
|
||||||
|
|
||||||
void flywheel$setInstancingController(@Nullable BlockEntityInstancingController<? super T> instancingController);
|
void flywheel$setVisualizer(@Nullable BlockEntityVisualizer<? super T> visualizer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,13 @@ package com.jozufozu.flywheel.extension;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.controller.EntityInstancingController;
|
import com.jozufozu.flywheel.api.visualization.EntityVisualizer;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
|
||||||
public interface EntityTypeExtension<T extends Entity> {
|
public interface EntityTypeExtension<T extends Entity> {
|
||||||
@Nullable
|
@Nullable
|
||||||
EntityInstancingController<? super T> flywheel$getInstancingController();
|
EntityVisualizer<? super T> flywheel$getVisualizer();
|
||||||
|
|
||||||
void flywheel$setInstancingController(@Nullable EntityInstancingController<? super T> instancingController);
|
void flywheel$setVisualizer(@Nullable EntityVisualizer<? super T> visualizer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package com.jozufozu.flywheel.gl.buffer;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
public class ElementBuffer {
|
public class ElementBuffer {
|
||||||
|
|
||||||
protected final int elementCount;
|
protected final int elementCount;
|
||||||
protected final VertexFormat.IndexType eboIndexType;
|
protected final VertexFormat.IndexType eboIndexType;
|
||||||
public final int glBuffer;
|
public final int glBuffer;
|
||||||
|
|
|
@ -112,5 +112,4 @@ public class ErrorReporter {
|
||||||
|
|
||||||
LOGGER.error(builder.toString());
|
LOGGER.error(builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.jozufozu.flywheel.handler;
|
package com.jozufozu.flywheel.handler;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
@ -14,8 +14,8 @@ public class EntityWorldHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlwUtil.canUseInstancing(level)) {
|
if (FlwUtil.canUseVisualization(level)) {
|
||||||
InstancedRenderDispatcher.getEntities(level)
|
VisualizedRenderDispatcher.getEntities(level)
|
||||||
.queueAdd(event.getEntity());
|
.queueAdd(event.getEntity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,8 @@ public class EntityWorldHandler {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlwUtil.canUseInstancing(level)) {
|
if (FlwUtil.canUseVisualization(level)) {
|
||||||
InstancedRenderDispatcher.getEntities(level)
|
VisualizedRenderDispatcher.getEntities(level)
|
||||||
.remove(event.getEntity());
|
.remove(event.getEntity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.jozufozu.flywheel.handler;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.Flywheel;
|
import com.jozufozu.flywheel.Flywheel;
|
||||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
||||||
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.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
@ -22,7 +22,7 @@ public class ForgeEvents {
|
||||||
debug.add("");
|
debug.add("");
|
||||||
debug.add("Flywheel: " + Flywheel.getVersion());
|
debug.add("Flywheel: " + Flywheel.getVersion());
|
||||||
|
|
||||||
InstancedRenderDispatcher.addDebugInfo(debug);
|
VisualizedRenderDispatcher.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()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import com.jozufozu.flywheel.api.backend.Backend;
|
||||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||||
import com.jozufozu.flywheel.backend.Backends;
|
import com.jozufozu.flywheel.backend.Backends;
|
||||||
import com.jozufozu.flywheel.config.FlwConfig;
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
import com.jozufozu.flywheel.impl.instancing.InstancedRenderDispatcher;
|
import com.jozufozu.flywheel.impl.visualization.VisualizedRenderDispatcher;
|
||||||
import com.jozufozu.flywheel.lib.backend.SimpleBackend;
|
import com.jozufozu.flywheel.lib.backend.SimpleBackend;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ public final class BackendManagerImpl {
|
||||||
backend = chooseBackend();
|
backend = chooseBackend();
|
||||||
|
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
InstancedRenderDispatcher.resetInstanceWorld(level);
|
VisualizedRenderDispatcher.resetVisualWorld(level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl;
|
|
||||||
|
|
||||||
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.extension.BlockEntityTypeExtension;
|
|
||||||
import com.jozufozu.flywheel.extension.EntityTypeExtension;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
//TODO: Add freezing
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public final class InstancingControllerRegistryImpl {
|
|
||||||
@Nullable
|
|
||||||
public static <T extends BlockEntity> BlockEntityInstancingController<? super T> getController(BlockEntityType<T> type) {
|
|
||||||
return ((BlockEntityTypeExtension<T>) type).flywheel$getInstancingController();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static <T extends Entity> EntityInstancingController<? super T> getController(EntityType<T> type) {
|
|
||||||
return ((EntityTypeExtension<T>) type).flywheel$getInstancingController();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends BlockEntity> void setController(BlockEntityType<T> type, BlockEntityInstancingController<? super T> instancingController) {
|
|
||||||
((BlockEntityTypeExtension<T>) type).flywheel$setInstancingController(instancingController);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends Entity> void setController(EntityType<T> type, EntityInstancingController<? super T> instancingController) {
|
|
||||||
((EntityTypeExtension<T>) type).flywheel$setInstancingController(instancingController);
|
|
||||||
}
|
|
||||||
|
|
||||||
private InstancingControllerRegistryImpl() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package com.jozufozu.flywheel.impl;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visualization.BlockEntityVisualizer;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.EntityVisualizer;
|
||||||
|
import com.jozufozu.flywheel.extension.BlockEntityTypeExtension;
|
||||||
|
import com.jozufozu.flywheel.extension.EntityTypeExtension;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
//TODO: Add freezing
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public final class VisualizerRegistryImpl {
|
||||||
|
@Nullable
|
||||||
|
public static <T extends BlockEntity> BlockEntityVisualizer<? super T> getVisualizer(BlockEntityType<T> type) {
|
||||||
|
return ((BlockEntityTypeExtension<T>) type).flywheel$getVisualizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static <T extends Entity> EntityVisualizer<? super T> getVisualizer(EntityType<T> type) {
|
||||||
|
return ((EntityTypeExtension<T>) type).flywheel$getVisualizer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends BlockEntity> void setVisualizer(BlockEntityType<T> type, BlockEntityVisualizer<? super T> visualizer) {
|
||||||
|
((BlockEntityTypeExtension<T>) type).flywheel$setVisualizer(visualizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Entity> void setVisualizer(EntityType<T> type, EntityVisualizer<? super T> visualizer) {
|
||||||
|
((EntityTypeExtension<T>) type).flywheel$setVisualizer(visualizer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualizerRegistryImpl() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,77 +0,0 @@
|
||||||
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,39 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean willAccept(Effect obj) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.manager;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
|
||||||
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
|
||||||
import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
|
|
||||||
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
|
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
|
|
||||||
public class EntityInstanceManager extends InstanceManager<Entity> {
|
|
||||||
private final EntityStorage storage;
|
|
||||||
|
|
||||||
public EntityInstanceManager(Engine engine) {
|
|
||||||
storage = new EntityStorage(engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Storage<Entity> getStorage() {
|
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class EntityStorage extends One2OneStorage<Entity> {
|
|
||||||
public EntityStorage(Engine engine) {
|
|
||||||
super(engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
protected Instance createRaw(Entity obj) {
|
|
||||||
var controller = InstancingControllerHelper.getController(obj);
|
|
||||||
if (controller == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean willAccept(Entity entity) {
|
|
||||||
if (!entity.isAlive()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!InstancingControllerHelper.canInstance(entity)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Level level = entity.level;
|
|
||||||
|
|
||||||
return FlwUtil.isFlywheelLevel(level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.manager;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import org.joml.FrustumIntersection;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
|
||||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
|
||||||
import com.jozufozu.flywheel.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;
|
|
||||||
import com.jozufozu.flywheel.impl.instancing.storage.Transaction;
|
|
||||||
|
|
||||||
public abstract class InstanceManager<T> {
|
|
||||||
private final Queue<Transaction<T>> queue = new ConcurrentLinkedQueue<>();
|
|
||||||
|
|
||||||
protected DistanceUpdateLimiter tickLimiter;
|
|
||||||
protected DistanceUpdateLimiter frameLimiter;
|
|
||||||
|
|
||||||
public InstanceManager() {
|
|
||||||
tickLimiter = createUpdateLimiter();
|
|
||||||
frameLimiter = createUpdateLimiter();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Storage<T> getStorage();
|
|
||||||
|
|
||||||
protected DistanceUpdateLimiter createUpdateLimiter() {
|
|
||||||
if (FlwConfig.get().limitUpdates()) {
|
|
||||||
return new BandedPrimeLimiter();
|
|
||||||
} else {
|
|
||||||
return new NonLimiter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the number of game objects that are currently being instanced.
|
|
||||||
*
|
|
||||||
* @return The object count.
|
|
||||||
*/
|
|
||||||
public int getInstanceCount() {
|
|
||||||
return getStorage().getAllInstances().size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(T obj) {
|
|
||||||
if (!getStorage().willAccept(obj)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStorage().add(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void queueAdd(T obj) {
|
|
||||||
if (!getStorage().willAccept(obj)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
queue.add(Transaction.add(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(T obj) {
|
|
||||||
getStorage().remove(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void queueRemove(T obj) {
|
|
||||||
queue.add(Transaction.remove(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the instance associated with an object.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* By default this is the only hook an {@link Instance} has to change its internal state. This is the lowest frequency
|
|
||||||
* update hook {@link Instance} gets. For more frequent updates, see {@link TickableInstance} and
|
|
||||||
* {@link DynamicInstance}.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param obj the object to update.
|
|
||||||
*/
|
|
||||||
public void update(T obj) {
|
|
||||||
if (!getStorage().willAccept(obj)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStorage().update(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void queueUpdate(T obj) {
|
|
||||||
if (!getStorage().willAccept(obj)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
queue.add(Transaction.update(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void recreateAll() {
|
|
||||||
getStorage().recreateAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void invalidate() {
|
|
||||||
getStorage().invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void processQueue() {
|
|
||||||
var storage = getStorage();
|
|
||||||
Transaction<T> transaction;
|
|
||||||
while ((transaction = queue.poll()) != null) {
|
|
||||||
transaction.apply(storage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ticks the InstanceManager.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* {@link TickableInstance}s get ticked.
|
|
||||||
* <br>
|
|
||||||
* Queued updates are processed.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) {
|
|
||||||
tickLimiter.tick();
|
|
||||||
processQueue();
|
|
||||||
|
|
||||||
var instances = getStorage().getTickableInstances();
|
|
||||||
distributeWork(executor, instances, instance -> tickInstance(instance, cameraX, cameraY, cameraZ));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void tickInstance(TickableInstance instance, double cameraX, double cameraY, double cameraZ) {
|
|
||||||
if (!instance.decreaseTickRateWithDistance() || tickLimiter.shouldUpdate(instance.distanceSquared(cameraX, cameraY, cameraZ))) {
|
|
||||||
instance.tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void beginFrame(TaskExecutor executor, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
|
||||||
frameLimiter.tick();
|
|
||||||
processQueue();
|
|
||||||
|
|
||||||
var instances = getStorage().getDynamicInstances();
|
|
||||||
distributeWork(executor, instances, instance -> updateInstance(instance, cameraX, cameraY, cameraZ, frustum));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateInstance(DynamicInstance instance, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
|
||||||
if (!instance.decreaseFramerateWithDistance() || frameLimiter.shouldUpdate(instance.distanceSquared(cameraX, cameraY, cameraZ))) {
|
|
||||||
if (instance.isVisible(frustum)) {
|
|
||||||
instance.beginFrame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <I> void distributeWork(TaskExecutor executor, List<I> instances, Consumer<I> action) {
|
|
||||||
final int size = instances.size();
|
|
||||||
final int threadCount = executor.getThreadCount();
|
|
||||||
|
|
||||||
if (threadCount == 1) {
|
|
||||||
executor.execute(() -> instances.forEach(action));
|
|
||||||
} else {
|
|
||||||
final int stride = Math.max(size / (threadCount * 2), 1);
|
|
||||||
for (int start = 0; start < size; start += stride) {
|
|
||||||
int end = Math.min(start + stride, size);
|
|
||||||
|
|
||||||
var sub = instances.subList(start, end);
|
|
||||||
executor.execute(() -> sub.forEach(action));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.storage;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
|
||||||
|
|
||||||
public abstract class AbstractStorage<T> implements Storage<T> {
|
|
||||||
protected final Engine engine;
|
|
||||||
protected final List<TickableInstance> tickableInstances = new ArrayList<>();
|
|
||||||
protected final List<DynamicInstance> dynamicInstances = new ArrayList<>();
|
|
||||||
|
|
||||||
protected AbstractStorage(Engine engine) {
|
|
||||||
this.engine = engine;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TickableInstance> getTickableInstances() {
|
|
||||||
return tickableInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<DynamicInstance> getDynamicInstances() {
|
|
||||||
return dynamicInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setup(Instance instance) {
|
|
||||||
instance.init();
|
|
||||||
|
|
||||||
if (instance instanceof TickableInstance tickable) {
|
|
||||||
tickableInstances.add(tickable);
|
|
||||||
tickable.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (instance instanceof DynamicInstance dynamic) {
|
|
||||||
dynamicInstances.add(dynamic);
|
|
||||||
dynamic.beginFrame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.storage;
|
|
||||||
|
|
||||||
public enum Action {
|
|
||||||
ADD,
|
|
||||||
REMOVE,
|
|
||||||
UPDATE,
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
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,101 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.storage;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
|
||||||
|
|
||||||
public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
|
||||||
private final Map<T, Instance> instances = new HashMap<>();
|
|
||||||
|
|
||||||
public One2OneStorage(Engine engine) {
|
|
||||||
super(engine);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Instance> getAllInstances() {
|
|
||||||
return instances.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(T obj) {
|
|
||||||
Instance instance = instances.get(obj);
|
|
||||||
|
|
||||||
if (instance == null) {
|
|
||||||
create(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void remove(T obj) {
|
|
||||||
Instance instance = instances.remove(obj);
|
|
||||||
|
|
||||||
if (instance == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tickableInstances.remove(instance);
|
|
||||||
dynamicInstances.remove(instance);
|
|
||||||
instance.delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(T obj) {
|
|
||||||
Instance instance = instances.get(obj);
|
|
||||||
|
|
||||||
if (instance == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// resetting instances is by default used to handle block state changes.
|
|
||||||
if (instance.shouldReset()) {
|
|
||||||
// delete and re-create the instance.
|
|
||||||
// resetting an instance supersedes updating it.
|
|
||||||
remove(obj);
|
|
||||||
create(obj);
|
|
||||||
} else {
|
|
||||||
instance.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void recreateAll() {
|
|
||||||
tickableInstances.clear();
|
|
||||||
dynamicInstances.clear();
|
|
||||||
instances.replaceAll((obj, instance) -> {
|
|
||||||
instance.delete();
|
|
||||||
|
|
||||||
Instance out = createRaw(obj);
|
|
||||||
|
|
||||||
if (out != null) {
|
|
||||||
setup(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidate() {
|
|
||||||
tickableInstances.clear();
|
|
||||||
dynamicInstances.clear();
|
|
||||||
instances.values().forEach(Instance::delete);
|
|
||||||
instances.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void create(T obj) {
|
|
||||||
Instance instance = createRaw(obj);
|
|
||||||
|
|
||||||
if (instance != null) {
|
|
||||||
setup(instance);
|
|
||||||
instances.put(obj, instance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected abstract Instance createRaw(T obj);
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.storage;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.instance.DynamicInstance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
|
||||||
|
|
||||||
public interface Storage<T> {
|
|
||||||
Collection<Instance> getAllInstances();
|
|
||||||
|
|
||||||
List<TickableInstance> getTickableInstances();
|
|
||||||
|
|
||||||
List<DynamicInstance> getDynamicInstances();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Is the given object currently capable of being added?
|
|
||||||
*
|
|
||||||
* @return true if the object is currently capable of being instanced.
|
|
||||||
*/
|
|
||||||
boolean willAccept(T obj);
|
|
||||||
|
|
||||||
void add(T obj);
|
|
||||||
|
|
||||||
void remove(T obj);
|
|
||||||
|
|
||||||
void update(T obj);
|
|
||||||
|
|
||||||
void recreateAll();
|
|
||||||
|
|
||||||
void invalidate();
|
|
||||||
}
|
|
|
@ -3,8 +3,8 @@ package com.jozufozu.flywheel.impl.vertex;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
import com.jozufozu.flywheel.api.vertex.ReusableVertexList;
|
||||||
import com.jozufozu.flywheel.lib.format.AbstractVertexList;
|
|
||||||
import com.jozufozu.flywheel.lib.math.RenderMath;
|
import com.jozufozu.flywheel.lib.math.RenderMath;
|
||||||
|
import com.jozufozu.flywheel.lib.vertex.AbstractVertexList;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing;
|
package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -8,17 +8,17 @@ import com.jozufozu.flywheel.api.backend.BackendManager;
|
||||||
import com.jozufozu.flywheel.api.backend.Engine;
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
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.instance.DynamicInstance;
|
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||||
import com.jozufozu.flywheel.api.instance.TickableInstance;
|
import com.jozufozu.flywheel.api.visual.Effect;
|
||||||
import com.jozufozu.flywheel.api.instance.effect.Effect;
|
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||||
import com.jozufozu.flywheel.backend.task.FlwTaskExecutor;
|
import com.jozufozu.flywheel.backend.task.FlwTaskExecutor;
|
||||||
import com.jozufozu.flywheel.backend.task.ParallelTaskExecutor;
|
import com.jozufozu.flywheel.backend.task.ParallelTaskExecutor;
|
||||||
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.impl.instancing.manager.BlockEntityInstanceManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager;
|
||||||
import com.jozufozu.flywheel.impl.instancing.manager.EffectInstanceManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
|
||||||
import com.jozufozu.flywheel.impl.instancing.manager.EntityInstanceManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager;
|
||||||
import com.jozufozu.flywheel.impl.instancing.manager.InstanceManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.VisualManager;
|
||||||
|
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
|
@ -28,43 +28,43 @@ import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
* A manager class for a single world where instancing is supported.
|
* A manager class for a single world where instancing is supported.
|
||||||
*/
|
*/
|
||||||
// AutoCloseable is implemented to prevent leaking this object from WorldAttached
|
// AutoCloseable is implemented to prevent leaking this object from WorldAttached
|
||||||
public class InstanceWorld implements AutoCloseable {
|
public class VisualWorld implements AutoCloseable {
|
||||||
private final Engine engine;
|
private final Engine engine;
|
||||||
private final ParallelTaskExecutor taskExecutor;
|
private final ParallelTaskExecutor taskExecutor;
|
||||||
|
|
||||||
private final InstanceManager<BlockEntity> blockEntities;
|
private final VisualManager<BlockEntity> blockEntities;
|
||||||
private final InstanceManager<Entity> entities;
|
private final VisualManager<Entity> entities;
|
||||||
private final InstanceManager<Effect> effects;
|
private final VisualManager<Effect> effects;
|
||||||
|
|
||||||
public InstanceWorld(LevelAccessor level) {
|
public VisualWorld(LevelAccessor level) {
|
||||||
engine = BackendManager.getBackend().createEngine(level);
|
engine = BackendManager.getBackend().createEngine(level);
|
||||||
taskExecutor = FlwTaskExecutor.get();
|
taskExecutor = FlwTaskExecutor.get();
|
||||||
|
|
||||||
blockEntities = new BlockEntityInstanceManager(engine);
|
blockEntities = new BlockEntityVisualManager(engine);
|
||||||
entities = new EntityInstanceManager(engine);
|
entities = new EntityVisualManager(engine);
|
||||||
effects = new EffectInstanceManager(engine);
|
effects = new EffectVisualManager(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Engine getEngine() {
|
public Engine getEngine() {
|
||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceManager<BlockEntity> getBlockEntities() {
|
public VisualManager<BlockEntity> getBlockEntities() {
|
||||||
return blockEntities;
|
return blockEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceManager<Entity> getEntities() {
|
public VisualManager<Entity> getEntities() {
|
||||||
return entities;
|
return entities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstanceManager<Effect> getEffects() {
|
public VisualManager<Effect> getEffects() {
|
||||||
return effects;
|
return effects;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tick the instances after the game has ticked:
|
* Tick the visuals after the game has ticked:
|
||||||
* <p>
|
* <p>
|
||||||
* Call {@link TickableInstance#tick()} on all instances in this world.
|
* Call {@link TickableVisual#tick()} on all visuals in this world.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void tick(double cameraX, double cameraY, double cameraZ) {
|
public void tick(double cameraX, double cameraY, double cameraZ) {
|
||||||
|
@ -78,7 +78,7 @@ public class InstanceWorld implements AutoCloseable {
|
||||||
* <p>
|
* <p>
|
||||||
* Check and update the render origin.
|
* Check and update the render origin.
|
||||||
* <br>
|
* <br>
|
||||||
* Call {@link DynamicInstance#beginFrame()} on all instances in this world.
|
* Call {@link DynamicVisual#beginFrame()} on all visuals in this world.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void beginFrame(RenderContext context) {
|
public void beginFrame(RenderContext context) {
|
||||||
|
@ -109,7 +109,7 @@ public class InstanceWorld implements AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draw all instances for the given stage.
|
* Draw all visuals for the given stage.
|
||||||
*/
|
*/
|
||||||
public void renderStage(RenderContext context, RenderStage stage) {
|
public void renderStage(RenderContext context, RenderStage stage) {
|
||||||
taskExecutor.syncPoint();
|
taskExecutor.syncPoint();
|
||||||
|
@ -117,15 +117,15 @@ public class InstanceWorld implements AutoCloseable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDebugInfo(List<String> info) {
|
public void addDebugInfo(List<String> info) {
|
||||||
info.add("B: " + blockEntities.getInstanceCount()
|
info.add("B: " + blockEntities.getVisualCount()
|
||||||
+ ", E: " + entities.getInstanceCount()
|
+ ", E: " + entities.getVisualCount()
|
||||||
+ ", F: " + effects.getInstanceCount());
|
+ ", F: " + effects.getVisualCount());
|
||||||
info.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
|
info.add("Update limiting: " + FlwCommands.boolToText(FlwConfig.get().limitUpdates()).getString());
|
||||||
engine.addDebugInfo(info);
|
engine.addDebugInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free all acquired resources and invalidate this instance world.
|
* Free all acquired resources and invalidate this visual world.
|
||||||
*/
|
*/
|
||||||
public void delete() {
|
public void delete() {
|
||||||
taskExecutor.discardAndAwait();
|
taskExecutor.discardAndAwait();
|
|
@ -0,0 +1,77 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visualization.BlockEntityVisualizer;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.EntityVisualizer;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizerRegistry;
|
||||||
|
|
||||||
|
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 VisualizationHelper {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nullable
|
||||||
|
public static <T extends BlockEntity> BlockEntityVisualizer<? super T> getVisualizer(T blockEntity) {
|
||||||
|
return VisualizerRegistry.getVisualizer((BlockEntityType<? super T>) blockEntity.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nullable
|
||||||
|
public static <T extends Entity> EntityVisualizer<? super T> getVisualizer(T entity) {
|
||||||
|
return VisualizerRegistry.getVisualizer((EntityType<? super T>) entity.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given block entity can be visualized.
|
||||||
|
* @param type The block entity to check.
|
||||||
|
* @param <T> The block entity.
|
||||||
|
* @return {@code true} if the block entity can be visualized.
|
||||||
|
*/
|
||||||
|
public static <T extends BlockEntity> boolean canVisualize(T blockEntity) {
|
||||||
|
return getVisualizer(blockEntity) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given entity can be visualized.
|
||||||
|
* @param type The entity to check.
|
||||||
|
* @param <T> The entity.
|
||||||
|
* @return {@code true} if the entity can be visualized.
|
||||||
|
*/
|
||||||
|
public static <T extends Entity> boolean canVisualize(T entity) {
|
||||||
|
return getVisualizer(entity) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given block entity is visualized 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 visualized and should not be rendered normally.
|
||||||
|
*/
|
||||||
|
public static <T extends BlockEntity> boolean shouldSkipRender(T blockEntity) {
|
||||||
|
BlockEntityVisualizer<? super T> visualizer = getVisualizer(blockEntity);
|
||||||
|
if (visualizer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return visualizer.shouldSkipRender(blockEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given entity is visualized 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 visualized and should not be rendered normally.
|
||||||
|
*/
|
||||||
|
public static <T extends Entity> boolean shouldSkipRender(T entity) {
|
||||||
|
EntityVisualizer<? super T> visualizer = getVisualizer(entity);
|
||||||
|
if (visualizer == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return visualizer.shouldSkipRender(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private VisualizationHelper() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing;
|
package com.jozufozu.flywheel.impl.visualization;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
|
import com.jozufozu.flywheel.api.event.BeginFrameEvent;
|
||||||
import com.jozufozu.flywheel.api.event.RenderStageEvent;
|
import com.jozufozu.flywheel.api.event.RenderStageEvent;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.visual.Effect;
|
||||||
import com.jozufozu.flywheel.api.instance.effect.Effect;
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
||||||
import com.jozufozu.flywheel.impl.instancing.manager.InstanceManager;
|
import com.jozufozu.flywheel.impl.visualization.manager.VisualManager;
|
||||||
import com.jozufozu.flywheel.lib.util.AnimationTickHolder;
|
import com.jozufozu.flywheel.lib.util.AnimationTickHolder;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
import com.jozufozu.flywheel.util.WorldAttached;
|
import com.jozufozu.flywheel.util.WorldAttached;
|
||||||
|
@ -21,81 +21,81 @@ import net.minecraft.world.level.LevelAccessor;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraftforge.event.TickEvent;
|
import net.minecraftforge.event.TickEvent;
|
||||||
|
|
||||||
public class InstancedRenderDispatcher {
|
public class VisualizedRenderDispatcher {
|
||||||
private static final WorldAttached<InstanceWorld> INSTANCE_WORLDS = new WorldAttached<>(InstanceWorld::new);
|
private static final WorldAttached<VisualWorld> VISUAL_WORLDS = new WorldAttached<>(VisualWorld::new);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this when you want to run {@link Instance#update()}.
|
* Call this when you want to run {@link Visual#update()}.
|
||||||
* @param blockEntity The block entity whose instance you want to update.
|
* @param blockEntity The block entity whose visual you want to update.
|
||||||
*/
|
*/
|
||||||
public static void queueUpdate(BlockEntity blockEntity) {
|
public static void queueUpdate(BlockEntity blockEntity) {
|
||||||
if (!(blockEntity.getLevel() instanceof ClientLevel level)) {
|
if (!(blockEntity.getLevel() instanceof ClientLevel level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_WORLDS.get(level)
|
VISUAL_WORLDS.get(level)
|
||||||
.getBlockEntities()
|
.getBlockEntities()
|
||||||
.queueUpdate(blockEntity);
|
.queueUpdate(blockEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this when you want to run {@link Instance#update()}.
|
* Call this when you want to run {@link Visual#update()}.
|
||||||
* @param entity The entity whose instance you want to update.
|
* @param entity The entity whose visual you want to update.
|
||||||
*/
|
*/
|
||||||
public static void queueUpdate(Entity entity) {
|
public static void queueUpdate(Entity entity) {
|
||||||
Level level = entity.level;
|
Level level = entity.level;
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_WORLDS.get(level)
|
VISUAL_WORLDS.get(level)
|
||||||
.getEntities()
|
.getEntities()
|
||||||
.queueUpdate(entity);
|
.queueUpdate(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this when you want to run {@link Instance#update()}.
|
* Call this when you want to run {@link Visual#update()}.
|
||||||
* @param effect The effect whose instance you want to update.
|
* @param effect The effect whose visual you want to update.
|
||||||
*/
|
*/
|
||||||
public static void queueUpdate(LevelAccessor level, Effect effect) {
|
public static void queueUpdate(LevelAccessor level, Effect effect) {
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_WORLDS.get(level)
|
VISUAL_WORLDS.get(level)
|
||||||
.getEffects()
|
.getEffects()
|
||||||
.queueUpdate(effect);
|
.queueUpdate(effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get or create the {@link InstanceWorld} for the given world.
|
* Get or create the {@link VisualWorld} for the given world.
|
||||||
* @throws IllegalStateException if the backend is off
|
* @throws IllegalStateException if the backend is off
|
||||||
*/
|
*/
|
||||||
private static InstanceWorld getInstanceWorld(LevelAccessor level) {
|
private static VisualWorld getVisualWorld(LevelAccessor level) {
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
throw new IllegalStateException("Cannot retrieve instance world when backend is off!");
|
throw new IllegalStateException("Cannot retrieve visual world when backend is off!");
|
||||||
}
|
}
|
||||||
return INSTANCE_WORLDS.get(level);
|
return VISUAL_WORLDS.get(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstanceManager<BlockEntity> getBlockEntities(LevelAccessor level) {
|
public static VisualManager<BlockEntity> getBlockEntities(LevelAccessor level) {
|
||||||
return getInstanceWorld(level).getBlockEntities();
|
return getVisualWorld(level).getBlockEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstanceManager<Entity> getEntities(LevelAccessor level) {
|
public static VisualManager<Entity> getEntities(LevelAccessor level) {
|
||||||
return getInstanceWorld(level).getEntities();
|
return getVisualWorld(level).getEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InstanceManager<Effect> getEffects(LevelAccessor level) {
|
public static VisualManager<Effect> getEffects(LevelAccessor level) {
|
||||||
return getInstanceWorld(level).getEffects();
|
return getVisualWorld(level).getEffects();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Vec3i getRenderOrigin(LevelAccessor level) {
|
public static Vec3i getRenderOrigin(LevelAccessor level) {
|
||||||
return getInstanceWorld(level).getEngine().renderOrigin();
|
return getVisualWorld(level).getEngine().renderOrigin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void tick(TickEvent.ClientTickEvent event) {
|
public static void tick(TickEvent.ClientTickEvent event) {
|
||||||
|
@ -116,7 +116,7 @@ public class InstancedRenderDispatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
Level level = cameraEntity.level;
|
Level level = cameraEntity.level;
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ public class InstancedRenderDispatcher {
|
||||||
double cameraY = cameraEntity.getEyeY();
|
double cameraY = cameraEntity.getEyeY();
|
||||||
double cameraZ = cameraEntity.getZ();
|
double cameraZ = cameraEntity.getZ();
|
||||||
|
|
||||||
INSTANCE_WORLDS.get(level).tick(cameraX, cameraY, cameraZ);
|
VISUAL_WORLDS.get(level).tick(cameraX, cameraY, cameraZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void onBeginFrame(BeginFrameEvent event) {
|
public static void onBeginFrame(BeginFrameEvent event) {
|
||||||
|
@ -133,30 +133,30 @@ public class InstancedRenderDispatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientLevel level = event.getContext().level();
|
ClientLevel level = event.getContext().level();
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_WORLDS.get(level).beginFrame(event.getContext());
|
VISUAL_WORLDS.get(level).beginFrame(event.getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void onRenderStage(RenderStageEvent event) {
|
public static void onRenderStage(RenderStageEvent event) {
|
||||||
ClientLevel level = event.getContext().level();
|
ClientLevel level = event.getContext().level();
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANCE_WORLDS.get(level).renderStage(event.getContext(), event.getStage());
|
VISUAL_WORLDS.get(level).renderStage(event.getContext(), event.getStage());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void resetInstanceWorld(ClientLevel level) {
|
public static void resetVisualWorld(ClientLevel level) {
|
||||||
INSTANCE_WORLDS.remove(level, InstanceWorld::delete);
|
VISUAL_WORLDS.remove(level, VisualWorld::delete);
|
||||||
|
|
||||||
if (!FlwUtil.canUseInstancing(level)) {
|
if (!FlwUtil.canUseVisualization(level)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InstanceWorld world = INSTANCE_WORLDS.get(level);
|
VisualWorld world = VISUAL_WORLDS.get(level);
|
||||||
// Block entities are loaded while chunks are baked.
|
// 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.
|
// Entities are loaded with the level, so when chunks are reloaded they need to be re-added.
|
||||||
ClientLevelExtension.getAllLoadedEntities(level)
|
ClientLevelExtension.getAllLoadedEntities(level)
|
||||||
|
@ -165,8 +165,8 @@ public class InstancedRenderDispatcher {
|
||||||
|
|
||||||
public static void addDebugInfo(List<String> info) {
|
public static void addDebugInfo(List<String> info) {
|
||||||
ClientLevel level = Minecraft.getInstance().level;
|
ClientLevel level = Minecraft.getInstance().level;
|
||||||
if (FlwUtil.canUseInstancing(level)) {
|
if (FlwUtil.canUseVisualization(level)) {
|
||||||
INSTANCE_WORLDS.get(level).addDebugInfo(info);
|
VISUAL_WORLDS.get(level).addDebugInfo(info);
|
||||||
} else {
|
} else {
|
||||||
info.add("Disabled");
|
info.add("Disabled");
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.manager;
|
package com.jozufozu.flywheel.impl.visualization.manager;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
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.BlockEntityInstance;
|
import com.jozufozu.flywheel.api.visual.BlockEntityVisual;
|
||||||
import com.jozufozu.flywheel.api.instance.Instance;
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
import com.jozufozu.flywheel.api.instance.controller.InstanceContext;
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
import com.jozufozu.flywheel.impl.instancing.InstancingControllerHelper;
|
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
|
||||||
import com.jozufozu.flywheel.impl.instancing.storage.One2OneStorage;
|
import com.jozufozu.flywheel.impl.visualization.storage.One2OneStorage;
|
||||||
import com.jozufozu.flywheel.impl.instancing.storage.Storage;
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
import com.jozufozu.flywheel.util.FlwUtil;
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||||
|
@ -20,10 +20,10 @@ import net.minecraft.world.level.BlockGetter;
|
||||||
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;
|
||||||
|
|
||||||
public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
public class BlockEntityVisualManager extends VisualManager<BlockEntity> {
|
||||||
private final BlockEntityStorage storage;
|
private final BlockEntityStorage storage;
|
||||||
|
|
||||||
public BlockEntityInstanceManager(Engine engine) {
|
public BlockEntityVisualManager(Engine engine) {
|
||||||
storage = new BlockEntityStorage(engine);
|
storage = new BlockEntityStorage(engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,15 +32,15 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getCrumblingInstances(long pos, List<BlockEntityInstance<?>> data) {
|
public void getCrumblingVisuals(long pos, List<BlockEntityVisual<?>> visuals) {
|
||||||
BlockEntityInstance<?> instance = storage.posLookup.get(pos);
|
BlockEntityVisual<?> visual = storage.posLookup.get(pos);
|
||||||
if (instance != null) {
|
if (visual != null) {
|
||||||
data.add(instance);
|
visuals.add(visual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class BlockEntityStorage extends One2OneStorage<BlockEntity> {
|
private static class BlockEntityStorage extends One2OneStorage<BlockEntity> {
|
||||||
private final Long2ObjectMap<BlockEntityInstance<?>> posLookup = new Long2ObjectOpenHashMap<>();
|
private final Long2ObjectMap<BlockEntityVisual<?>> posLookup = new Long2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
public BlockEntityStorage(Engine engine) {
|
public BlockEntityStorage(Engine engine) {
|
||||||
super(engine);
|
super(engine);
|
||||||
|
@ -52,7 +52,7 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InstancingControllerHelper.canInstance(blockEntity)) {
|
if (!VisualizationHelper.canVisualize(blockEntity)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,18 +79,18 @@ public class BlockEntityInstanceManager extends InstanceManager<BlockEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
protected Instance createRaw(BlockEntity obj) {
|
protected Visual createRaw(BlockEntity obj) {
|
||||||
var controller = InstancingControllerHelper.getController(obj);
|
var visualizer = VisualizationHelper.getVisualizer(obj);
|
||||||
if (controller == null) {
|
if (visualizer == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var instance = controller.createInstance(new InstanceContext(engine, engine.renderOrigin()), obj);
|
var visual = visualizer.createVisual(new VisualizationContext(engine, engine.renderOrigin()), obj);
|
||||||
|
|
||||||
BlockPos blockPos = obj.getBlockPos();
|
BlockPos blockPos = obj.getBlockPos();
|
||||||
posLookup.put(blockPos.asLong(), instance);
|
posLookup.put(blockPos.asLong(), visual);
|
||||||
|
|
||||||
return instance;
|
return visual;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.manager;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Effect;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.storage.One2ManyStorage;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
|
|
||||||
|
public class EffectVisualManager extends VisualManager<Effect> {
|
||||||
|
private final EffectStorage storage;
|
||||||
|
|
||||||
|
public EffectVisualManager(Engine engine) {
|
||||||
|
storage = new EffectStorage(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Storage<Effect> getStorage() {
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EffectStorage extends One2ManyStorage<Effect> {
|
||||||
|
public EffectStorage(Engine engine) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Collection<? extends Visual> createRaw(Effect obj) {
|
||||||
|
return obj.createVisuals(new VisualizationContext(engine, engine.renderOrigin()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean willAccept(Effect obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.manager;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.VisualizationHelper;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.storage.One2OneStorage;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
|
import com.jozufozu.flywheel.util.FlwUtil;
|
||||||
|
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
public class EntityVisualManager extends VisualManager<Entity> {
|
||||||
|
private final EntityStorage storage;
|
||||||
|
|
||||||
|
public EntityVisualManager(Engine engine) {
|
||||||
|
storage = new EntityStorage(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Storage<Entity> getStorage() {
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EntityStorage extends One2OneStorage<Entity> {
|
||||||
|
public EntityStorage(Engine engine) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
protected Visual createRaw(Entity obj) {
|
||||||
|
var visualizer = VisualizationHelper.getVisualizer(obj);
|
||||||
|
if (visualizer == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return visualizer.createVisual(new VisualizationContext(engine, engine.renderOrigin()), obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean willAccept(Entity entity) {
|
||||||
|
if (!entity.isAlive()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VisualizationHelper.canVisualize(entity)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Level level = entity.level;
|
||||||
|
|
||||||
|
return FlwUtil.isFlywheelLevel(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.manager;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.joml.FrustumIntersection;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||||
|
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||||
|
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
|
import com.jozufozu.flywheel.config.FlwConfig;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.ratelimit.BandedPrimeLimiter;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.ratelimit.DistanceUpdateLimiter;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.ratelimit.NonLimiter;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.storage.Storage;
|
||||||
|
import com.jozufozu.flywheel.impl.visualization.storage.Transaction;
|
||||||
|
|
||||||
|
public abstract class VisualManager<T> {
|
||||||
|
private final Queue<Transaction<T>> queue = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
protected DistanceUpdateLimiter tickLimiter;
|
||||||
|
protected DistanceUpdateLimiter frameLimiter;
|
||||||
|
|
||||||
|
public VisualManager() {
|
||||||
|
tickLimiter = createUpdateLimiter();
|
||||||
|
frameLimiter = createUpdateLimiter();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Storage<T> getStorage();
|
||||||
|
|
||||||
|
protected DistanceUpdateLimiter createUpdateLimiter() {
|
||||||
|
if (FlwConfig.get().limitUpdates()) {
|
||||||
|
return new BandedPrimeLimiter();
|
||||||
|
} else {
|
||||||
|
return new NonLimiter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of game objects that are currently being visualized.
|
||||||
|
*
|
||||||
|
* @return The object count.
|
||||||
|
*/
|
||||||
|
public int getVisualCount() {
|
||||||
|
return getStorage().getAllVisuals().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(T obj) {
|
||||||
|
if (!getStorage().willAccept(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStorage().add(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueAdd(T obj) {
|
||||||
|
if (!getStorage().willAccept(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.add(Transaction.add(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(T obj) {
|
||||||
|
getStorage().remove(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueRemove(T obj) {
|
||||||
|
queue.add(Transaction.remove(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the visual associated with an object.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* By default this is the only hook a {@link Visual} has to change its internal state. This is the lowest frequency
|
||||||
|
* update hook {@link Visual} gets. For more frequent updates, see {@link TickableVisual} and
|
||||||
|
* {@link DynamicVisual}.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param obj the object whose visual will be updated.
|
||||||
|
*/
|
||||||
|
public void update(T obj) {
|
||||||
|
if (!getStorage().willAccept(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getStorage().update(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void queueUpdate(T obj) {
|
||||||
|
if (!getStorage().willAccept(obj)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.add(Transaction.update(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void recreateAll() {
|
||||||
|
getStorage().recreateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void invalidate() {
|
||||||
|
getStorage().invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void processQueue() {
|
||||||
|
var storage = getStorage();
|
||||||
|
Transaction<T> transaction;
|
||||||
|
while ((transaction = queue.poll()) != null) {
|
||||||
|
transaction.apply(storage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ticks the VisualManager.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@link TickableVisual}s get ticked.
|
||||||
|
* <br>
|
||||||
|
* Queued updates are processed.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public void tick(TaskExecutor executor, double cameraX, double cameraY, double cameraZ) {
|
||||||
|
tickLimiter.tick();
|
||||||
|
processQueue();
|
||||||
|
|
||||||
|
var visuals = getStorage().getTickableVisuals();
|
||||||
|
distributeWork(executor, visuals, visual -> tickVisual(visual, cameraX, cameraY, cameraZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void tickVisual(TickableVisual visual, double cameraX, double cameraY, double cameraZ) {
|
||||||
|
if (!visual.decreaseTickRateWithDistance() || tickLimiter.shouldUpdate(visual.distanceSquared(cameraX, cameraY, cameraZ))) {
|
||||||
|
visual.tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void beginFrame(TaskExecutor executor, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
||||||
|
frameLimiter.tick();
|
||||||
|
processQueue();
|
||||||
|
|
||||||
|
var visuals = getStorage().getDynamicVisuals();
|
||||||
|
distributeWork(executor, visuals, visual -> updateVisual(visual, cameraX, cameraY, cameraZ, frustum));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void updateVisual(DynamicVisual visual, double cameraX, double cameraY, double cameraZ, FrustumIntersection frustum) {
|
||||||
|
if (!visual.decreaseFramerateWithDistance() || frameLimiter.shouldUpdate(visual.distanceSquared(cameraX, cameraY, cameraZ))) {
|
||||||
|
if (visual.isVisible(frustum)) {
|
||||||
|
visual.beginFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <V> void distributeWork(TaskExecutor executor, List<V> visuals, Consumer<V> action) {
|
||||||
|
final int amount = visuals.size();
|
||||||
|
final int threadCount = executor.getThreadCount();
|
||||||
|
|
||||||
|
if (threadCount == 1) {
|
||||||
|
executor.execute(() -> visuals.forEach(action));
|
||||||
|
} else {
|
||||||
|
final int stride = Math.max(amount / (threadCount * 2), 1);
|
||||||
|
for (int start = 0; start < amount; start += stride) {
|
||||||
|
int end = Math.min(start + stride, amount);
|
||||||
|
|
||||||
|
var sub = visuals.subList(start, end);
|
||||||
|
executor.execute(() -> sub.forEach(action));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.ratelimit;
|
package com.jozufozu.flywheel.impl.visualization.ratelimit;
|
||||||
|
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.ratelimit;
|
package com.jozufozu.flywheel.impl.visualization.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.impl.instancing.ratelimit;
|
package com.jozufozu.flywheel.impl.visualization.ratelimit;
|
||||||
|
|
||||||
public class NonLimiter implements DistanceUpdateLimiter {
|
public class NonLimiter implements DistanceUpdateLimiter {
|
||||||
@Override
|
@Override
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.storage;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
|
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||||
|
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
|
|
||||||
|
public abstract class AbstractStorage<T> implements Storage<T> {
|
||||||
|
protected final Engine engine;
|
||||||
|
protected final List<TickableVisual> tickableVisuals = new ArrayList<>();
|
||||||
|
protected final List<DynamicVisual> dynamicVisuals = new ArrayList<>();
|
||||||
|
|
||||||
|
protected AbstractStorage(Engine engine) {
|
||||||
|
this.engine = engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TickableVisual> getTickableVisuals() {
|
||||||
|
return tickableVisuals;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DynamicVisual> getDynamicVisuals() {
|
||||||
|
return dynamicVisuals;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setup(Visual visual) {
|
||||||
|
visual.init();
|
||||||
|
|
||||||
|
if (visual instanceof TickableVisual tickable) {
|
||||||
|
tickableVisuals.add(tickable);
|
||||||
|
tickable.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visual instanceof DynamicVisual dynamic) {
|
||||||
|
dynamicVisuals.add(dynamic);
|
||||||
|
dynamic.beginFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.storage;
|
||||||
|
|
||||||
|
public enum Action {
|
||||||
|
ADD,
|
||||||
|
REMOVE,
|
||||||
|
UPDATE,
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.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.visual.Visual;
|
||||||
|
|
||||||
|
public abstract class One2ManyStorage<T> extends AbstractStorage<T> {
|
||||||
|
private final Multimap<T, Visual> allVisuals = HashMultimap.create();
|
||||||
|
|
||||||
|
public One2ManyStorage(Engine engine) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Visual> getAllVisuals() {
|
||||||
|
return allVisuals.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(T obj) {
|
||||||
|
Collection<Visual> visuals = allVisuals.get(obj);
|
||||||
|
|
||||||
|
if (visuals.isEmpty()) {
|
||||||
|
create(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(T obj) {
|
||||||
|
Collection<Visual> visuals = allVisuals.removeAll(obj);
|
||||||
|
|
||||||
|
if (visuals.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tickableVisuals.removeAll(visuals);
|
||||||
|
dynamicVisuals.removeAll(visuals);
|
||||||
|
visuals.forEach(Visual::delete);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(T obj) {
|
||||||
|
Collection<Visual> visuals = allVisuals.get(obj);
|
||||||
|
|
||||||
|
if (visuals.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: shouldReset cannot be checked here because all visuals are created at once
|
||||||
|
visuals.forEach(Visual::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recreateAll() {
|
||||||
|
tickableVisuals.clear();
|
||||||
|
dynamicVisuals.clear();
|
||||||
|
allVisuals.values().forEach(Visual::delete);
|
||||||
|
|
||||||
|
List<T> objects = List.copyOf(allVisuals.keySet());
|
||||||
|
allVisuals.clear();
|
||||||
|
objects.forEach(this::create);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
tickableVisuals.clear();
|
||||||
|
dynamicVisuals.clear();
|
||||||
|
allVisuals.values().forEach(Visual::delete);
|
||||||
|
allVisuals.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void create(T obj) {
|
||||||
|
Collection<? extends Visual> visuals = createRaw(obj);
|
||||||
|
|
||||||
|
if (!visuals.isEmpty()) {
|
||||||
|
visuals.forEach(this::setup);
|
||||||
|
allVisuals.putAll(obj, visuals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Collection<? extends Visual> createRaw(T obj);
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.storage;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.backend.Engine;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
|
|
||||||
|
public abstract class One2OneStorage<T> extends AbstractStorage<T> {
|
||||||
|
private final Map<T, Visual> visuals = new HashMap<>();
|
||||||
|
|
||||||
|
public One2OneStorage(Engine engine) {
|
||||||
|
super(engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Visual> getAllVisuals() {
|
||||||
|
return visuals.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(T obj) {
|
||||||
|
Visual visual = visuals.get(obj);
|
||||||
|
|
||||||
|
if (visual == null) {
|
||||||
|
create(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(T obj) {
|
||||||
|
Visual visual = visuals.remove(obj);
|
||||||
|
|
||||||
|
if (visual == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tickableVisuals.remove(visual);
|
||||||
|
dynamicVisuals.remove(visual);
|
||||||
|
visual.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(T obj) {
|
||||||
|
Visual visual = visuals.get(obj);
|
||||||
|
|
||||||
|
if (visual == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// resetting visuals is by default used to handle block state changes.
|
||||||
|
if (visual.shouldReset()) {
|
||||||
|
// delete and re-create the visual.
|
||||||
|
// resetting a visual supersedes updating it.
|
||||||
|
remove(obj);
|
||||||
|
create(obj);
|
||||||
|
} else {
|
||||||
|
visual.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void recreateAll() {
|
||||||
|
tickableVisuals.clear();
|
||||||
|
dynamicVisuals.clear();
|
||||||
|
visuals.replaceAll((obj, visual) -> {
|
||||||
|
visual.delete();
|
||||||
|
|
||||||
|
Visual out = createRaw(obj);
|
||||||
|
|
||||||
|
if (out != null) {
|
||||||
|
setup(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidate() {
|
||||||
|
tickableVisuals.clear();
|
||||||
|
dynamicVisuals.clear();
|
||||||
|
visuals.values().forEach(Visual::delete);
|
||||||
|
visuals.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void create(T obj) {
|
||||||
|
Visual visual = createRaw(obj);
|
||||||
|
|
||||||
|
if (visual != null) {
|
||||||
|
setup(visual);
|
||||||
|
visuals.put(obj, visual);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected abstract Visual createRaw(T obj);
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.jozufozu.flywheel.impl.visualization.storage;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||||
|
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||||
|
import com.jozufozu.flywheel.api.visual.Visual;
|
||||||
|
|
||||||
|
public interface Storage<T> {
|
||||||
|
Collection<Visual> getAllVisuals();
|
||||||
|
|
||||||
|
List<TickableVisual> getTickableVisuals();
|
||||||
|
|
||||||
|
List<DynamicVisual> getDynamicVisuals();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the given object currently capable of being added?
|
||||||
|
*
|
||||||
|
* @return true if the object is currently capable of being visualized.
|
||||||
|
*/
|
||||||
|
boolean willAccept(T obj);
|
||||||
|
|
||||||
|
void add(T obj);
|
||||||
|
|
||||||
|
void remove(T obj);
|
||||||
|
|
||||||
|
void update(T obj);
|
||||||
|
|
||||||
|
void recreateAll();
|
||||||
|
|
||||||
|
void invalidate();
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.jozufozu.flywheel.impl.instancing.storage;
|
package com.jozufozu.flywheel.impl.visualization.storage;
|
||||||
|
|
||||||
public record Transaction<T>(T obj, Action action) {
|
public record Transaction<T>(T obj, Action action) {
|
||||||
public static <T> Transaction<T> add(T obj) {
|
public static <T> Transaction<T> add(T obj) {
|
|
@ -1,93 +1,28 @@
|
||||||
package com.jozufozu.flywheel.lib.instance;
|
package com.jozufozu.flywheel.lib.instance;
|
||||||
|
|
||||||
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.InstanceHandle;
|
||||||
import com.jozufozu.flywheel.api.instancer.InstancerProvider;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
import com.jozufozu.flywheel.lib.light.LightListener;
|
|
||||||
import com.jozufozu.flywheel.lib.light.LightUpdater;
|
|
||||||
import com.jozufozu.flywheel.lib.struct.FlatLit;
|
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
public abstract class AbstractInstance implements Instance {
|
||||||
import net.minecraft.core.SectionPos;
|
protected final InstanceType<?> type;
|
||||||
import net.minecraft.core.Vec3i;
|
protected final InstanceHandle handle;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.LightLayer;
|
|
||||||
|
|
||||||
public abstract class AbstractInstance implements Instance, LightListener {
|
protected AbstractInstance(InstanceType<?> type, InstanceHandle handle) {
|
||||||
protected final InstancerProvider instancerProvider;
|
this.type = type;
|
||||||
protected final Vec3i renderOrigin;
|
this.handle = handle;
|
||||||
protected final Level level;
|
|
||||||
|
|
||||||
protected boolean deleted = false;
|
|
||||||
|
|
||||||
public AbstractInstance(InstanceContext ctx, Level level) {
|
|
||||||
this.instancerProvider = ctx.instancerProvider();
|
|
||||||
this.renderOrigin = ctx.renderOrigin();
|
|
||||||
this.level = level;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
public InstanceType<?> type() {
|
||||||
LightUpdater.get(level).addListener(this);
|
return type;
|
||||||
updateLight();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public final void setChanged() {
|
||||||
public void update() {
|
handle.setChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldReset() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called after construction and when a light update occurs in the world.
|
|
||||||
*
|
|
||||||
* <br> If your model needs it, update light here.
|
|
||||||
*/
|
|
||||||
public void updateLight() {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void _delete();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void delete() {
|
public final void delete() {
|
||||||
if (deleted) {
|
handle.setDeleted();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_delete();
|
|
||||||
deleted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLightUpdate(LightLayer type, SectionPos pos) {
|
|
||||||
updateLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInvalid() {
|
|
||||||
return deleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void relight(BlockPos pos, FlatLit<?>... parts) {
|
|
||||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void relight(int block, int sky, FlatLit<?>... parts) {
|
|
||||||
for (FlatLit<?> part : parts) {
|
|
||||||
part.setLight(block, sky);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <L extends FlatLit<?>> void relight(BlockPos pos, Stream<L> parts) {
|
|
||||||
relight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos), parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected <L extends FlatLit<?>> void relight(int block, int sky, Stream<L> parts) {
|
|
||||||
parts.forEach(model -> model.setLight(block, sky));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
package com.jozufozu.flywheel.lib.struct;
|
package com.jozufozu.flywheel.lib.instance;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.Handle;
|
import com.jozufozu.flywheel.api.instance.InstanceHandle;
|
||||||
import com.jozufozu.flywheel.api.struct.StructType;
|
import com.jozufozu.flywheel.api.instance.InstanceType;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.LightTexture;
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
|
||||||
public abstract class ColoredLitPart extends AbstractInstancePart implements FlatLit<ColoredLitPart> {
|
public abstract class ColoredLitInstance extends AbstractInstance implements FlatLit<ColoredLitInstance> {
|
||||||
public byte blockLight;
|
public byte blockLight;
|
||||||
public byte skyLight;
|
public byte skyLight;
|
||||||
|
|
||||||
|
@ -14,19 +14,19 @@ public abstract class ColoredLitPart extends AbstractInstancePart implements Fla
|
||||||
public byte b = (byte) 0xFF;
|
public byte b = (byte) 0xFF;
|
||||||
public byte a = (byte) 0xFF;
|
public byte a = (byte) 0xFF;
|
||||||
|
|
||||||
public ColoredLitPart(StructType<? extends ColoredLitPart> type, Handle handle) {
|
public ColoredLitInstance(InstanceType<? extends ColoredLitInstance> type, InstanceHandle handle) {
|
||||||
super(type, handle);
|
super(type, handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ColoredLitPart setBlockLight(int blockLight) {
|
public ColoredLitInstance setBlockLight(int blockLight) {
|
||||||
this.blockLight = (byte) blockLight;
|
this.blockLight = (byte) blockLight;
|
||||||
setChanged();
|
setChanged();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ColoredLitPart setSkyLight(int skyLight) {
|
public ColoredLitInstance setSkyLight(int skyLight) {
|
||||||
this.skyLight = (byte) skyLight;
|
this.skyLight = (byte) skyLight;
|
||||||
setChanged();
|
setChanged();
|
||||||
return this;
|
return this;
|
||||||
|
@ -37,11 +37,11 @@ public abstract class ColoredLitPart extends AbstractInstancePart implements Fla
|
||||||
return LightTexture.pack(blockLight, skyLight);
|
return LightTexture.pack(blockLight, skyLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColoredLitPart setColor(int color) {
|
public ColoredLitInstance setColor(int color) {
|
||||||
return setColor(color, false);
|
return setColor(color, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColoredLitPart setColor(int color, boolean alpha) {
|
public ColoredLitInstance setColor(int color, boolean alpha) {
|
||||||
byte r = (byte) ((color >> 16) & 0xFF);
|
byte r = (byte) ((color >> 16) & 0xFF);
|
||||||
byte g = (byte) ((color >> 8) & 0xFF);
|
byte g = (byte) ((color >> 8) & 0xFF);
|
||||||
byte b = (byte) (color & 0xFF);
|
byte b = (byte) (color & 0xFF);
|
||||||
|
@ -54,11 +54,11 @@ public abstract class ColoredLitPart extends AbstractInstancePart implements Fla
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColoredLitPart setColor(int r, int g, int b) {
|
public ColoredLitInstance setColor(int r, int g, int b) {
|
||||||
return setColor((byte) r, (byte) g, (byte) b);
|
return setColor((byte) r, (byte) g, (byte) b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColoredLitPart setColor(byte r, byte g, byte b) {
|
public ColoredLitInstance setColor(byte r, byte g, byte b) {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
|
@ -66,7 +66,7 @@ public abstract class ColoredLitPart extends AbstractInstancePart implements Fla
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColoredLitPart setColor(byte r, byte g, byte b, byte a) {
|
public ColoredLitInstance setColor(byte r, byte g, byte b, byte a) {
|
||||||
this.r = r;
|
this.r = r;
|
||||||
this.g = g;
|
this.g = g;
|
||||||
this.b = b;
|
this.b = b;
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.jozufozu.flywheel.lib.instance;
|
||||||
|
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstanceWriter;
|
||||||
|
|
||||||
|
public abstract class ColoredLitWriter<I extends ColoredLitInstance> implements InstanceWriter<I> {
|
||||||
|
@Override
|
||||||
|
public void write(final long ptr, final I instance) {
|
||||||
|
MemoryUtil.memPutShort(ptr, instance.blockLight);
|
||||||
|
MemoryUtil.memPutShort(ptr + 2, instance.skyLight);
|
||||||
|
MemoryUtil.memPutByte(ptr + 4, instance.r);
|
||||||
|
MemoryUtil.memPutByte(ptr + 5, instance.g);
|
||||||
|
MemoryUtil.memPutByte(ptr + 6, instance.b);
|
||||||
|
MemoryUtil.memPutByte(ptr + 7, instance.a);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,39 +1,39 @@
|
||||||
package com.jozufozu.flywheel.lib.struct;
|
package com.jozufozu.flywheel.lib.instance;
|
||||||
|
|
||||||
import com.jozufozu.flywheel.api.struct.InstancePart;
|
import com.jozufozu.flywheel.api.instance.Instance;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.world.level.BlockAndTintGetter;
|
import net.minecraft.world.level.BlockAndTintGetter;
|
||||||
import net.minecraft.world.level.LightLayer;
|
import net.minecraft.world.level.LightLayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface that implementors of {@link InstancePart} should also implement
|
* An interface that implementors of {@link Instance} should also implement
|
||||||
* if they wish to make use of Flywheel's provided light update methods.
|
* if they wish to make use of Flywheel's provided light update methods.
|
||||||
* <p>
|
* <p>
|
||||||
* This only covers flat lighting, smooth lighting is still TODO.
|
* This only covers flat lighting, smooth lighting is still TODO.
|
||||||
*
|
*
|
||||||
* @param <P> The name of the class that implements this interface.
|
* @param <I> The name of the class that implements this interface.
|
||||||
*/
|
*/
|
||||||
public interface FlatLit<P extends InstancePart & FlatLit<P>> {
|
public interface FlatLit<I extends Instance & FlatLit<I>> {
|
||||||
/**
|
/**
|
||||||
* @param blockLight An integer in the range [0, 15] representing the
|
* @param blockLight An integer in the range [0, 15] representing the
|
||||||
* amount of block light this instance should receive.
|
* amount of block light this instance should receive.
|
||||||
* @return {@code this}
|
* @return {@code this}
|
||||||
*/
|
*/
|
||||||
P setBlockLight(int blockLight);
|
I setBlockLight(int blockLight);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param skyLight An integer in the range [0, 15] representing the
|
* @param skyLight An integer in the range [0, 15] representing the
|
||||||
* amount of sky light this instance should receive.
|
* amount of sky light this instance should receive.
|
||||||
* @return {@code this}
|
* @return {@code this}
|
||||||
*/
|
*/
|
||||||
P setSkyLight(int skyLight);
|
I setSkyLight(int skyLight);
|
||||||
|
|
||||||
default P setLight(int blockLight, int skyLight) {
|
default I setLight(int blockLight, int skyLight) {
|
||||||
return setBlockLight(blockLight).setSkyLight(skyLight);
|
return setBlockLight(blockLight).setSkyLight(skyLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
default P updateLight(BlockAndTintGetter level, BlockPos pos) {
|
default I updateLight(BlockAndTintGetter level, BlockPos pos) {
|
||||||
return setLight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos));
|
return setLight(level.getBrightness(LightLayer.BLOCK, pos), level.getBrightness(LightLayer.SKY, pos));
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue