Recycling rope

- Bump flywheel version
- Fix rope pulleys being invisible
- Use an InstanceRecycler in AbstractPulleyVisual
- Remove Select, Group, and ConditionalInstance
This commit is contained in:
Jozufozu 2024-09-29 00:17:48 -07:00
parent b6da803886
commit eb2f1fecdc
8 changed files with 72 additions and 304 deletions

View file

@ -23,7 +23,7 @@ use_parchment = true
# dependency versions # dependency versions
registrate_version = MC1.20-1.3.3 registrate_version = MC1.20-1.3.3
flywheel_minecraft_version = 1.20.1 flywheel_minecraft_version = 1.20.1
flywheel_version = 1.0.0-beta-136 flywheel_version = 1.0.0-beta-145
jei_minecraft_version = 1.20.1 jei_minecraft_version = 1.20.1
jei_version = 15.10.0.39 jei_version = 15.10.0.39
curios_minecraft_version = 1.20.1 curios_minecraft_version = 1.20.1

View file

@ -5,17 +5,16 @@ import java.util.function.Consumer;
import com.mojang.math.Axis; import com.mojang.math.Axis;
import com.simibubi.create.content.kinetics.base.KineticBlockEntity; import com.simibubi.create.content.kinetics.base.KineticBlockEntity;
import com.simibubi.create.content.kinetics.base.ShaftVisual; import com.simibubi.create.content.kinetics.base.ShaftVisual;
import com.simibubi.create.foundation.render.ConditionalInstance;
import com.simibubi.create.foundation.render.GroupInstance;
import com.simibubi.create.foundation.render.SelectInstance;
import dev.engine_room.flywheel.api.instance.Instance; import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.instance.Instancer; import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.api.visual.DynamicVisual; import dev.engine_room.flywheel.api.visual.DynamicVisual;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.OrientedInstance; import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.math.MoreMath; import dev.engine_room.flywheel.lib.math.MoreMath;
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual; import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
import dev.engine_room.flywheel.lib.visual.util.SmartRecycler;
import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.bytes.ByteList; import it.unimi.dsi.fastutil.bytes.ByteList;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
@ -29,9 +28,8 @@ import net.minecraft.world.level.LightLayer;
public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends ShaftVisual<T> implements SimpleDynamicVisual { public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends ShaftVisual<T> implements SimpleDynamicVisual {
private final OrientedInstance coil; private final OrientedInstance coil;
private final SelectInstance<OrientedInstance> magnet; private final TransformedInstance magnet;
private final GroupInstance<OrientedInstance> rope; private final SmartRecycler<Boolean, TransformedInstance> rope;
private final ConditionalInstance<OrientedInstance> halfRope;
protected final Direction rotatingAbout; protected final Direction rotatingAbout;
protected final Axis rotationAxis; protected final Axis rotationAxis;
@ -50,14 +48,12 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
.position(getVisualPosition()); .position(getVisualPosition());
coil.setChanged(); coil.setChanged();
magnet = new SelectInstance<>(this::getMagnetModelIndex); magnet = magnetInstancer().createInstance();
magnet.addModel(getMagnetModel())
.addModel(getHalfMagnetModel());
rope = new GroupInstance<>(getRopeModel()); rope = new SmartRecycler<>(b -> b ? getHalfRopeModel().createInstance() : getRopeModel().createInstance());
halfRope = new ConditionalInstance<>(getHalfRopeModel()).withCondition(this::shouldRenderHalfRope);
updateOffset(partialTick); updateOffset(partialTick);
updateLight(partialTick);
} }
@Override @Override
@ -66,67 +62,68 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
lightCache.updateSections(); lightCache.updateSections();
} }
protected abstract Instancer<OrientedInstance> getRopeModel(); protected abstract Instancer<TransformedInstance> getRopeModel();
protected abstract Instancer<OrientedInstance> getMagnetModel(); protected abstract Instancer<TransformedInstance> getMagnetModel();
protected abstract Instancer<OrientedInstance> getHalfMagnetModel(); protected abstract Instancer<TransformedInstance> getHalfMagnetModel();
protected abstract Instancer<OrientedInstance> getCoilModel(); protected abstract Instancer<OrientedInstance> getCoilModel();
protected abstract Instancer<OrientedInstance> getHalfRopeModel(); protected abstract Instancer<TransformedInstance> getHalfRopeModel();
protected abstract float getOffset(float pt); protected abstract float getOffset(float pt);
protected abstract boolean isRunning(); protected abstract boolean isRunning();
private Instancer<TransformedInstance> magnetInstancer() {
return offset > .25f ? getMagnetModel() : getHalfMagnetModel();
}
@Override @Override
public void beginFrame(DynamicVisual.Context ctx) { public void beginFrame(DynamicVisual.Context ctx) {
updateOffset(ctx.partialTick()); updateOffset(ctx.partialTick());
coil.rotation(rotationAxis.rotationDegrees(offset * 180)) coil.rotation(rotationAxis.rotationDegrees(offset * 180))
.setChanged(); .setChanged();
int neededRopeCount = getNeededRopeCount(); magnet.setVisible(isRunning() || offset == 0);
rope.resize(neededRopeCount);
magnet.update() magnetInstancer().stealInstance(magnet);
.get()
.ifPresent(data -> {
int i = Math.max(0, Mth.floor(offset));
int light = lightCache.getPackedLight(i);
data.position(getVisualPosition())
.translatePosition(0, -offset, 0)
.light(light)
.setChanged();
});
halfRope.update() magnet.setIdentityTransform()
.get() .translate(getVisualPosition())
.ifPresent(rope1 -> { .translate(0, -offset, 0)
float f = offset % 1; .light(lightCache.getPackedLight(Math.max(0, Mth.floor(offset))))
float halfRopeNudge = f > .75f ? f - 1 : f; .setChanged();
int light = lightCache.getPackedLight(0); rope.resetCount();
rope1.position(getVisualPosition())
.translatePosition(0, -halfRopeNudge, 0) if (shouldRenderHalfRope()) {
.light(light) float f = offset % 1;
.setChanged(); float halfRopeNudge = f > .75f ? f - 1 : f;
});
rope.get(true).setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -halfRopeNudge, 0)
.light(lightCache.getPackedLight(0))
.setChanged();
}
if (isRunning()) { if (isRunning()) {
int size = rope.size(); int neededRopeCount = getNeededRopeCount();
for (int i = 0; i < size; i++) {
int light = lightCache.getPackedLight(size - 1 - i);
rope.get(i) for (int i = 0; i < neededRopeCount; i++) {
.position(getVisualPosition())
.translatePosition(0, -offset + i + 1, 0) rope.get(false)
.light(light) .setIdentityTransform()
.translate(getVisualPosition())
.translate(0, -offset + i + 1, 0)
.light(lightCache.getPackedLight(neededRopeCount - 1 - i))
.setChanged(); .setChanged();
} }
} else {
rope.clear();
} }
rope.discardExtra();
} }
@Override @Override
@ -151,21 +148,11 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
return offset > .75f && (f < .25f || f > .75f); return offset > .75f && (f < .25f || f > .75f);
} }
private int getMagnetModelIndex() {
if (isRunning() || offset == 0) {
return offset > .25f ? 0 : 1;
} else {
return -1;
}
}
@Override @Override
public void collectCrumblingInstances(Consumer<Instance> consumer) { public void collectCrumblingInstances(Consumer<Instance> consumer) {
super.collectCrumblingInstances(consumer); super.collectCrumblingInstances(consumer);
consumer.accept(coil); consumer.accept(coil);
magnet.forEach(consumer); consumer.accept(magnet);
rope.forEach(consumer);
halfRope.forEach(consumer);
} }
@Override @Override
@ -173,8 +160,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
super._delete(); super._delete();
coil.delete(); coil.delete();
magnet.delete(); magnet.delete();
rope.clear(); rope.delete();
halfRope.delete();
} }
private class LightCache { private class LightCache {
@ -186,6 +172,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
public void setSize(int size) { public void setSize(int size) {
if (size != data.size()) { if (size != data.size()) {
data.size(size); data.size(size);
update();
int sectionCount = MoreMath.ceilingDiv(size + 15 - pos.getY() + pos.getY() / 4 * 4, SectionPos.SECTION_SIZE); int sectionCount = MoreMath.ceilingDiv(size + 15 - pos.getY() + pos.getY() / 4 * 4, SectionPos.SECTION_SIZE);
if (sectionCount != this.sectionCount) { if (sectionCount != this.sectionCount) {
@ -215,7 +202,7 @@ public abstract class AbstractPulleyVisual<T extends KineticBlockEntity> extends
for (int i = 0; i < data.size(); i++) { for (int i = 0; i < data.size(); i++) {
int blockLight = level.getBrightness(LightLayer.BLOCK, mutablePos); int blockLight = level.getBrightness(LightLayer.BLOCK, mutablePos);
int skyLight = level.getBrightness(LightLayer.SKY, mutablePos); int skyLight = level.getBrightness(LightLayer.SKY, mutablePos);
int light = ((skyLight << 4) & 0xF) | (blockLight & 0xF); int light = ((skyLight & 0xF) << 4) | (blockLight & 0xF);
data.set(i, (byte) light); data.set(i, (byte) light);
mutablePos.move(Direction.DOWN); mutablePos.move(Direction.DOWN);
} }

View file

@ -7,6 +7,7 @@ import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes; import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.OrientedInstance; import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.model.Models; import dev.engine_room.flywheel.lib.model.Models;
public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity> { public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity> {
@ -15,18 +16,18 @@ public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity
} }
@Override @Override
protected Instancer<OrientedInstance> getRopeModel() { protected Instancer<TransformedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE)); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE));
} }
@Override @Override
protected Instancer<OrientedInstance> getMagnetModel() { protected Instancer<TransformedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_MAGNET)); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_MAGNET));
} }
@Override @Override
protected Instancer<OrientedInstance> getHalfMagnetModel() { protected Instancer<TransformedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_HALF_MAGNET)); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_HALF_MAGNET));
} }
@Override @Override
@ -35,8 +36,8 @@ public class HosePulleyVisual extends AbstractPulleyVisual<HosePulleyBlockEntity
} }
@Override @Override
protected Instancer<OrientedInstance> getHalfRopeModel() { protected Instancer<TransformedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.HOSE_HALF)); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.HOSE_HALF));
} }
@Override @Override

