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:
PepperCode1 2023-04-09 12:00:03 -07:00
parent 6648751ef7
commit 3b62a4d721
151 changed files with 2056 additions and 2106 deletions

View file

@ -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.

View file

@ -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;

View file

@ -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();

View file

@ -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();
}

View file

@ -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 {
}

View file

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

View file

@ -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();
} }

View file

@ -0,0 +1,7 @@
package com.jozufozu.flywheel.api.instance;
public interface InstanceHandle {
void setChanged();
void setDeleted();
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();
} }

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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) {
}

View file

@ -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() {
}
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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 {

View file

@ -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) {
} }
} }

View file

@ -1,7 +0,0 @@
package com.jozufozu.flywheel.api.struct;
public interface Handle {
void setChanged();
void setDeleted();
}

View file

@ -1,8 +0,0 @@
package com.jozufozu.flywheel.api.struct;
public interface InstancePart {
StructType<?> type();
@Deprecated
InstancePart copy(Handle handle);
}

View file

@ -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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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);
} }

View 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);
}

View file

@ -0,0 +1,4 @@
package com.jozufozu.flywheel.api.visual;
public interface EffectVisual<T extends Effect> extends Visual {
}

View file

@ -0,0 +1,6 @@
package com.jozufozu.flywheel.api.visual;
import net.minecraft.world.entity.Entity;
public interface EntityVisual<T extends Entity> extends Visual {
}

View file

@ -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;

View 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();
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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) {
}

View file

@ -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() {
}
}

View file

@ -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) {
} }

View file

@ -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);
} }

View file

@ -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);

View file

@ -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) {
} }

View file

@ -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);
} }

View file

@ -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();

View file

@ -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;
} }

View file

@ -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) {
} }

View file

@ -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);
} }

View file

@ -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);

View file

@ -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() {

View file

@ -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);
} }

View file

@ -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));
} }

View file

@ -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;
} }

View file

@ -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;
} }

View file

@ -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));

View file

@ -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++) {

View file

@ -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);
} }

View file

@ -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;

View file

@ -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();

View file

@ -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()

View file

@ -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);

View file

@ -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");

View file

@ -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) {
} }

View file

@ -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);
} }

View file

@ -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);
} }

View file

@ -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;

View file

@ -112,5 +112,4 @@ public class ErrorReporter {
LOGGER.error(builder.toString()); LOGGER.error(builder.toString());
} }
} }

View file

@ -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());
} }
} }

View file

@ -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()));
} }

View file

@ -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);
} }
} }

View file

@ -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() {
}
}

View file

@ -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() {
}
}

View file

@ -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() {
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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));
}
}
}
}

View file

@ -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();
}
}
}

View file

@ -1,7 +0,0 @@
package com.jozufozu.flywheel.impl.instancing.storage;
public enum Action {
ADD,
REMOVE,
UPDATE,
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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;

View file

@ -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();

View file

@ -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() {
}
}

View file

@ -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");
} }

View file

@ -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

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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));
}
}
}
}

View file

@ -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;

View file

@ -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.

View file

@ -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

View file

@ -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();
}
}
}

View file

@ -0,0 +1,7 @@
package com.jozufozu.flywheel.impl.visualization.storage;
public enum Action {
ADD,
REMOVE,
UPDATE,
}

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();
}

View file

@ -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) {

View file

@ -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));
} }
} }

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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