diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java
new file mode 100644
index 000000000..8fbfdc2ef
--- /dev/null
+++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/AbstractInstance.java
@@ -0,0 +1,97 @@
+package com.jozufozu.flywheel.backend.instancing;
+
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import com.jozufozu.flywheel.backend.instancing.tile.TileInstanceManager;
+import com.jozufozu.flywheel.backend.material.MaterialManager;
+import com.jozufozu.flywheel.core.materials.IFlatLight;
+import com.jozufozu.flywheel.light.GridAlignedBB;
+import com.jozufozu.flywheel.light.ILightUpdateListener;
+import com.jozufozu.flywheel.light.LightUpdater;
+import com.jozufozu.flywheel.light.ListenerStatus;
+
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IBlockDisplayReader;
+import net.minecraft.world.LightType;
+import net.minecraft.world.World;
+
+/**
+ * A general interface providing information about any type of thing that could use Flywheel's instanced rendering.
+ * Right now, that's only {@link TileInstanceManager}, but there could be an entity equivalent in the future.
+ */
+public abstract class AbstractInstance implements IInstance, ILightUpdateListener {
+
+ protected final MaterialManager materialManager;
+ protected final World world;
+
+ public AbstractInstance(MaterialManager materialManager, World world) {
+ this.materialManager = materialManager;
+ this.world = world;
+ }
+
+ /**
+ * Free any acquired resources.
+ */
+ public abstract void remove();
+
+ /**
+ * Update instance data here. Good for when data doesn't change very often and when animations are GPU based.
+ * Don't query lighting data here, that's handled separately in {@link #updateLight()}.
+ *
+ *
If your animations are complex or more CPU driven, see {@link IDynamicInstance} or {@link ITickableInstance}.
+ */
+ public void update() {
+ }
+
+ /**
+ * Called after construction and when a light update occurs in the world.
+ *
+ *
If your model needs it, update light here.
+ */
+ public void updateLight() {
+ }
+
+ /**
+ * When an instance is reset, the instance is deleted and re-created.
+ *
+ *
+ * Just before {@link #update()} would be called, shouldReset()
is checked.
+ * If this function returns true
, then this instance will be {@link #remove removed},
+ * 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.
+ *
true
if this instance should be discarded and refreshed.
+ */
+ public boolean shouldReset() {
+ return false;
+ }
+
+ @Override
+ public ListenerStatus status() {
+ return ListenerStatus.OKAY;
+ }
+
+ @Override
+ public void onLightUpdate(IBlockDisplayReader world, LightType type, GridAlignedBB changed) {
+ updateLight();
+ }
+
+ protected void relight(BlockPos pos, IFlatLight>... models) {
+ relight(world.getBrightness(LightType.BLOCK, pos), world.getBrightness(LightType.SKY, pos), models);
+ }
+
+ protected - * This is used to handle things like block state changes. - *
- * - * @return true if this instance should be reset - */ - boolean shouldReset(); - - void update(); + BlockPos getWorldPosition(); } diff --git a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java index 113a4d83e..69f87bf1c 100644 --- a/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java +++ b/src/main/java/com/jozufozu/flywheel/backend/instancing/InstanceManager.java @@ -25,7 +25,7 @@ public abstract class InstanceManagershouldReset()
is checked.
- * If this function returns true
, then this instance will be {@link #remove removed},
- * 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.
- *
- * @return true
if this instance should be discarded and refreshed.
- */
- public boolean shouldReset() {
- return false;
+ @Override
+ public ListenerStatus status() {
+ return ListenerStatus.UPDATE;
}
/**
@@ -119,30 +90,4 @@ public abstract class EntityInstanceshouldReset()
is checked.
* If this function returns true
, then this instance will be {@link #remove removed},
@@ -106,23 +76,6 @@ public abstract class TileEntityInstance- * When a light update occurs in the chunk the position is contained in, - * {@link ILightUpdateListener#onLightUpdate} will be called. - * - * @param pos The position in the world that the listener cares about. + * Add a listener. + * @param listener The object that wants to receive light update notifications. */ - public void startListening(BlockPos pos, ILightUpdateListener listener) { - LongRBTreeSet sections = clearSections(listener); - LongRBTreeSet chunks = clearChunks(listener); + public void addListener(ILightUpdateListener listener) { + allListeners.add(listener); - long sectionPos = worldToSection(pos); - addToSection(sectionPos, listener); - sections.add(sectionPos); + Volume volume = listener.getVolume(); - long chunkPos = sectionToChunk(sectionPos); - addToChunk(chunkPos, listener); - chunks.add(chunkPos); - } + LongSet sections = this.sections.getAndResetContainment(listener); + LongSet chunks = this.chunks.getAndResetContainment(listener); - /** - * Add a listener associated with the given {@link GridAlignedBB}. - *
- * When a light update occurs in any chunk spanning the given volume,
- * {@link ILightUpdateListener#onLightUpdate} will be called.
- *
- * @param volume The volume in the world that the listener cares about.
- * @param listener The object that wants to receive light update notifications.
- */
- public void startListening(GridAlignedBB volume, ILightUpdateListener listener) {
- LongRBTreeSet sections = clearSections(listener);
- LongRBTreeSet chunks = clearSections(listener);
+ if (volume instanceof Volume.Block) {
+ BlockPos pos = ((Volume.Block) volume).pos;
+ long sectionPos = blockToSection(pos);
+ this.sections.put(sectionPos, listener);
+ sections.add(sectionPos);
- int minX = SectionPos.blockToSectionCoord(volume.minX);
- int minY = SectionPos.blockToSectionCoord(volume.minY);
- int minZ = SectionPos.blockToSectionCoord(volume.minZ);
- int maxX = SectionPos.blockToSectionCoord(volume.maxX);
- int maxY = SectionPos.blockToSectionCoord(volume.maxY);
- int maxZ = SectionPos.blockToSectionCoord(volume.maxZ);
+ long chunkPos = sectionToChunk(sectionPos);
+ this.chunks.put(chunkPos, listener);
+ chunks.add(chunkPos);
+ } else if (volume instanceof Volume.Box) {
+ GridAlignedBB box = ((Volume.Box) volume).box;
- for (int x = minX; x <= maxX; x++) {
- for (int z = minZ; z <= maxZ; z++) {
- for (int y = minY; y <= maxY; y++) {
- long sectionPos = SectionPos.asLong(x, y, z);
- addToSection(sectionPos, listener);
- sections.add(sectionPos);
+ int minX = SectionPos.blockToSectionCoord(box.minX);
+ int minY = SectionPos.blockToSectionCoord(box.minY);
+ int minZ = SectionPos.blockToSectionCoord(box.minZ);
+ int maxX = SectionPos.blockToSectionCoord(box.maxX);
+ int maxY = SectionPos.blockToSectionCoord(box.maxY);
+ int maxZ = SectionPos.blockToSectionCoord(box.maxZ);
+
+ for (int x = minX; x <= maxX; x++) {
+ for (int z = minZ; z <= maxZ; z++) {
+ for (int y = minY; y <= maxY; y++) {
+ long sectionPos = SectionPos.asLong(x, y, z);
+ this.sections.put(sectionPos, listener);
+ sections.add(sectionPos);
+ }
+ long chunkPos = SectionPos.asLong(x, 0, z);
+ this.chunks.put(chunkPos, listener);
+ chunks.add(chunkPos);
}
- long chunkPos = SectionPos.asLong(x, 0, z);
- addToChunk(chunkPos, listener);
- chunks.add(chunkPos);
}
}
}
@@ -110,13 +96,17 @@ public class LightUpdater {
* @param sectionPos A long representing the section position where light changed.
*/
public void onLightUpdate(IBlockDisplayReader world, LightType type, long sectionPos) {
- WeakHashSet
+ * First, uses the reverse lookup map to remove listener from all sets in the lookup map.
+ * Then, clears the listeners containment set.
+ *