View file

@ -9,6 +9,7 @@ import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.api.visualization.VisualizationContext; import dev.engine_room.flywheel.api.visualization.VisualizationContext;
import dev.engine_room.flywheel.lib.instance.InstanceTypes; import dev.engine_room.flywheel.lib.instance.InstanceTypes;
import dev.engine_room.flywheel.lib.instance.OrientedInstance; import dev.engine_room.flywheel.lib.instance.OrientedInstance;
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
import dev.engine_room.flywheel.lib.model.Models; import dev.engine_room.flywheel.lib.model.Models;
public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> { public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
@ -17,18 +18,18 @@ public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
} }
@Override @Override
protected Instancer<OrientedInstance> getRopeModel() { protected Instancer<TransformedInstance> getRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, VirtualRenderHelper.blockModel(AllBlocks.ROPE.getDefaultState())); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.ROPE.getDefaultState()));
} }
@Override @Override
protected Instancer<OrientedInstance> getMagnetModel() { protected Instancer<TransformedInstance> getMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, VirtualRenderHelper.blockModel(AllBlocks.PULLEY_MAGNET.getDefaultState())); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, VirtualRenderHelper.blockModel(AllBlocks.PULLEY_MAGNET.getDefaultState()));
} }
@Override @Override
protected Instancer<OrientedInstance> getHalfMagnetModel() { protected Instancer<TransformedInstance> getHalfMagnetModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_HALF_MAGNET)); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.ROPE_HALF_MAGNET));
} }
@Override @Override
@ -37,8 +38,8 @@ public class RopePulleyVisual extends AbstractPulleyVisual<PulleyBlockEntity> {
} }
@Override @Override
protected Instancer<OrientedInstance> getHalfRopeModel() { protected Instancer<TransformedInstance> getHalfRopeModel() {
return instancerProvider().instancer(InstanceTypes.ORIENTED, Models.partial(AllPartialModels.ROPE_HALF)); return instancerProvider().instancer(InstanceTypes.TRANSFORMED, Models.partial(AllPartialModels.ROPE_HALF));
} }
@Override @Override

View file

@ -49,7 +49,7 @@ public class AllInstanceTypes {
MemoryUtil.memPutByte(ptr + 33, instance.rotationAxisY); MemoryUtil.memPutByte(ptr + 33, instance.rotationAxisY);
MemoryUtil.memPutByte(ptr + 34, instance.rotationAxisZ); MemoryUtil.memPutByte(ptr + 34, instance.rotationAxisZ);
}) })
.register(); .build();
public static final InstanceType<BeltInstance> BELT = SimpleInstanceType.builder(BeltInstance::new) public static final InstanceType<BeltInstance> BELT = SimpleInstanceType.builder(BeltInstance::new)
.cullShader(asResource("instance/cull/belt.glsl")) .cullShader(asResource("instance/cull/belt.glsl"))
@ -87,7 +87,7 @@ public class AllInstanceTypes {
MemoryUtil.memPutFloat(ptr + 68, instance.maxV); MemoryUtil.memPutFloat(ptr + 68, instance.maxV);
MemoryUtil.memPutFloat(ptr + 72, instance.scrollMult); MemoryUtil.memPutFloat(ptr + 72, instance.scrollMult);
}) })
.register(); .build();
// TODO: use this for belts too // TODO: use this for belts too
public static final InstanceType<ScrollInstance> SCROLLING = SimpleInstanceType.builder(ScrollInstance::new) public static final InstanceType<ScrollInstance> SCROLLING = SimpleInstanceType.builder(ScrollInstance::new)
@ -121,7 +121,7 @@ public class AllInstanceTypes {
MemoryUtil.memPutFloat(ptr + 56, instance.scaleU); MemoryUtil.memPutFloat(ptr + 56, instance.scaleU);
MemoryUtil.memPutFloat(ptr + 60, instance.scaleV); MemoryUtil.memPutFloat(ptr + 60, instance.scaleV);
}) })
.register(); .build();
public static final InstanceType<ActorInstance> ACTOR = SimpleInstanceType.builder(ActorInstance::new) public static final InstanceType<ActorInstance> ACTOR = SimpleInstanceType.builder(ActorInstance::new)
.cullShader(asResource("instance/cull/actor.glsl")) .cullShader(asResource("instance/cull/actor.glsl"))
@ -151,7 +151,7 @@ public class AllInstanceTypes {
MemoryUtil.memPutByte(ptr + 42, instance.rotationCenterZ); MemoryUtil.memPutByte(ptr + 42, instance.rotationCenterZ);
MemoryUtil.memPutFloat(ptr + 44, instance.speed); MemoryUtil.memPutFloat(ptr + 44, instance.speed);
}) })
.register(); .build();
// TODO: remove // TODO: remove
public static final InstanceType<FlapInstance> FLAP = SimpleInstanceType.builder(FlapInstance::new) public static final InstanceType<FlapInstance> FLAP = SimpleInstanceType.builder(FlapInstance::new)
@ -183,7 +183,7 @@ public class AllInstanceTypes {
MemoryUtil.memPutFloat(ptr + 48, instance.flapScale); MemoryUtil.memPutFloat(ptr + 48, instance.flapScale);
MemoryUtil.memPutFloat(ptr + 52, instance.flapness); MemoryUtil.memPutFloat(ptr + 52, instance.flapness);
}) })
.register(); .build();
public static void init() { public static void init() {
// noop // noop

View file

@ -1,68 +0,0 @@
package com.simibubi.create.foundation.render;
import java.util.Optional;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.lib.instance.AbstractInstance;
public class ConditionalInstance<D extends AbstractInstance> {
final Instancer<D> model;
ICondition condition;
Consumer<D> setupFunc;
@Nullable
private D instance;
public ConditionalInstance(Instancer<D> model) {
this.model = model;
this.condition = () -> true;
}
public ConditionalInstance<D> withSetupFunc(Consumer<D> setupFunc) {
this.setupFunc = setupFunc;
return this;
}
public ConditionalInstance<D> withCondition(ICondition condition) {
this.condition = condition;
return this;
}
public ConditionalInstance<D> update() {
boolean shouldShow = condition.shouldShow();
if (shouldShow && instance == null) {
instance = model.createInstance();
if (setupFunc != null) setupFunc.accept(instance);
} else if (!shouldShow && instance != null) {
instance.delete();
instance = null;
}
return this;
}
public Optional<D> get() {
return Optional.ofNullable(instance);
}
public void delete() {
if (instance != null) instance.delete();
}
public void forEach(Consumer<Instance> consumer) {
if (instance != null) {
consumer.accept(instance);
}
}
@FunctionalInterface
public interface ICondition {
boolean shouldShow();
}
}

View file

@ -1,84 +0,0 @@
package com.simibubi.create.foundation.render;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import dev.engine_room.flywheel.api.instance.Instancer;
import dev.engine_room.flywheel.lib.instance.AbstractInstance;
public class GroupInstance<D extends AbstractInstance> extends AbstractCollection<D> {
final Instancer<D> model;
final List<D> backing;
public GroupInstance(Instancer<D> model) {
this.model = model;
this.backing = new ArrayList<>();
}
public GroupInstance(Instancer<D> model, int size) {
this.model = model;
this.backing = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
addInstance();
}
}
/**
* @param count
* @return True if the number of elements changed.
*/
public boolean resize(int count) {
int size = size();
if (count == size) return false;
if (count <= 0) {
clear();
return size > 0;
}
if (count > size) {
for (int i = size; i < count; i++) {
addInstance();
}
} else {
List<D> unnecessary = backing.subList(count, size);
unnecessary.forEach(AbstractInstance::delete);
unnecessary.clear();
}
return true;
}
public D addInstance() {
D instance = model.createInstance();
backing.add(instance);
return instance;
}
public D get(int index) {
return backing.get(index);
}
@Override
public Iterator<D> iterator() {
return backing.iterator();
}
@Override
public int size() {
return backing.size();
}
@Override
public void clear() {
backing.forEach(AbstractInstance::delete);
backing.clear();
}
}

View file

@ -1,69 +0,0 @@
package com.simibubi.create.foundation.render;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import dev.engine_room.flywheel.api.instance.Instance;
import dev.engine_room.flywheel.api.instance.Instancer;
public class SelectInstance<D extends Instance> {
final List<Instancer<D>> models;
ModelSelector selector;
private int last = -1;
@Nullable
private D current;
public SelectInstance(ModelSelector selector) {
this.models = new ArrayList<>();
this.selector = selector;
}
public SelectInstance<D> addModel(Instancer<D> model) {
models.add(model);
return this;
}
public SelectInstance<D> update() {
int i = selector.modelIndexToShow();
if (i < 0 || i >= models.size()) {
if (current != null) {
current.handle().setDeleted();
current = null;
}
} else if (i != last) {
if (current != null) current.handle().setDeleted();
current = models.get(i)
.createInstance();
}
last = i;
return this;
}
public Optional<D> get() {
return Optional.ofNullable(current);
}
public void delete() {
if (current != null) current.handle().setDeleted();
}
public void forEach(Consumer<Instance> consumer) {
if (current != null) {
consumer.accept(current);
}
}
public interface ModelSelector {
int modelIndexToShow();
}
}